核心代码如下:
javascript
const handleCheckedTree = data => {
if(!data) return [];
data.forEach(node => {
if (node.select === true) {
node.checked = true;
}
if (node.children && node.children.length > 0) {
handleCheckedTree(node.children);
}
});
return data;
}
const toggleCheck = (node, checked) =>{
//设置当前节点及其所有子节点的状态
setNodeAndChildren(node, checked);
//向上重新计算所有父节点的状态
updateAllParents(node);
}
const setNodeAndChildren = (node, checked) => {
// 清除半选状态
if (node.indeterminate) {
node.indeterminate = false;
}
node.checked = checked;
if (node?.children?.length) {
node.children.forEach(child => {
setNodeAndChildren(child, checked);
});
}
}
const updateAllParents = startNode => {
// 从当前节点开始,向上递归更新所有父节点
function updateParent(node) {
// 如果没有父节点,停止递归
if (!node.parent) return;
const parent = node.parent;
const children = parent.children || [];
// 检查子节点的状态
let checkedCount = 0;
let indeterminateCount = 0;
let uncheckedCount = 0;
children.forEach(child => {
if (child.indeterminate) {
indeterminateCount++;
} else if (child.checked) {
checkedCount++;
} else {
uncheckedCount++;
}
});
// 设置父节点的状态
if (indeterminateCount > 0) {
// 有子节点是半选状态,父节点一定是半选
parent.checked = false;
parent.indeterminate = true;
} else if (checkedCount === children.length) {
// 所有子节点都选中
parent.checked = true;
parent.indeterminate = false;
} else if (uncheckedCount === children.length) {
// 所有子节点都没选中
parent.checked = false;
parent.indeterminate = false;
} else {
// 部分选中,部分未选中(但没有半选)
parent.checked = false;
parent.indeterminate = true;
}
// 继续向上更新
updateParent(parent);
}
// 从当前节点开始向上更新
updateParent(startNode);
}
const initTreeBySelect = nodes => {
const buildTree = (nodeData, parent = null) => {
const node = {
...nodeData,
parent: parent,
checked: false,
indeterminate: false,
children: []
};
if (nodeData.children && nodeData.children.length > 0) {
node.children = nodeData.children.map(childData => buildTree(childData, node));
}
return node;
}
const tree = nodes.map(nodeData => buildTree(nodeData));
const calculateNodeState = node => {
if (!node.children || node.children.length === 0) {
node.checked = node.isSelect || false;
node.indeterminate = false;
return {
hasSelected: node.checked,
hasUnselected: !node.checked
};
}
// 非叶子节点:先计算子节点
let hasSelectedChild = false;
let hasUnselectedChild = false;
let allChildrenSelected = true;
node.children.forEach(child => {
const childResult = calculateNodeState(child);
if (childResult.hasSelected) hasSelectedChild = true;
if (childResult.hasUnselected) hasUnselectedChild = true;
// 检查子节点自身的checked状态
if (!child.checked && !child.indeterminate) {
allChildrenSelected = false;
}
});
if (node.isSelect) {
// 如果父级选中,强制所有子级为选中状态
node.checked = true;
node.indeterminate = false;
// 递归设置所有子节点为选中状态
const setAllChildrenChecked = (childNode) => {
childNode.checked = true;
childNode.indeterminate = false;
if (childNode.children && childNode.children.length > 0) {
childNode.children.forEach(setAllChildrenChecked);
}
};
node.children.forEach(setAllChildrenChecked);
} else {
// 父级未选中,根据子节点状态计算
if (hasSelectedChild && hasUnselectedChild) {
// 子节点部分选中,部分未选中
node.checked = false;
node.indeterminate = true;
} else if (hasSelectedChild && !hasUnselectedChild) {
// 所有子节点都选中(或没有子节点)
node.checked = true;
node.indeterminate = false;
} else if (!hasSelectedChild && hasUnselectedChild) {
// 所有子节点都未选中
node.checked = false;
node.indeterminate = false;
} else {
// 默认情况
node.checked = false;
node.indeterminate = false;
}
}
return {
hasSelected: node.checked || node.indeterminate || hasSelectedChild,
hasUnselected: !node.checked || hasUnselectedChild
};
}
// 计算每个根节点的状态
tree.forEach(root => calculateNodeState(root));
return tree;
};
使用方法
javascript
//后端返回了一个list
treeData= initTreeBySelect(res?.data || []);
//然后点击复选框方法
//item是点击的项目,
toggleCheck(item,!item.checked)
//处理完成之后包含三种状态
未选择: checked:false indeterminate:false
半选:checked:false indeterminate:true
全选:checked:true indeterminate:false