如果你用el-tree做菜单和按钮的权限数据选择,那么按钮和他所属的菜单联动关系之一是当前菜单可以独立选择,即不选按钮可单独选择菜单。
那么问题来了,el-tree的联动模式只有是否强制遵循父子联动关系,并没有为单独的特例而破格。在菜单和按钮权限的大背景之下,我们需要的是按钮和他的上级菜单不遵循强制的父子联动关系,而其余菜单则强制遵循父子联动关系。明白了这一点之后,我们需要对el-tree做一下简单的封装,写上几个函数帮助我们完成代办的事项。
下面是最后实现的效果展示
首先要明确一点el-tree判断一个节点是否被勾选是根据当前节点对象数据中的checked去判断的,checked 为true则判定勾选,false则为未勾选。一个节点对象的数据结构如下所示:
然后我们把el-tree的联动模式设置为:check-strictly="true" 严格的遵循父子不互相关联,剩下的操作就是我们自己去完成菜单和菜单之间,菜单和按钮之间的联动关系。其中的核心操作就是在合适的时机去设置节点的checked值。
el-tree二次封装的代码如下:
<template>
<el-tree
:data="data"
show-checkbox
:node-key="nodeKey"
:default-checked-keys="defaultCheckedKeys"
highlight-current
:check-strictly="true"
@check="nodeCheck"
:ref="refKey"
>
</el-tree>
</template>
<script>
let count = 0
export default {
name: 'hb-tree',
props: {
'data': {
type: Array,
default () { return [] }
},
'nodeKey': {
type: String,
default: 'id'
},
'defaultCheckedKeys': {
type: Array,
default () { return [] }
},
'unlinkNode': {
type: Array,
default () { return ['type', 'button'] }
},
'checkedKeyList': {
type: Array,
default () { return [] }
}
},
watch: {
defaultCheckedKeys () {
this.$emit('update:checkedKeyList', this.defaultCheckedKeys)
}
},
data () {
return {
refKey: '',
treeNode: ''
}
},
methods: {
setNodes (list = [], flag = false) {
if (!list.length) return []
for (let item of list) {
item.checked = flag
if (item.childNodes && item.childNodes.length) this.setNodes(item.childNodes, flag)
}
},
getParentKey (node = {}, ret = []) {
node.key && ret.push(node.key)
if (node.parent) this.getParentKey(node.parent, ret)
return ret
},
unSelectedMenu (node) {
if (node.id === 0) return
let flag = true
for (let item of node.childNodes) {
if (item.checked) {
flag = false
break
}
}
if (flag) {
node.checked = false
this.unSelectedMenu(node.parent)
}
},
nodeCheck (data, obj) {
let isSelected = obj.checkedKeys.includes(data.id)
let curNode = this.treeNode.getNode(data.id)
let nodes = curNode.childNodes || []
this.setNodes(nodes, isSelected)
if (isSelected) {
let keys = this.getParentKey(curNode.parent)
keys.forEach(item => {
this.treeNode.setChecked(item, true)
})
}
if (!isSelected && (data[this.unlinkNode[0]] !== this.unlinkNode[1])) this.unSelectedMenu(curNode.parent)
this.$emit('check', data, obj)
this.$emit('update:checkedKeyList', this.treeNode.getCheckedKeys())
},
getTreeNode () {
return this.$refs[this.refKey]
}
},
created () {
this.refKey = this.ref || `tree${count++}`
},
mounted () {
this.treeNode = this.getTreeNode()
}
}
</script>
这里解释一下unlinkNode的作用,unlinkNode可以指定需要解除联动关系节点的某个属性和属性值,这会用于后续的判断,这里的默认值是['type', 'button'], 即代表如果节点的type是button,那么取消该节点时将不会和上级发生联动。
操作函数中对于checked的赋值都是用到了递归,关于树形结构的递归可以参考笔者的另外几篇博客非标准树状结构数据的处理,js遍历树形结构方法,过滤树形结构数据并获取新的树形结构。这里简单讲一些封装的主要逻辑,当你操作复选框的时候就会进入nodeCheck进行一系列判断的。首先我们要获取当前节点的选中状态(true为被选中,false为未选中),然后根据这个状态去设置他子节点的checked值,接下来如果他处于被选中的状态那么去设置他父级节点的选中状态(这里仅当他父级节点未选中时才会去设置父级节点的checked为true)。如果当前被操作的节点处于未勾选的状态那么会根据unlinkNode去判断是否要进一步的取消上级节点的选中状态。
关于el-tree取消特定叶子节点与上级的勾选联动的介绍就到此结束了,如有任何疑问可与bolg下方留言。