- 需求原因:全選時,傳給接口的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-currentauto-expand-parentshow-checkboxref="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 = truetempKeys.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 } = nodeif (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;});});// 獲取最上層節點的gridCodeconst finalResult = topLevelNodes.map((node: any) => node.data.gridCode);emit('update:modelValue', finalResult);
};