- 需求原因:全选时,传给接口的code数据太多了;
如果加上 check-strictly 父节点与子节点无关联,可以初步满足需求
效果如下使用了check-strictly的话,tree就没有了半选效果不好的地方:用户体验感不好,按道理你勾选中了南昌市的话,下面的子节点都需要全部勾选上的,但由于传参数据太多,不得已使用了 check-strictly,问题来了,此时需要满足俩个要求:
1、全选时只返回父节点
2、半选只返回所有勾选中的节点
3、举列子:勾选南昌市和景德镇市只传(3601,3602);勾选南昌市和景德镇市下面的俩个区只传(3601,360201,360202);
- 具体实现思路:
1、使用 @check=“handleChange” 组件自带的方法
2、拿到checkedInfo里面的checkedKeys(选中的key)和 halfCheckedKeys(半选的key:父节点)
3、创建一个tempKeys来存当前半选的父节点,存上一次点击时halfCheckedKeys的值,目的是找出半选变为全选的值,通过这个值,在
checkedKeys里面去除下面的子节点
4、去除子节点保留自己的节点,这里用的正则,判断前缀(因为数据格式比较统一)
5、特别需要注意:这里需要注意当没有半选变为全选的节点时,那么就只需要返回父节点的值,就是tempKeys和halfCheckedKeys 值一样的情况
- 代码实现
<el-tree
:load="loadNode"
v-loading="loading"
highlight-current
auto-expand-parent
show-checkbox
ref="treeRef"
node-key="gridCode"
lazy
:props="defaultProps"
:default-expanded-keys="expandCodes"
:default-checked-keys="modelValue"
@check="handleChange"
></el-tree>
const handleChange = (node: any, checkedInfo: { checkedKeys: any; halfCheckedKeys: any }) => {
const { checkedKeys, halfCheckedKeys } = checkedInfo;
const result = new Set<string>(checkedKeys);
// 找出"半选变为全选"的父节点
const becameFullChecked = tempKeys.value.filter(
key => !halfCheckedKeys.includes(key) && checkedKeys.includes(key)
);
if (becameFullChecked.length === 0) {
// 如果没有半选变为全选的节点,则只返回子节点全部选中的父节点值
const parentNodes = new Set<string>();
checkedKeys.forEach((key: string) => {
// 检查这个key是否是某个父节点的子节点
const isChild = checkedKeys.some((parentKey: string) => {
if (parentKey === key) return false;
return key.startsWith(parentKey);
});
if (!isChild) {
// 如果不是任何节点的子节点,说明它是父节点
parentNodes.add(key);
}
});
const finalResult = Array.from(parentNodes);
emit('update:modelValue', finalResult);
return;
}
// 对于这些父节点,去除所有"包含该父节点值的子节点值",但保留父节点本身
becameFullChecked.forEach(parentKey => {
// 正则匹配:以parentKey开头,且不是parentKey本身
const reg = new RegExp(`^${parentKey}(?!$)`);
Array.from(result).forEach(val => {
if (reg.test(val) && val !== parentKey) {
result.delete(val);
}
});
});
// 更新tempKeys为当前半选的父节点
tempKeys.value = [...halfCheckedKeys];
const finalResult = Array.from(result);
emit('update:modelValue', finalResult);
};
const props = defineProps({
modelValue: {
type: Array,
default: () => []
},
onlyMirco: {
// 是否微网格:这个可以不用
type: Boolean,
default: () => true
}
})
const defaultProps = {
label: 'gridName',
isLeaf: 'leaf',
disabled: (node: { level: any }) => {
return Number(node.level) < 6 && props.onlyMirco
},
lazy: true
}
const loading = ref(false)
const expandCodes = ref<string[]>([])
const tempKeys = ref<string[]>([])
const treeRef = ref()
onMounted(() => {
console.log('props.modelValue.length', props.modelValue.length, props.modelValue)
if (props.modelValue.length) {
getExpandCodes() //获取展开的code列表
loading.value = true
tempKeys.value = props.modelValue as string[]
}
else {
tempKeys.value = []
}
})
const getExpandCodes = () => {
// multiExpandedTreeByCode 这个是接口:替换成自己的
multiExpandedTreeByCode({
codes: props.modelValue + '' || userStore.userInfo?.globalCode
}).then((data: any[]) => {
let _expandCodes: Iterable<any> = []
data.forEach((paths) => {
paths.forEach((ele: { gridCode: any }) => {
_expandCodes.push(ele.gridCode)
})
})
expandCodes.value = Array.from(new Set(_expandCodes))
})
}
// 懒加载节点
const loadNode = (node: { data?: any; level?: any }, resolve: (arg0: any[]) => void) => {
const { level } = node
if (level) {
if (node.data.isLeaf || level > 5) {
resolve([])
loading.value = false
} else {
getGridChildren({
gridCode: node.data.gridCode
}).then((data: { level: any }[]) => {
resolve(data)
loading.value = false
})
}
} else if (props.onlyMirco) {
// 如果只是能选微网格,则查询下一级
getGridChildren({
gridCode: userStore.userInfo?.globalCode || '36'
}).then((data: { level: any }[]) => {
resolve(data)
})
} else {
// 否则本级也要查
getGrid({
gridCode: userStore.userInfo?.globalCode || '36'
}).then((data: any) => {
resolve([data])
})
}
}
上面用到的接口:都需要换成自己的
优化
- 存在一个比较深的bug: 假如我新增的时候勾选的是某个乡镇,然后保存,再进行编辑时,全选中他父级的父级,或者再上一级,此时,收集到的结果将包含所有勾选中的节点值
- 原因是因为我的 halfCheckedKeys 值没有变化,并且,没有乡镇父一级的code,导致无法去除掉子节点的值
- 列如我勾选的乡镇节点是【3602101,3602102,3602103】,此时我再直接勾选江西省(上一级的上一级),此时我的halfCheckedKeys 里面只有【36】,并没有3602,所以删除不了,估传参就是全部的code
- 解决办法:找出所有选中的节点,从最上层节点开始判断是否选中,如果选中则拿到该节点的值,没有则往下继续找,判断检查当前节点是否是其他选中节点的子节点
- 改写handleChange方法
const handleChange = (node: any, checkedInfo: { checkedKeys: any; halfCheckedKeys: any }) => {
const { checkedKeys} = checkedInfo;
// 找出所有选中的节点
const allCheckedNodes = checkedKeys.map((key: string) => {
return treeRef.value?.getNode(key);
}).filter(Boolean);
// 找出最上层的选中节点
const topLevelNodes = allCheckedNodes.filter((node: any) => {
// 检查当前节点是否是其他选中节点的子节点
return !allCheckedNodes.some((otherNode: any) => {
if (otherNode === node) return false;
// 检查otherNode是否是node的祖先节点
let parent = node.parent;
while (parent) {
if (parent === otherNode) return true;
parent = parent.parent;
}
return false;
});
});
// 获取最上层节点的gridCode
const finalResult = topLevelNodes.map((node: any) => node.data.gridCode);
emit('update:modelValue', finalResult);
};