当前位置:首页 » 《随便一记》 » 正文

ElementUI中Tree组件使用

25 人参与  2023年05月03日 14:17  分类 : 《随便一记》  评论

点击全文阅读


       首先官网上的树形控件教程地址为Element - The world's most popular Vue UI framework

案例一:

要实现这种类型的树控件,并且后边相关操作:

1.1后端准备工作

 首先,数据库表为:

 查询接口返回的实体类为:

@Data@NoArgsConstructor@RequiredArgsConstructor // 有参构造@EqualsAndHashCode(callSuper = false ,of = "name")// 表示以name去重写的Equals和HashCode@Accessors(chain = true)@TableName("t_department")@ApiModel(value="Department对象", description="")public class Department implements Serializable {    private static final long serialVersionUID = 1L;    @ApiModelProperty(value = "id")    @TableId(value = "id", type = IdType.AUTO)    private Integer id;    @ApiModelProperty(value = "部门名称")    @Excel(name = "部门")    @NonNull    private String name;    @ApiModelProperty(value = "父id")    private Integer parentId;    @ApiModelProperty(value = "路径")    private String depPath;    @ApiModelProperty(value = "是否启用")    private Boolean enabled;    @ApiModelProperty(value = "是否上级")    private Boolean isParent;    @ApiModelProperty(value = "子部门列表")    @TableField(exist = false)    private List<Department> children;    @ApiModelProperty(value = "返回结果,存储过程使用")    @TableField(exist = false)    private Integer result;}

查询接口返回的数据格式:通过属性 children来判断是否有子节点

[  {    "id": 1,    "name": "股东会",    "parentId": -1,    "depPath": ".1",    "enabled": true,    "isParent": true,    "children": [      {        "id": 2,        "name": "董事会",        "parentId": 1,        "depPath": ".1.2",        "enabled": true,        "isParent": true,        "children": [          {            "id": 3,            "name": "总办",            "parentId": 2,            "depPath": ".1.2.3",            "enabled": true,            "isParent": true,            "children": [              {                "id": 4,                "name": "财务部",                "parentId": 3,                "depPath": ".1.2.3.4",                "enabled": true,                "isParent": false,                "children": [],                "result": null              },              {                "id": 5,                "name": "市场部",                "parentId": 3,                "depPath": ".1.2.3.5",                "enabled": true,                "isParent": true,                "children": [                  {                    "id": 6,                    "name": "华东市场部",                    "parentId": 5,                    "depPath": "1.2.3.5.6",                    "enabled": true,                    "isParent": true,                    "children": [                      {                        "id": 8,                        "name": "上海市场部",                        "parentId": 6,                        "depPath": "1.2.3.5.6.8",                        "enabled": true,                        "isParent": false,                        "children": [],                        "result": null                      }                    ],                    "result": null                  },                  {                    "id": 7,                    "name": "华南市场部",                    "parentId": 5,                    "depPath": "1.2.3.5.7",                    "enabled": true,                    "isParent": false,                    "children": [],                    "result": null                  },                  {                    "id": 9,                    "name": "西北市场部",                    "parentId": 5,                    "depPath": ".1.2.3.5.9",                    "enabled": true,                    "isParent": true,                    "children": [                      {                        "id": 10,                        "name": "贵阳市场",                        "parentId": 9,                        "depPath": ".1.2.3.5.9.10",                        "enabled": true,                        "isParent": true,                        "children": [                          {                            "id": 11,                            "name": "乌当区市场",                            "parentId": 10,                            "depPath": ".1.2.3.5.9.10.11",                            "enabled": true,                            "isParent": false,                            "children": [],                            "result": null                          }                        ],                        "result": null                      }                    ],                    "result": null                  }                ],                "result": null              },              {                "id": 12,                "name": "技术部",                "parentId": 3,                "depPath": ".1.2.3.12",                "enabled": true,                "isParent": false,                "children": [],                "result": null              },              {                "id": 13,                "name": "运维部",                "parentId": 3,                "depPath": ".1.2.3.13",                "enabled": true,                "isParent": false,                "children": [],                "result": null              }            ],            "result": null          }        ],        "result": null      },      {        "id": 150,        "name": "aaa",        "parentId": 1,        "depPath": ".1.150",        "enabled": true,        "isParent": true,        "children": [          {            "id": 151,            "name": "abbb",            "parentId": 150,            "depPath": ".1.150.151",            "enabled": true,            "isParent": false,            "children": [],            "result": null          }        ],        "result": null      },      {        "id": 154,        "name": "ccc",        "parentId": 1,        "depPath": ".1.154",        "enabled": true,        "isParent": false,        "children": [],        "result": null      },      {        "id": 157,        "name": "dddd",        "parentId": 1,        "depPath": ".1.157",        "enabled": true,        "isParent": false,        "children": [],        "result": null      }    ],    "result": null  }]

1.2前端代码

从官网上复制一个模板过来,搜索的话,直接往下找一个有搜索框的,把搜索框复制过来,复制好就可以开始修改,写增删改查的方法调用了。

 写好的代码如下:

<template>  <div style="width: 550px;">    <el-input      placeholder="请输入部门名称进行搜索..."      prefix-icon="el-icon-search"      v-model="filterText">    </el-input>    <el-tree      :data="deps"      :props="defaultProps"      :filter-node-method="filterNode"      :expand-on-click-node="false"      ref="tree">      <!-- slot-scope可以自定义树节点里的内容,node当前节点的node对象,data后端对应返回的数据 -->      <span class="custom-tree-node" slot-scope="{ node, data }" style="display:flex; justify-content: space-between;width: 100%;">        <span>{{ data.name }}</span>        <span>          <el-button            type="primary"            size="mini"            class="depBtn"            @click="() => showAddDep(data)">            添加部门          </el-button>          <el-button            type="danger"            size="mini"            class="depBtn"            @click="() => deleteDep(data)">            删除部门          </el-button>        </span>      </span>    </el-tree>    <!-- 添加部门的弹出框 -->    <el-dialog      title="添加部门"      :visible.sync="dialogVisible"      width="30%">      <div>        <table>          <tr>            <td><el-tag>上级部门</el-tag></td>            <td><el-tag>{{pname}}</el-tag></td>          </tr>          <tr>            <td><el-tag>部门名称</el-tag></td>            <td><el-input v-model="dep.name" placeholder="请输入部门名称..."></el-input></td>          </tr>        </table>      </div>      <span slot="footer" class="dialog-footer">        <el-button @click="dialogVisible = false">取 消</el-button>        <el-button type="primary" @click="doAddDep">确 定</el-button>      </span>    </el-dialog>  </div></template><script>export default {    data(){      return{        // 树的搜索条件        filterText: '',        // 树的数据        deps: [],        // 树的配置        defaultProps: {          children: 'children',          label: 'name'        },        // 添加弹出框        dialogVisible: false,        // 添加的部门数据        dep:{          name:'',          parentId: -1        },        // 上级部门名称        pname: ''      }    },    mounted(){      this.initDeps();    },    methods: {      // 初始化数据      initDeps(){        this.getRequest('/system/basic/department/').then(resp=>{          this.deps = resp;        })      },      // 树的搜索      filterNode(value, data) {        if (!value) return true;        return data.name.indexOf(value) !== -1;      },      // 添加弹框      showAddDep(data){        console.log(data)        this.dep.parentId = data.id;        this.pname = data.name;        this.dialogVisible = 'true'      },      // 添加      doAddDep(){        this.postRequest('/system/basic/department/',this.dep).then(resp=>{          if(resp.code==200){            resp.obj.children = []            this.addDep2Deps(this.deps,resp.obj);            this.initDep();            this.dialogVisible = false;          }        })      },      initDep(){        this.dep = {          name: '',          parentId: -1        }        this.panme = ''      },      // 添加成功后手动的给树加数据      addDep2Deps(deps,dep){        for(let i = 0; i<deps.length; i++){          let d= deps[i];          if(d.id == dep.parentId){            d.children = d.children.concat(dep);            if(d.children.length>0){              d.isParent = true;            }            return;          }else{            // 递归            this.addDep2Deps(d.children,dep);          }        }      },      // 删除      deleteDep(data){        console.log(data)        if(data.isParent){          this.$message.error("父部门无法删除");        }else{          this.$confirm('此操作将永久['+data.name+']部门, 是否继续?', '提示', {            confirmButtonText: '确定',            cancelButtonText: '取消',            type: 'warning'          }).then(() => {            this.deleteRequest('/system/basic/department/'+data.id).then(resp=>{              if(resp.code==200){                this.removeDepFromDeps(null,this.deps,data.id);              }            })          }).catch(() => {            this.$message({              type: 'info',              message: '已取消删除'            });                    });        }      },      // 手动删除  (父部门,总部门数据,要删除的部门id)      removeDepFromDeps(p,deps,id){        for(let i=0; i<deps.length; i++){          let d = deps[i];          if(d.id==id){            deps.splice(i,1);            if(deps.length==0){              p.isParent = false;            }            return;          }else{            this.removeDepFromDeps(d,d.children,id);          }        }      }    },    watch: {      filterText(val) {        this.$refs.tree.filter(val);      }    }}</script><style>.depBtn{  padding: 3px;}</style>

需要注意的是:

1:树组件中    :data="deps" 加载树组建的数据。:props="defaultProps" 加载树组件的配置,其中label是给个枝叶的名字对应的字段,children是子节点对应的字段。:filter-node-method="filterNode" 树组件的搜索,filterNode过滤的回调,这个官网拷贝即可。:expand-on-click-node="false" 关闭点击折叠,只有点击前面的小三角才能展开折叠。

2:搜索框绑定的数据filterText,下面监听这个数据的改变,如果改变了,就调用树的filter方法(this.$refs.tree.filter(val);),进行过滤,这个方法的回调在树组件的属性中定义:filter-node-method="filterNode" ,也就是输入框值改变了,会去掉filterNode这个方法,这个方法传入两个值,value是文本框输入值,data是树组件的数据,value为空直接返回,不为空则过滤。

3: 添加部门的时候,添加成功后不能单纯的掉接口,重新请求一次树中的数据,这样的话,每添加一个部门,整个树就会折叠起来。这时候就需要不调接口请求新的数据,在js中操作树中的数据this.deps,让它和数据库保持同步。首先在成功后调用方法: addDep2Deps(deps,dep)  这个方法第一个参数是整个树中的数据,第二个参数是新添加的树的数据(这个数据是添加成功接口回显的数据,也就是添加的这条记录信息)在这个方法中,会循环,找到对应的父节点,给它的子节点拼接数据,修改父节点isParent属性(是否有孩子),之后就一直递归,知道添加完成为止。这个数据添加成功后,清空弹出框中输入的信息,关闭弹出框。

4:删除操作,同样,删除时也要手动的在js中把这条数据删除,不能请求新数据,请求新数据会导致树整个关闭,用户体验十分不好,需要在js中把树中的数据和数据库中进行同步。

调用接口删除成功后,调用removeDepFromDeps(p,deps,id)方法,在js中进行数据删除,调用时,第一个参数传null表示从顶层节点开始往下找,第二个参数是树的数据,第三个参数是要删除部门的id。同样进行循环,添加中比较的是父节点的id,删除是,比较的就是这个叶子的id,如果相同,那就删除,同时,判断把这个叶子删掉后,这个枝下面还有没有叶子,因为有叶子的枝是不能删除的,所以要再判断一下修改isParent的状态,之后还是一样,在一层中没有查询到的话,就递归到下一层去找。

案例二

这种前面带有选择框的,进行授权操作 

2.1后端准备

对应的数据库表

 对应的实体类:

@Data@EqualsAndHashCode(callSuper = false)@Accessors(chain = true)@TableName("t_menu")@ApiModel(value="Menu对象", description="")public class Menu implements Serializable {    private static final long serialVersionUID = 1L;    @ApiModelProperty(value = "id")    @TableId(value = "id", type = IdType.AUTO)    private Integer id;    @ApiModelProperty(value = "url")    private String url;    @ApiModelProperty(value = "path")    private String path;    @ApiModelProperty(value = "组件")    private String component;    @ApiModelProperty(value = "菜单名")    private String name;    @ApiModelProperty(value = "图标")    private String iconCls;    @ApiModelProperty(value = "是否保持激活")    private Boolean keepAlive;    @ApiModelProperty(value = "是否要求权限")    private Boolean requireAuth;    @ApiModelProperty(value = "父id")    private Integer parentId;    @ApiModelProperty(value = "是否启用")    private Boolean enabled;    @ApiModelProperty(value = "子菜单")    @TableField(exist = false) // 告诉mybatisplus这个字段不在表中,查询的时候不要去查    private List<Menu> children;    @ApiModelProperty(value = "角色列表")    @TableField(exist = false)    private List<Role> roles;}

 查询接口返回的数据

{  "code": 200,  "message": "查询成功",  "obj": [    {      "id": 1,      "url": null,      "path": null,      "component": null,      "name": "所有",      "iconCls": null,      "keepAlive": null,      "requireAuth": null,      "parentId": null,      "enabled": null,      "children": [        {          "id": 2,          "url": null,          "path": null,          "component": null,          "name": "员工资料",          "iconCls": null,          "keepAlive": null,          "requireAuth": null,          "parentId": null,          "enabled": null,          "children": [            {              "id": 7,              "url": null,              "path": null,              "component": null,              "name": "基本资料",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 8,              "url": null,              "path": null,              "component": null,              "name": "高级资料",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            }          ],          "roles": null        },        {          "id": 3,          "url": null,          "path": null,          "component": null,          "name": "人事管理",          "iconCls": null,          "keepAlive": null,          "requireAuth": null,          "parentId": null,          "enabled": null,          "children": [            {              "id": 9,              "url": null,              "path": null,              "component": null,              "name": "员工资料",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 10,              "url": null,              "path": null,              "component": null,              "name": "员工奖惩",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 11,              "url": null,              "path": null,              "component": null,              "name": "员工培训",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 12,              "url": null,              "path": null,              "component": null,              "name": "员工调薪",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 13,              "url": null,              "path": null,              "component": null,              "name": "员工调动",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            }          ],          "roles": null        },        {          "id": 4,          "url": null,          "path": null,          "component": null,          "name": "薪资管理",          "iconCls": null,          "keepAlive": null,          "requireAuth": null,          "parentId": null,          "enabled": null,          "children": [            {              "id": 14,              "url": null,              "path": null,              "component": null,              "name": "工资账套管理",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 15,              "url": null,              "path": null,              "component": null,              "name": "员工账套设置",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 16,              "url": null,              "path": null,              "component": null,              "name": "工资表管理",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 17,              "url": null,              "path": null,              "component": null,              "name": "月末处理",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 18,              "url": null,              "path": null,              "component": null,              "name": "工资表查询",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            }          ],          "roles": null        },        {          "id": 5,          "url": null,          "path": null,          "component": null,          "name": "统计管理",          "iconCls": null,          "keepAlive": null,          "requireAuth": null,          "parentId": null,          "enabled": null,          "children": [            {              "id": 19,              "url": null,              "path": null,              "component": null,              "name": "综合信息统计",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 20,              "url": null,              "path": null,              "component": null,              "name": "员工积分统计",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 21,              "url": null,              "path": null,              "component": null,              "name": "人事信息统计",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 22,              "url": null,              "path": null,              "component": null,              "name": "人事记录统计",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            }          ],          "roles": null        },        {          "id": 6,          "url": null,          "path": null,          "component": null,          "name": "系统管理",          "iconCls": null,          "keepAlive": null,          "requireAuth": null,          "parentId": null,          "enabled": null,          "children": [            {              "id": 23,              "url": null,              "path": null,              "component": null,              "name": "基础信息设置",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 24,              "url": null,              "path": null,              "component": null,              "name": "系统管理",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 25,              "url": null,              "path": null,              "component": null,              "name": "操作日志管理",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 26,              "url": null,              "path": null,              "component": null,              "name": "操作员管理",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 27,              "url": null,              "path": null,              "component": null,              "name": "备份恢复数据库",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            },            {              "id": 28,              "url": null,              "path": null,              "component": null,              "name": "初始化数据库",              "iconCls": null,              "keepAlive": null,              "requireAuth": null,              "parentId": null,              "enabled": null,              "children": null,              "roles": null            }          ],          "roles": null        }      ],      "roles": null    }  ]}

2.2前端代码

<template>  <div>    <!-- 添加角色  -->    <div class="permissManaTool">        <el-input size="small" placeholder="请输入角色英文名" v-model="role.name">          <template slot="prepend">ROLE_</template>        </el-input>        <el-input size="small" placeholder="请输入角色中文名" v-model="role.nameZh" @keydown.enter.native="addRole"></el-input>        <el-button size="small" type="primary" icon="el-icon-plus" @click="addRole">添加角色</el-button>    </div>    <!-- 手风琴  -->    <div style="margin-top:10px; width:660px">      <el-collapse v-model="activeName" accordion @change="change">        <el-collapse-item :title="r.nameZh" :name="r.id" v-for="(r,index) in roles" :key="index">          <!-- 卡片 -->          <el-card class="box-card">            <div slot="header" class="clearfix">              <span>可访问资源</span>              <el-button style="float: right; padding: 3px 0; color: #ff0000;" type="text" icon="el-icon-delete" @click="doDeleteRole(r)"></el-button>            </div>            <div>              <!-- 树 -->              <el-tree                 show-checkbox                node-key="id"                ref="tree"                :key="index"                :default-checked-keys="selectMenus"                 :data="allMenus" :props="defaultProps"></el-tree>                <div style="display:flex; justify-content: flex-end">                  <el-button size="mini" @click="cancelUpdate">取消修改</el-button>                  <el-button size="mini" type="primary" @click="doUpdate(r.id,index)">确定修改</el-button>                </div>            </div>          </el-card>        </el-collapse-item>      </el-collapse>    </div>  </div></template><script>export default {    data(){      return{        role:{          name:'',          nameZh:''        },        activeName : '-1',        roles:[],        allMenus: [],        // 树菜单的属性,children子菜单的属性,label显示出来的名字,都是后端接口返回来的字段名字        defaultProps: {          children: 'children',          label: 'name'        },        // 树中选中的节点        selectMenus: []      }    },    mounted(){      this.initRoles();          },    methods:{      // 初始化所有菜单      initAllMenus(){        this.getRequest('/system/basic/permiss/menus').then(resp=>{          if(resp.code==200){            this.allMenus = resp.obj;          }        })      },      // 根据角色id获取菜单      initSelectdMenus(rid){        this.getRequest('/system/basic/permiss/mid/'+rid).then(resp=>{          if(resp.code==200){            this.selectMenus = resp.obj;          }        })      },      // 获取角色列表      initRoles(){        this.getRequest('/system/basic/permiss/').then(resp=>{          if(resp.code==200){            this.roles = resp.obj;          }        })      },      // 手风琴点击事件,展开rid是每个name,name对应着后端字段id,关闭时rid为空      change(rid){        if(rid){          this.initAllMenus();          this.initSelectdMenus(rid);        }      },      // 修改角色权限       doUpdate(rid,index){        // 拿到这个手风琴下面的树        let tree = this.$refs.tree[index];        // 传true只打印叶子节点的id        let selectedKeys = tree.getCheckedKeys(true);        let url = '/system/basic/permiss/?rid='+ rid;        selectedKeys.forEach(key => {          url += '&mids='+ key;        });        this.putRequest(url).then(resp=>{          if(resp.code==200){            this.activeName = '-1'          }        })      },      cancelUpdate(){        this.activeName = '-1'      },      // 添加角色      addRole(){        if(this.role.name && this.role.nameZh){          this.postRequest('/system/basic/permiss/',this.role).then(resp=>{            if(resp.code==200){              this.initRoles();              this.role.name = '';              this.role.nameZh = '';            }          })        }else{          this.$message.error("所有字段不能为空");        }      },      // 删除角色      doDeleteRole(role){        this.$confirm('此操作将永久删除'+role.nameZh+'角色, 是否继续?', '提示', {          confirmButtonText: '确定',          cancelButtonText: '取消',          type: 'warning'        }).then(() => {          this.deleteRequest('/system/basic/permiss/role/'+r.id).then(resp => {            if(resp.code == 200){                            this.initRoles();            }else{              this.$message.error(resp.message)            }          })        }).catch(() => {          this.$message({            type: 'info',            message: '已取消删除'          });                  });      }    }}</script><style>.permissManaTool{  display: flex;  justify-content: flex-start;}.permissManaTool .el-input{  width: 300px;  margin-right: 10px;}</style>

值得注意的是:

1:每个角色下面展开的权限树列表用的是手风琴组件(折叠面板)这里还要给树加上key,因为每个手风琴下面都是一个树 折叠面板饿了吗官网

2:树要展示前面的选择框,要给树组件加上show-checkbox属性。:default-checked-keys="selectMenus" 默认选中的key,这个selectMenus需要去data中定义一个数组。

3:在添加时,可通过 let tree = this.$refs.tree[index]; 拿到整个手风琴下面的树,let selectedKeys = tree.getCheckedKeys(true) 获取选中节点的id,修改成功后,把手风琴的折叠属性,定义成-1,折叠即可,因为下次再打开的时候,会去数据库查出这个角色对应菜单的最新数据。

 

 


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/60461.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1