2024.1.11今天我学习了如何对多个el-tree树进行相同节点的联动效果,如图:
这边有两棵树,我们发现第一个树和第二个树之间会有重复的指标,当我们选中第一个树的指标,我们希望第二个树如果也有重复的指标也能进行勾选上,反之也是。
一、难点:
(1)要让父节点变成半选状态
这个组件比较复杂的地方是要通过选中的子节点,拿到另外一个树对应的父节点,直接通过getHalfCheckedKeys是不行的,因为你当前不是在另外一个树做操作,获取不到当前的父节点,所以只能通过递归子节点,拿到对应的父节点。
(2)要拿到选中的所有子节点数据
setCheckedKeys(data,check)的data是拿到当前选中的子节点数据,如果当前子节点数据含有children数据,我们也需要通过递归的方法拿到所有的子节点数据。
二、重点方法:
setCheckedKeys(data,check)//data为当前选中节点的数据 check为所有选中节点的数据
三、关键步骤如下:
(1)第一个树选中的节点数据赋值给第二个树回显。
(2)第二个树选中的节点拼接第一个树选中的节点并过滤第二个树去掉的节点。
四、完整代码如下:
<template>
<div>
<el-tree
style="height: 30vh;overflow-y: scroll"
node-key="id"
ref="tree1"
:default-expand-all="true"
:props="defaultProps"
:data="tree_demo1"
@check="tree_check1"
show-checkbox
>
<span slot-scope="{ node, data }">【{{ data.id }}】{{ data.label }}</span>
</el-tree>
<hr/>
<el-tree
node-key="id"
ref="tree2"
style="height: 30vh;overflow-y: scroll"
:default-expand-all="true"
:props="defaultProps"
:data="tree_demo2"
@check="tree_check2"
show-checkbox
>
<span slot-scope="{ node, data }">【{{ data.id }}】{{ data.label }}</span>
</el-tree>
</div>
</template>
<script>
export default {
data() {
return {
tree_demo1: [],
tree_demo2: [],
default_data: [],
defaultProps: {
label: 'label',
children: 'children'
}
}
},
created() {
this.getList()
},
methods: {
getList() {
let demo = [
{
"children": [
{
"children": [
{
"id": "001",
"label": "指标一",
"parent_id": 100,
},
{
"id": "002",
"label": "指标二",
"parent_id": 100,
},
{
"id": "003",
"label": "指标三",
"parent_id": 100,
},
{
"id": "004",
"label": "指标四",
"parent_id": 100,
},
{
"id": "005",
"label": "指标五",
"parent_id": 100,
},
{
"id": "006",
"label": "指标六",
"parent_id": 100,
},
{
"id": "007",
"label": "指标七",
"parent_id": 100,
},
{
"id": "008",
"label": "指标八",
"parent_id": 100,
},
{
"id": "009",
"label": "指标九",
"parent_id": 100,
},
{
"id": "010",
"label": "指标十",
"parent_id": 100,
},
{
"id": "011",
"label": "指标十一",
"parent_id": 100,
},
{
"id": "012",
"label": "指标十二",
"parent_id": 100,
},
{
"id": "013",
"label": "指标十三",
"parent_id": 100,
},
{
"id": "014",
"label": "指标十四",
"parent_id": 100,
},
{
"id": "015",
"label": "指标十五",
"parent_id": 100,
},
{
"id": "016",
"label": "指标十六",
"parent_id": 100,
},
{
"id": "017",
"label": "指标十七",
"parent_id": 100,
},
{
"id": "018",
"label": "指标十八",
"parent_id": 100,
},
{
"id": "019",
"label": "指标十九",
"parent_id": 100,
},
{
"id": "020",
"label": "指标二十",
"parent_id": 100,
},
{
"id": "021",
"label": "指标二十一",
"parent_id": 100,
},
{
"id": "022",
"label": "指标二十二",
"parent_id": 100,
},
{
"id": "023",
"label": "指标二十三",
"parent_id": 100,
},
{
"id": "024",
"label": "指标二十四",
"parent_id": 100,
},
{
"id": "025",
"label": "指标二十五",
"parent_id": 100,
}
],
"id": "100",
"label": "指标分类1-1",
"parent_id": 1,
},
{
"children": [
{
"id": "026",
"label": "指标二十六",
"parent_id": 101,
},
{
"id": "027",
"label": "指标二十七",
"parent_id": 101,
},
{
"id": "028",
"label": "指标二十八",
"parent_id": 101,
},
{
"id": "029",
"label": "指标二十九",
"parent_id": 101,
},
{
"id": "030",
"label": "指标三十",
"parent_id": 101,
},
{
"id": "031",
"label": "指标三十一",
"parent_id": 101,
},
{
"id": "032",
"label": "指标三十二",
"parent_id": 101,
},
{
"id": "033",
"label": "指标三十三",
"parent_id": 101,
},
{
"id": "034",
"label": "指标三十四",
"parent_id": 101,
},
{
"id": "035",
"label": "指标三十五",
"parent_id": 101,
},
{
"id": "036",
"label": "指标三十六",
"parent_id": 101,
},
{
"id": "037",
"label": "指标三十七",
"parent_id": 101,
},
{
"id": "038",
"label": "指标三十八",
"parent_id": 101,
},
{
"id": "039",
"label": "指标三十九",
"parent_id": 101,
},
{
"id": "040",
"label": "指标四十",
"parent_id": 101,
}
],
"id": "101",
"label": "指标分类1-2",
"parent_id": 1,
},
],
"id": "1",
"label": "指标分类一",
"parent_id": 0,
"status": 1
},
{
"children": [
{
"id": "005",
"label": "指标五"
},
{
"id": "010",
"label": "指标十"
},
{
"id": "011",
"label": "指标十一"
},
{
"id": "016",
"label": "指标十六"
},
{
"id": "020",
"label": "指标二十"
},
{
"id": "030",
"label": "指标三十"
},
{
"id": "034",
"label": "指标三十四"
},
{
"id": "033",
"label": "指标三十三"
},
{
"id": "031",
"label": "指标三十一"
},
{
"id": "021",
"label": "指标二十一"
},
{
"id": "050",
"label": "指标五十"
},
{
"id": "060",
"label": "指标六十"
},
{
"id": "066",
"label": "指标六十六"
},
{
"id": "068",
"label": "指标六十八"
},
{
"id": "070",
"label": "指标七十"
},
],
"id": "2",
"label": "指标分类二"
}
]
this.tree_demo1 = [demo[0]]//第一棵树数据
this.tree_demo2 = [demo[1]]//第二棵树数据
},
// 第一棵树选中节点数据
tree_check1(data, check) {
this.component_check_data_method(data, check, 'tree2', 'tree_demo2')
},
// 第二棵树选中节点数据
tree_check2(data, check) {
this.component_check_data_method(data, check, 'tree1', 'tree_demo1')
},
//递归拿到选中所有的子节点数据
findChildrenIds(data) {
let all_ids = [data.id]
if (data.children && data.children.length > 0) {
for (let child of data.children) {
all_ids = all_ids.concat(this.findChildrenIds(child));//判断是否含有children数据,如果有就递归继续拼接
}
}
return all_ids
},
// 递归拿到对应所有的父节点数据
findParentIds(id, data) {
const parentIds = [];
function findNode(node, parentId) {
if (node.id === id) {
parentIds.push(parentId);
return true;
}
if (node.children) {
for (const child of node.children) {
if (findNode(child, node.id)) {
parentIds.push(parentId);
return true;
}
}
}
return false;
}
for (const node of data) {
if (findNode(node, null)) {
break;
}
}
return parentIds.reverse(); // 反转数组,让根节点id在最前面
},
// 通用选中节点获取数据的方法
component_check_data_method(data, check, other_tree_ref, other_tree_data) {
let first_data = this.$refs[other_tree_ref].getCheckedKeys()//获取另外一棵树的节点数据
let all_nodes = []
for (const nodeId of this.findChildrenIds(data)) {
const parentIds = this.findParentIds(nodeId, this[other_tree_data])
all_nodes.push(...parentIds)//拿到当前子节点对应的所有父节点
}
const filteredArr = [...new Set(all_nodes.filter(item => item !== null))]//去重和过滤null
let select_all_data = [...filteredArr, ...this.findChildrenIds(data)]//拼接所有父节点和所有选中的子节点
first_data = first_data.filter(item => !select_all_data.includes(item));//如果包含当前子节点就过滤
this.$refs[other_tree_ref].setCheckedKeys([...first_data, ...check.checkedKeys])
}
}
}
</script>
可以直接复制demo查看效果。大家如果有不懂的地方或是更好的方法可以分享到评论区,谢谢!