原先用的 el-tree 好好的,既好看又方便,但是产品呐就是会给咱找活做,非要搞横着的。身为打工人😭为了讨饭吃只能委屈求全
效果图:
大致就是手动实现一个tree 🐶
🤪🤪作为一个“卷心菜”
我能想到的只有使用递归
所以接下来xdm就会看到一堆递归来实现的tree(有啥别的思路和更好的方法请在评论区评论哦 🙏)
好啦废话不多说了,我们来分析vue实现 tree 的几个步骤
- 未知层级,能够按照横向树形来展示出来
- 点击节点,能够改变当前节点的勾选状态
- 点击一个含有子节点的节点,不仅仅改变当前节点的状态,还能根据当前节点的状态改变其子孙节点的状态
- 点击有父节点的节点,不仅仅改变当前节点的状态,还能根据当前节点的状态改变其父爷节点的状态
我们一层一层来实现
首先展示还是很简单的,我们直接用组件自调用就可实现(组件递归)
但是要⚠注意的是name记得要写上,相当于 components的的注册
这就展示出来了
是有点丑hhh
凑合看
第二点,也很简单,我们使用 v-model 绑定节点的勾选状态字段就好了
好了,我们要开始函数递归了,当节点状态改变时,我们要去更新他所有的子节点状态,这一看就能看懂就不多讲(这里的函数传值都是值传递即对treeList进行直接操作)
最后我们要去更新父节点的状态: 首先我们需要找到当前节点的直接父节点,我们能拿到当前节点直接父节点的id, 使用递归去找直接父节点
但是这里就出现问题了,你会发现一直找不到,一直找不到,当你打印 treeList 时,你会惊奇的发现,当前节点竟然是 treeList 的一个根节点,这是啥情况呢?
其实这就是组件自调用的问题,你跟节点所在的组件的treeList是上一级组件节点的children,所以导致在当前组件的 treeList 里永远找不到父节点。
那么问题来了,我们怎么找父节点呢,之前说了我们在当前组件是找不到了,那么我们去父组件呢?答案是 ofcourse
我们稍微改一下,点击当前节点时,如果当前节点有 parentId 那么咱们去触发父组件的changeParentNode 我们会发现!! 呀成功了,直接父节点的状态同步了
当然呢不光是直接父节点,还有祖父节点 ... 一直到根节点,这里又是递归哦 🤪
最后贴上tree组件代码
<template>
<div>
<div
v-for="item in treeList"
:key="item.id"
style="display: flex;"
:class="item.floor === 1 ? 'margin' : 'margin-1'"
>
<div style="width: 200px">
<span>{{ item.displayName }}</span>
<el-checkbox v-model="item.isGrants" style="margin-left: 10px;" @change="changeGrants(item.parentId, item)" />
</div>
<div>
<tree :tree-list="item.children" @changeParentNode="changeParentNode" />
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Tree',
props: {
treeList: {
type: Array,
default: () => []
}
},
methods: {
changeGrants(parentId, item) {
// 更新子节点的状态
this.checkNode(item.children, item.isGrants)
if (parentId) {
// 更新父节点的状态
this.$emit('changeParentNode', parentId)
}
},
checkNode(children, checked) {
children.forEach(item => {
item.isGrants = checked
if (item.children.length) {
this.checkNode(item.children, item.isGrants)
}
})
},
changeParentNode(parentId) {
this.findNode(parentId, this.treeList)
},
findNode(parentId, treeList) {
treeList.forEach(item => {
if (item.id === parentId) {
if (item.children.some(item => item.isGrants)) {
item.isGrants = true
} else {
item.isGrants = false
}
// 如果还有prentId
if (item.parentId) {
this.$emit('changeParentNode', item.parentId)
}
} else {
if (item.children.length) {
this.findNode(parentId, item.children)
}
}
})
}
},
mounted() {}
}
</script>
<style>
.margin {
padding-top: 20px;
padding-bottom: 20px;
border-bottom: 1px solid #eee;
}
.margin-1 {
padding-top: 5px;
padding-bottom: 5px;
}
</style>