React antd tree树组件 - 父子节点没有自动关联情况下 - 显示半选、全选状态以及实现父子节点互动

实现的效果图如下:

如Ant Design Vue 中所示,并没有提供获取半选节点的方法,当设置checked和checkStrictly时,父子节点也不再自动关联了

前提:从后端可以获取的数据分别是完整的树型数据、所有选中的节点数据(一个数组、同时包含 父节点和子节点),具体的大概数据可以看下面
树形结构(二重),parId是-1则表示是父节点数据:

这是返回的已选中节点的数组:

下面是具体的代码:

javascript 复制代码
state = {
  treeData: [], // 树形数据
  checkedKeys: [],
  checkable: false, // 因为我的页面是树型结构在页面右侧,根据左侧显示具体(看自己需求)
  loading: true,
  halfChecked: [], // 半选
  checked: [] // 全选
};

render() {
const { checkedKeys, treeData, checkable, halfChecked, checked, loading } = this.state
<Tree
  ref={r => this.treeRef = r}
  className={styles.menuTree}
  blockNode
  showLine
  // selectable={false}
  checkable={checkable}
  defaultExpandAll
  // checkedKeys={checkedKeys}
  checkedKeys={{
    checked,
    halfChecked
  }}
  checkStrictly
  onCheck={({ checked: ck, halfChecked: hc, ...oth }, { checked, node: { props: { dataRef } } }) => {
    // 选择
    if (checked && dataRef.id) {
      ck = ck.filter(i => i != `${dataRef.id}`).concat(`${dataRef.id}`);
    }
    // 如果有子级,则全部选上
    if(dataRef.parId == '-1'&&dataRef.childList&&dataRef.childList.length > 0){
      const kidKeys = this.getCKidKey(dataRef.childList);
      if(checked){
        ck = ck.concat(kidKeys);
      }else{
          ck = ck.filter(k=>{
          const bo= !kidKeys.includes(k);
          return bo;
        })
      }
    }
    // 如果选中的是子级,其父级也默认选中;取消选中时如果其父级下无选中内容,父级取消选中
    if(dataRef.parId != '-1'){
      if(checked){
        ck.push(dataRef.parId.toString())
        ck = Array.from(new Set(ck));
      }else{
        const ckId = dataRef.id; // 当前选中子级的id
        const ckParId = dataRef.parId; // 当前选中de子级的父级id
        const childList = this.treeMap[ckParId]?.childList; // 当前选中子级的父级 包含的子级
        let isHave = false // 父级下的子级是否有选中的,默认无选中的
        childList.forEach((item => {
          const ass = ck.includes(item.id.toString())
          if(ass) { // 如果还有选中的
            isHave = true
            return
          }
        }))
        if(!isHave){
          ck = ck.filter(item => item != ckParId)
        }
      }
    }
    const lastAllData = Array.from(new Set([...ck, ...hc]))
    this.parentStatus(lastAllData);
  }}
>
  {this.renderTreeNodes(treeData)}
</Tree>
}

主要是onCheck方法里面的处理,下面是用到的一些方法

javascript 复制代码
// 所有已选节点分成两组,全选、半选。
parentStatus = (checked) => { // 这里的checked是指传入所有已选节点
  const { treeData } = this.state; 
  const pData = [] // 半选的父级id数组
  const allPData = [] // 全选的父级id数组
  checked.forEach(i => {
    treeData.forEach(j =>{
      if(i == j.id){ // 如果有选中的父级
        const ckPList = [] // 选中父级的子级数组
        j.childList.forEach(r =>{
          ckPList.push(r.id.toString())
        })
        if(this.isContained(checked, ckPList)){
          allPData.push(j.id.toString())
        }else{
          pData.push(i.toString())
        }
      }
    })
  })
  const halfCkData = [] // 半选状态数据
  const allCkData = [] // 全选状态数据
  for(const i of checked){
    pData.includes(i)&&halfCkData.push(i);
    !pData.includes(i)&&allCkData.push(i);
  }
  this.setState({
    checked: allCkData,
    halfChecked: halfCkData,
  })
}

// 判断一个数组是否包含了另一个数组的全部元素
isContained = (a, b) => {
  // a和b其中一个不是数组,直接返回false
  if (!(a instanceof Array) || !(b instanceof Array)) return false;
  const len = b.length;
  // a的长度小于b的长度,直接返回false
  if (a.length < len) return false;
  for (let i = 0; i < len; i++) {
      if (!a.includes(b[i])) return false;
  }
  return true;
};

// 
ckeys = []

getKidKey = kids => {
  kids.reduce((p, c, ci, arr) => {
    p.push(c.key);
    if (c.children) {
      this.getKidKey(c.children);
    }
    return p;
  }, this.ckeys);
}

getCKidKey = kids => {
  this.getKidKey(kids);
  const cks = [...this.ckeys];
  this.ckeys = []
  return cks;
}

ps因为代码是随着需求优化慢慢增加的,所以命名可能有点乱,方法也是比较杂又多,写出来了就随它了,懒得优化就这样了。如果有帮助到你的话就很nice啦~

相关推荐
一颗花生米。16 分钟前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
学习使我快乐0120 分钟前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio199521 分钟前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
勿语&1 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈1 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水2 小时前
简洁之道 - React Hook Form
前端
正小安4 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch6 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光6 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   6 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发