目前我这边的需求时:当用户的选择,只保留最顶层的选中节点
如果选中了父节点,则移除其所有子孙节点以及它的祖先节点(因为选中父节点代表选中整个分支,所以不需要再显示子节点;同时,如果存在祖先节点,那么祖先节点也会因为当前父节点的选中而变成部分选中,但我们希望只保留当前父节点,移除祖先节点)。
如果只选中了子节点(没有选中任何父节点),则移除这些子节点的所有父节点(即只显示叶子节点)
多层级选中逻辑处理:
- 需要同时处理父节点、子节点、祖先节点的复杂选中关系
- 当选中父节点时,需要自动移除其所有子节点和祖先节点
- 当只选中子节点时,需要移除所有父级节点
- 处理节点间的嵌套关系(父子、祖孙等)
性能优化:
- 递归遍历子节点时可能遇到深层嵌套数据结构
- 需要高效处理大规模节点数据集
- 使用Set进行去重和快速查找
状态一致性维护:
- 需要确保最终选中的key集合与级联选择器的实际状态一致
- 处理Element UI级联选择器返回的选中节点数据结构
- 协调flat()展开的选中值和节点树结构的关系
具体代码实现方式:
<template>
<el-cascader
ref="cascaderRef"
v-model="selectedOptions"
:options="options"
:props="{ multiple: true }"
@change="handleChange"
></el-cascader>
</template>
<script>
export default {
data() {
return {
selectedOptions: [],
options: [
{
value: 'parent1',
label: 'Parent 1',
children: [
{ value: 'child1', label: 'Child 1' },
{ value: 'child2', label: 'Child 2' }
]
},
{
value: 'parent2',
label: 'Parent 2',
children: [
{ value: 'child1', label: 'Child 1' },
{ value: 'child2', label: 'Child 2' }
]
}
],
selectKeys:[]
};
},
methods: {
// 选中
handleChange(e) {
// 获取当前选中的值(去重)
const selectData = [...new Set(e.flat())];
// 获取所有选中的节点
const checkedNodes = this.$refs.cascaderRef.getCheckedNodes();
// 找出所有选中的父节点(非叶子节点)
const parentNodes = checkedNodes.filter(
(node) => node.children && node.children.length > 0
);
// 找出所有选中的子节点
const childrenNodes = checkedNodes.filter(
(node) => node.children.length == 0
);
// 只选择了子节点(没有父节点) // 选中了至少一个父节点
this.selectKeys =
parentNodes.length === 0
? this.getChildren(checkedNodes, selectData)
: this.getParent(parentNodes, selectData, childrenNodes);
console.log(this.selectKeys, '========>');
},
// 处理选中父节点,去除子节点和父节点的父节点
getParent(parentNodes, selectAllKey, childrenNodes) {
// 创建两个Set用于存储需要移除的节点
const nodesToRemove = new Set(); // 所有需要移除的节点
const parentValues = new Set(); // 所有选中的父节点值
// 处理每个选中的父节点
parentNodes.forEach((parentNode) => {
// 添加当前父节点到集合
parentValues.add(parentNode.value);
console.log(parentNode, 'parentNode');
// 1. 收集当前父节点的所有上级节点(父节点的父节点)
if (parentNode.pathNodes && parentNode.pathNodes.length > 1) {
const parentPath = parentNode.pathNodes.filter(
(item) => !item.checked
); // 移除父级节点选中数据(当前节点)
parentPath.forEach((path) => nodesToRemove.add(path.value));
}
// 2. 递归收集所有子节点的值
const collectChildrenValues = (children) => {
children.forEach((child) => {
nodesToRemove.add(child.value);
if (child.children) {
collectChildrenValues(child.children);
}
});
};
collectChildrenValues(parentNode.children);
});
// 3. 最终选中的值 = 原始选中值 - 需要移除的节点
const finalSelected = selectAllKey.filter(
(value) => !nodesToRemove.has(value)
);
// 获取当前选中的子节点,过滤掉处理过的子节点
const childrenList =
childrenNodes
.filter((item) => !nodesToRemove.has(item.value))
.map((item) => item.value) || [];
const selectKey = [...new Set([...finalSelected, ...childrenList])];
// console.log('处理父节点结果:', selectAllKey, {
// finalSelected,
// selectKey,
// removedNodes: [...nodesToRemove],
// parentNodes: [...parentValues],
// });
return selectKey;
},
// 处理只选中子节点的情况(移除父节)
getChildren(checkedNodes, selectAllKey) {
// 收集所有子节点的父级
const parentPaths = [];
checkedNodes.forEach((item) => {
// 移除当前节点值,保留父级路径
if (item.path && item.path.length > 1) {
const path = item.path.slice(0, -1); // 移除最后一个元素(当前节点)
parentPaths.push(...path);
}
});
// 去重父级
const uniqueParentPaths = [...new Set(parentPaths)];
// 过滤掉所有父级,只保留子节点
const selectKey = selectAllKey.filter(
(item) => !uniqueParentPaths.includes(item)
);
// console.log('只选择子节点结果:', {
// selectKey, //当前选中
// parentPaths: uniqueParentPaths, //父级节点
// originalSelect: selectData, //所有选中数据(包含父节点)
// });
return selectKey;
},
}
};
</script>