el-tree 父子联动时,某些子节点取消时,父节点保留全选或半选状态

995 阅读2分钟

需求描述:系统菜单权限配置时,需要给每一个页面加上按钮权限。设计是,在选中页面的下一级可选中页面相应的按钮权限

实现如下效果:

  1. 在满足原来控件父子联动的情况下,当页面取消了所有按钮权限时,任然选中页面。
  2. 在取消所有页面时,页面对应的父级菜单也取消

image.png

el-tree中设置选中show-checkbox时,默认是父子联动的,选中父节点,其所有子节点也选中。若取消某节点的所有子节点,也取消该节点。

要实现如上功能,我们只需要对按钮级别的节点取消选中时特殊处理即可了。(因为有这样的需求: "取消所有按钮权限,也想要保留页面的权限")

有了方向就开始了新一轮探索之路,如何控制复选框显示状态?如何入手?

  1. 第一步入手的是点击事件,毕竟是通过点击事件来改变状态的。故探索之路,看官网

el-tree

image.png

可以看到,官网中的事件,如上两个是最符合要求的。对于复选框,这里check事件更适合。

但是这个事件并没有找到控制状态的回调参数,data时绑到树控件上的数据,第二个参数也是所有选中的数据。所以又使用了node-click事件,打印了一下回调参数

image.png 如上图可看到,indeterminate应该是半选状态,checked为全选状态。只要在check事件时,去改变按钮对应的父节点状态即可。那么问题来了,怎么能拿到想要的节点,继续看文档……

image.png

在方法中,找到getNode方法,入参是key,这个好办,check事件的第一个参数有我们所有的数据,只要设置了node-key="id"属性,就能拿到

准备就绪,开干吧!

<el-tree ref="tree"
         class="treeClass"
         :data="roleTree"
         show-checkbox
         node-key="id"
         @check="nodeClick"
         :check-strictly="false"
         :default-checked-keys="checkRoleArr"
         :props="defaultProps">
</el-tree>
// js
nodeClick(node,nodes){
    let curElnode = this.$refs.tree.getNode(node.id)
    console.log(curElnode)
    if(!curElnode.checked && node.type == 1) { // 这里type为1表示按钮 按钮取消 不取消按钮对应的页面
        nodes.checkedKeys.push(node.parentId);
        this.setParentNode(curElnode);
    }
    this.mapTree(nodes.checkedKeys,this.roleTree);  // 处理选中的数据,保存时传给后端
},
// 改变数据节点状态
mapTree(keys,arr){
    arr.forEach(item=>{
        if(keys.includes(item.id)){
            item.check = true
        }else{
            item.check = false
        }
        if(item.children.length > 0){
            this.mapTree(keys,item.children)
        }
    });
},

当然,入库之后就会有回显,有了前面的经验,回显也很简单。

// 获取树控件数据
handleRole(row){
    this.checkRoleArr= [] // 默认选中的节点
    GetMenu({isState: system.content.systemId},row.roleId).then(res=>{
        this.roleTree = res.data
        this.getCheckNode(JSON.parse(JSON.stringify(this.roleTree))) // 选中的节点
        this.$nextTick(() => { // 注意,设置默认选中的key之后,在$nextTick中才能通过getCheckedNodes方法拿到选中的Node
            const checkedNodes = this.$refs.tree.getCheckedNodes(); // 此时由于控件的父子联动功能,页面选中但是没有选中按钮时,按钮也被选中了
            checkedNodes.map( item => {
                if(!item.check && item.type == 1){// 取消没有设置权限的按钮
                    this.$refs.tree.getNode(item.id).checked = false; // 取消当前按钮的选中
                    node.checked = false; // 取消当前按钮的选中
                    this.setParentNode(node); // 该按钮的所有父节点设置成半选状态
                }
            });
        })
​
    })
},
setParentNode(node){
    if(node.parent){
        node.parent.indeterminate = true;
        this.setParentNode(node.parent);
    }
},
// 匹配已有的权限
getCheckNode(arr){
    arr.forEach(item=>{
        if(item.check){
            this.checkRoleArr.push(item.id)
        }
        if(item.children.length > 0){
            this.getCheckNode(item.children)
        }
    })
},
  1. 由于控件的父子联动功能,页面选中但是没有选中按钮时,按钮也被选中了。故回显时需要取消
  2. 对于情况1,使用递归设置所有父节点状态。可以根据需要,如要全选不用单独处理;如果要半选状态,需要单独处理。