上一篇文章如何在el-tree懒加载并且包含下级的情况下进行数据回显-02
说了一下在包含下级的时候,数据的回显,通过nodesMap进行赋值,这次说一下在做这个需求的过程中遇到的一些问题:
loadNode(node, resolve) {// 处理回显主要是通过,store里面的nodesMap,nodesMap是一个对象,里面的键是id,值是id对应的node节点信息,包括是否全选checked, indeterminatethis.nodeStore = node.store || {};if (this.orgPower) {new Promise((_resolve) =>_resolve({data: {name: "总行",orgRefno: "01",},})).then((res) => {this.orgPower = false;this.powerOrgPk = res.data.orgRefno// 这里主要通过nodesMap来处理没有懒加载数据的回显// if (// this.nodesMap[res.data.orgRefno] &&// node.store.nodesMap[res.data.orgRefno]// ) {// node.store.nodesMap[res.data.orgRefno].checked =// this.nodesMap[res.data.orgRefno].checked;// node.store.nodesMap[res.data.orgRefno].indeterminate =// this.nodesMap[res.data.orgRefno].indeterminate;// }// // 这里主要是用来处理第一次是半选,没有点击展开按钮,加载数据,回显不了的问题// this.containlow &&// node.store.nodesMap[res.data.orgRefno] &&// (this.powerOrgPkIsIndeterminate =// node.store.nodesMap[res.data.orgRefno].indeterminate);return resolve([res.data]);});} else {new Promise((_resolve) =>_resolve(this.handleNodeData(node.data.orgRefno))).then((res) => {return resolve(res.data);const { data = [] } = res;// 添加parentKey属性,以便于在左侧不选中数据的时候,将右侧下级的数据也取消掉const parentKey = [node.data && node.data.orgRefno || '']let parent = node.parent;while(parent && parent.data) {parentKey.push(parent.data && parent.data.orgRefno || '')parent = parent.parent}data.forEach(item => {item.parentKey = parentKey.join(',')})let superInNotSelect = false;// 如果上级没有进行选择(这里要改变一下treeCheckKeyList的数据)if(!node.checked && !node.indeterminate && this.containlow) {superInNotSelect = true;data.forEach(item => {// 这里处理一下 treeCheckKeyList, 因为这里有一个bug:在手动触发 checked事件的时候,nodesMap中的checked标识会根据是否选中进行改变/*** bug复现* 步骤一:第一级不选,第二级不选,第三级有一个是全选* 步骤二:第一级全选,第二级全不选(checked事件),会展开之前选中的第三级,这时候会发现,第三级会被莫名奇妙的选上(treeCheckKeyList包含第三级),第二级成半选状态*/// 注意这里处理treeCheckKeyList要在resolve方法之前const findIndex = this.treeCheckKeyList.findIndex(key => key === item.orgRefno)if(findIndex > -1) {this.treeCheckKeyList.splice(findIndex, 1)}})this.cacheSearchTreeData = this.leftSelectTreeData;}resolve(res.data);let num = 0;let sonHasIndeteminate = false;data.forEach((item) => {// 这里主要是通过 nodesMap来处理没有懒加载数据的回显if (this.nodesMap[item.orgRefno] &&node.store.nodesMap[item.orgRefno]) {// 回显全选node.store.nodesMap[item.orgRefno].checked =this.nodesMap[item.orgRefno].checked;// 回显半选this.containlow &&(node.store.nodesMap[item.orgRefno].indeterminate =this.nodesMap[item.orgRefno].indeterminate);if(this.containlow && node.store.nodesMap[item.orgRefno].indeterminate) {sonHasIndeteminate = true;}if ((this.containlow &&node.store.nodesMap[item.orgRefno].checked) ||node.store.nodesMap[item.orgRefno].indeterminate) {num++;}}// 这里主要用来处理右侧回显的问题,之前是通过点击复选框,进行右侧回显的,现在要做成点击复选框,子节点也跟着选中,所以要把子节点也要弄到已选列表里面if(this.containlow) {if(node.checked) {const index = this.leftSelectTreeData.findIndex(_item => _item.orgRefno === item.orgRefno)// 如果没有存在右侧,则将子节点添加进去if(index === -1) {this.leftSelectTreeData.push(item)}if(node.store.nodesMap[item.orgRefno]) {node.store.nodesMap[item.orgRefno].checked = truenode.store.nodesMap[item.orgRefno].indeterminate = true}} else {if(node.store.nodesMap[item.orgRefno] && !node.indeterminate) {node.store.nodesMap[item.orgRefno].checked = false}}}})// 这里主要用来处理是否半选(在没有懒加载的时候)// 这里这样处理主要是因为,有一个这样的bug,如果第一级是半选的状态,第二级只有一个半选的状态,那么第一级在点击的时候,半选状态在赋值的时候消失// 把这里注释掉,把 nodesMap改成 可以复现这个bug// nodesMap: {// '01': { 'checked': false, indeterminate: true, name: '总行'},// '0101': { 'checked': false, indeterminate: true, name: '测试0101'},// '010101': { 'checked': true, indeterminate: true, name: '测试010101'}// }// 这里在这样处理的时候也会有一个问题// 这里需要增加一个变量来处理三层联动问题,如果第一级是半选,第二级也是半选,第三级也是半选,逐个向下点击,如果下级有一个是全选的,并且只有这一个,就会初选上层半选全会变成不选// this.containlow &&// (node.indeterminate = num > 0 && num !== data.length);this.containlow &&(node.indeterminate = num > 0 && num !== data.length && !node.checked || sonHasIndeteminate);if (this.containlow && node.indeterminate) {// 这里处理的问题和上面的问题类似:如果是第一层是半选状态(没有全选状态),第二层级也是只有半选状态没有全选状态,第三层级也是这样// 展开第一层级没有问题,但是展开第二层级的时候,就会有第一层级的半选状态消失的问题let parent =node.store.nodesMap[node.data.orgRefno] &&node.store.nodesMap[node.data.orgRefno].parent;while (parent) {parent.indeterminate = true;parent = parent.parent;}}return;});}
},
- 为什么在展开下级的时候,上级勾选状态会受影响?
因为下级数据勾选回显都是在resolve方法之后的,在执行resolve方法的时候,源码中有这样一个方法
this.loadData((data) => {if (data instanceof Array) {if (this.checked) {this.setChecked(true, true);} else if (!this.store.checkStrictly) {// 包含下级,会走这个方法reInitChecked(this);}done();}
});const reInitChecked = function(node) {if (node.childNodes.length === 0 || node.loading) return;const {all, none, half} = getChildState(node.childNodes);if (all) {node.checked = true;node.indeterminate = false;} else if (half) {node.checked = false;node.indeterminate = true;} else if (none) {// 这里会影响本级节点node.checked = false;node.indeterminate = false;}const parent = node.parent;if (!parent || parent.level === 0) return;if (!node.store.checkStrictly) {// 主要这里会影响父级reInitChecked(parent);}
};// 这个方法主要用来判断状态。
export const getChildState = node => {let all = true;let none = true;let allWithoutDisable = true;for (let i = 0, j = node.length; i < j; i++) {const n = node[i];if (n.checked !== true || n.indeterminate) {all = false;if (!n.disabled) {allWithoutDisable = false;}}if (n.checked !== false || n.indeterminate) {none = false;}}return { all, none, allWithoutDisable, half: !all && !none };
};