上一篇文章如何在el-tree懒加载并且包含下级的情况下进行数据回显-02
说了一下在包含下级的时候,数据的回显,通过nodesMap进行赋值,这次说一下在做这个需求的过程中遇到的一些问题:
loadNode(node, resolve) {
// 处理回显主要是通过,store里面的nodesMap,nodesMap是一个对象,里面的键是id,值是id对应的node节点信息,包括是否全选checked, indeterminate
this.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 = true
node.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 };
};