需求描述:系统菜单权限配置时,需要给每一个页面加上按钮权限。设计是,在选中页面的下一级可选中页面相应的按钮权限
实现如下效果:
- 在满足原来控件父子联动的情况下,当页面取消了所有按钮权限时,任然选中页面。
- 在取消所有页面时,页面对应的父级菜单也取消
el-tree中设置选中show-checkbox时,默认是父子联动的,选中父节点,其所有子节点也选中。若取消某节点的所有子节点,也取消该节点。
要实现如上功能,我们只需要对按钮级别的节点取消选中时特殊处理即可了。(因为有这样的需求: "取消所有按钮权限,也想要保留页面的权限")
有了方向就开始了新一轮探索之路,如何控制复选框显示状态?如何入手?
- 第一步入手的是点击事件,毕竟是通过点击事件来改变状态的。故探索之路,看官网
可以看到,官网中的事件,如上两个是最符合要求的。对于复选框,这里check事件更适合。
但是这个事件并没有找到控制状态的回调参数,data时绑到树控件上的数据,第二个参数也是所有选中的数据。所以又使用了node-click事件,打印了一下回调参数
如上图可看到,indeterminate应该是半选状态,checked为全选状态。只要在check事件时,去改变按钮对应的父节点状态即可。那么问题来了,怎么能拿到想要的节点,继续看文档……
在方法中,找到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,使用递归设置所有父节点状态。可以根据需要,如要全选不用单独处理;如果要半选状态,需要单独处理。