前端树形结构实现勾选,半勾选,取消勾选。

核心代码如下:

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
相关推荐
嵌入式进阶行者7 小时前
【算法】深度优先搜索实例:华为OD机考双机位A卷- 中庸行者
c++·算法·华为od·深度优先
a3535413827 小时前
参数化曲线弧长公式推导
算法
不知名XL7 小时前
day27 贪心算法 part05
算法·贪心算法
可问春风_ren7 小时前
前端文件上传详细解析
前端·ecmascript·reactjs·js
Tisfy7 小时前
LeetCode 3047.求交集区域内的最大正方形面积:2层循环暴力枚举
算法·leetcode·题解·模拟·枚举·几何
junziruruo8 小时前
t-SNE可视化降维技术(以FMTrack频率感知与多专家融合文章中的内容为例)
人工智能·算法
羊小猪~~8 小时前
【QT】--文件操作
前端·数据库·c++·后端·qt·qt6.3
藦卡机器人8 小时前
自动焊接机器人的核心技术要求与标准
人工智能·算法·机器人
2501_940315268 小时前
【无标题】1.17给定一个数将其转换为任意一个进制数(用栈的方法)
开发语言·c++·算法
栈与堆8 小时前
LeetCode 21 - 合并两个有序链表
java·数据结构·python·算法·leetcode·链表·rust