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

核心代码如下:

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
相关推荐
diudiu_339 小时前
web漏洞--认证缺陷
java·前端·网络
阿珊和她的猫10 小时前
<video>` 和 `<audio>` 标签的常用属性解析
前端
laocooon52385788610 小时前
一个C项目实现框架
c语言·算法
LSL666_10 小时前
4 jQuery、JavaScript 作用域、闭包与 DOM 事件绑定
前端·javascript·html
yinuo11 小时前
前端跨页面通讯终极指南⑤:window.name 用法全解析
前端
小飞侠在吗11 小时前
vue computed 和 watch
前端·javascript·vue.js
yinuo11 小时前
前端跨页面通讯终极指南④:MessageChannel 用法全解析
前端
前端布鲁伊11 小时前
聊聊前端容易翻车的“环境管理”
前端·面试
c#上位机11 小时前
halcon图像增强——图像取反
图像处理·算法·c#·halcon