antd组织树之父子级联

需求:子级关联父级,父级不关联子级

主要需要花费时间去实现的是组织树的check勾选方法以及数据回显(全选、半选)

一、组织树勾选

kotlin 复制代码
check(checkedKeys, e) {
      const { checked, halfChecked, dataRef, eventKey } = e.node
      const { checked: selectChecked, halfChecked: halfSelectChecked } = checkedKeys
      console.log('checked:', checked, 'halfChecked:', halfChecked, e)
      // 判断有无子节点
      if (dataRef?.children && dataRef?.children?.length) {
        // 有子节点
        if (!checked && !halfChecked) {
          // 半选
          const nodes = this.getFathersById(eventKey, this.treeData, 'id').filter(
            (v) => v !== eventKey
          ) // 获取当前节点的所有父节点且过滤自身
          // 两个数组取交集,相同的部分取出来
          const remindData = selectChecked.filter((v) => nodes.indexOf(v) === -1)
          halfSelectChecked.push(eventKey)
          this.checkedKeys = {
            checked: remindData.filter((v) => v !== eventKey), // 把该key从全选里面去掉
            halfChecked: [...halfSelectChecked, ...nodes]
          }
        } else if (!checked && halfChecked) {
          // 全选
          const childNodesFirst = this.getChildNode(this.treeData, eventKey, [])
          const nodes = this.getFathersById(eventKey, this.treeData, 'id').filter(
            (v) => v !== eventKey
          ) // 获取当前节点的所有父节点且过滤自身
          let selectChecked1 = selectChecked
          let halfSelectChecked1 = halfSelectChecked
          let isSame
          if (nodes && nodes?.length > 0) {
            nodes.map((item) => {
              // 获取item下的所有子节点且包括item
              const childNodes = this.getChildNode(this.treeData, item, [])
              // 子节点全选时,点击全选状态变成空状态的交互
              const selectChecked2 = selectChecked1
              isSame = this.includes(
                [...childNodesFirst, ...selectChecked1],
                childNodes.filter((v) => v !== item)
              ) // 比较两个数组是否相同,后面的数组有一个没有在前面数组里面就false
              selectChecked2.push(item)
              selectChecked1 = isSame ? selectChecked2 : selectChecked1.filter((v) => v !== item)
              halfSelectChecked1 = halfSelectChecked
              halfSelectChecked1.push(item)
              return item
            })
          }
          this.checkedKeys = {
            checked: selectChecked1,
            halfChecked: isSame ? halfSelectChecked1 : halfSelectChecked
          }
          // 父节点全选
          this.checkedKeys = {
            checked: [...childNodesFirst, ...selectChecked1],
            halfChecked: [...halfSelectChecked]
          }
        } else {
          // 全不选
          const nodes = this.getFathersById(eventKey, this.treeData, 'id').filter(
            (v) => v !== eventKey
          ) // 获取当前节点的所有父节点且过滤自身
          const childNodes = this.getChildNode(this.treeData, eventKey, [])
          // 两个数组取交集,相同的部分取出来
          const remindData = selectChecked.filter((v) => childNodes.indexOf(v) === -1)
          const remindData2 = remindData.filter((v) => nodes.indexOf(v) === -1)
          // 把叶子节点的最近一级的父节点的半选去掉
          const remindHalfData = halfSelectChecked.filter((v) => childNodes.indexOf(v) === -1)
          this.checkedKeys = {
            checked: [...remindData2],
            halfChecked: [...remindHalfData, ...nodes] // 第一次在全选状态点击二级节点时,父节点变成空状态不是半选状态
          }
        }
      } else {
        // 无子节点
        const nodes = this.getFathersById(eventKey, this.treeData, 'id').filter(
          (v) => v !== eventKey
        ) // 获取当前节点的所有父节点且过滤自身
        let selectChecked1 = [...new Set(selectChecked)]
        let halfSelectChecked1 = halfSelectChecked
        let isSame
        let selectChecked2 = [] // 叶子节点全选反向选择的变量
        if (nodes && nodes?.length > 0) {
          nodes.forEach((item) => {
            // 获取item下的所有子节点且包括item
            const childNodes = this.getChildNode(this.treeData, item, [])
            // 子节点全选时,点击全选状态变成空状态的交互
            // const selectChecked2 = [...selectChecked1, ...nodes]
            // 这种会导致所有父节点都变成全选状态,但是实际爷节点是半选状态的时候,不需要变成全选
            isSame = this.includes(
              [...selectChecked, ...nodes], // 爷节点在孙子节点全选时没有变成全选状态
              childNodes.filter((v) => v !== item)
            ) // 比较两个数组是否相同,后面的数组有一个没有在前面数组里面就false
            // 子节点全选时只把当前父节点变成全选,爷节点不需要变成全选
            selectChecked2 = isSame
              ? [...selectChecked1, ...[item]]
              : selectChecked1.filter((v) => v !== item)
            selectChecked1 = selectChecked2
            halfSelectChecked1 = halfSelectChecked
            halfSelectChecked1.push(item)
            return item
          })
          this.checkedKeys = {
            checked: selectChecked1,
            halfChecked: isSame ? halfSelectChecked1 : halfSelectChecked
          }
        }
      }
      const checkedList = this.$lodash.uniq([
        ...this.checkedKeys.checked,
        ...this.checkedKeys.halfChecked
      ])
      this.$emit('onCheck', checkedList)
    }

数据回显

监听拿到需要回显的数据为一个数组,拿到当前组织树对应回显每个数据的所有子节点为一个数组,判断两个数组是否有交集,若有交集则为全选,无交集即半选。

javascript 复制代码
watch: {
    roleResources: {
      handler(newVal) {
        // this.checkedKeys = newVal
        const arr = newVal
        const checked = []
        const halfChecked = []
        arr.forEach((item) => {
          const value = this.getChildNode(this.treeData, item, []) // 获取item的所有子节点
          const isSame = this.includes(arr, value) // 判断两个数组是否有交集
          if (isSame) {
            checked.push(item) // true即勾选
          } else {
            halfChecked.push(item) // false即半选
          }
        })
        this.checkedKeys = { checked, halfChecked }
      },
      immediate: true,
      deep: true
    }
  },

完整代码

vue:

kotlin 复制代码
<template>
  <a-tree
    v-model="checkedKeys"
    checkable
    :checkStrictly="true"
    :checkedKeys="checkedKeys"
    :expanded-keys="expandedKeys"
    :auto-expand-parent="autoExpandParent"
    :selected-keys="selectedKeys"
    :tree-data="treeData"
    :replaceFields="replaceFields"
    @check="check"
    @expand="onExpand"
    @select="onSelect"
    class="role-ant-tree"
  />
</template>
<script>
import roleTreeChecked from '@/mixins/roleTreeChecked'

export default {
  name: 'RoleTree',
  mixins: [roleTreeChecked],
  props: {
    treeData: Array,
    roleResources: Array
  },
  data() {
    return {
      expandedKeys: [],
      autoExpandParent: true,
      checkedKeys: {
        checked: [],
        halfChecked: []
      },
      selectedKeys: [],
      replaceFields: { key: 'id' },
      childrenKeys: []
    }
  },
  watch: {
    roleResources: {
      handler(newVal) {
        // this.checkedKeys = newVal
        const arr = newVal
        const checked = []
        const halfChecked = []
        arr.forEach((item) => {
          const value = this.getChildNode(this.treeData, item, []) // 获取item的所有子节点
          const isSame = this.includes(arr, value) // 判断两个数组是否有交集
          if (isSame) {
            checked.push(item) // true即勾选
          } else {
            halfChecked.push(item) // false即半选
          }
        })
        this.checkedKeys = { checked, halfChecked }
      },
      immediate: true,
      deep: true
    }
  },
  methods: {
    onExpand(expandedKeys) {
      // if not set autoExpandParent to false, if children expanded, parent can not collapse.
      // or, you can remove all expanded children keys.
      this.expandedKeys = expandedKeys
      this.autoExpandParent = false
    },
    onSelect(selectedKeys) {
      this.selectedKeys = selectedKeys
    },
    handleChild(children) {
      children.forEach((item) => {
        this.childrenKeys.push(item.id)
        if (item?.children && item.children?.length > 0) {
          this.handleChild(item.children)
        }
      })
    },
    check(checkedKeys, e) {
      const { checked, halfChecked, dataRef, eventKey } = e.node
      const { checked: selectChecked, halfChecked: halfSelectChecked } = checkedKeys
      console.log('checked:', checked, 'halfChecked:', halfChecked, e)
      // 判断有无子节点
      if (dataRef?.children && dataRef?.children?.length) {
        // 有子节点
        if (!checked && !halfChecked) {
          // 半选
          const nodes = this.getFathersById(eventKey, this.treeData, 'id').filter(
            (v) => v !== eventKey
          ) // 获取当前节点的所有父节点且过滤自身
          // 两个数组取交集,相同的部分取出来
          const remindData = selectChecked.filter((v) => nodes.indexOf(v) === -1)
          halfSelectChecked.push(eventKey)
          this.checkedKeys = {
            checked: remindData.filter((v) => v !== eventKey), // 把该key从全选里面去掉
            halfChecked: [...halfSelectChecked, ...nodes]
          }
        } else if (!checked && halfChecked) {
          // 全选
          const childNodesFirst = this.getChildNode(this.treeData, eventKey, [])
          const nodes = this.getFathersById(eventKey, this.treeData, 'id').filter(
            (v) => v !== eventKey
          ) // 获取当前节点的所有父节点且过滤自身
          let selectChecked1 = selectChecked
          let halfSelectChecked1 = halfSelectChecked
          let isSame
          if (nodes && nodes?.length > 0) {
            nodes.map((item) => {
              // 获取item下的所有子节点且包括item
              const childNodes = this.getChildNode(this.treeData, item, [])
              // 子节点全选时,点击全选状态变成空状态的交互
              const selectChecked2 = selectChecked1
              isSame = this.includes(
                [...childNodesFirst, ...selectChecked1],
                childNodes.filter((v) => v !== item)
              ) // 比较两个数组是否相同,后面的数组有一个没有在前面数组里面就false
              selectChecked2.push(item)
              selectChecked1 = isSame ? selectChecked2 : selectChecked1.filter((v) => v !== item)
              halfSelectChecked1 = halfSelectChecked
              halfSelectChecked1.push(item)
              return item
            })
          }
          this.checkedKeys = {
            checked: selectChecked1,
            halfChecked: isSame ? halfSelectChecked1 : halfSelectChecked
          }
          // 父节点全选
          this.checkedKeys = {
            checked: [...childNodesFirst, ...selectChecked1],
            halfChecked: [...halfSelectChecked]
          }
        } else {
          // 全不选
          const nodes = this.getFathersById(eventKey, this.treeData, 'id').filter(
            (v) => v !== eventKey
          ) // 获取当前节点的所有父节点且过滤自身
          const childNodes = this.getChildNode(this.treeData, eventKey, [])
          // 两个数组取交集,相同的部分取出来
          const remindData = selectChecked.filter((v) => childNodes.indexOf(v) === -1)
          const remindData2 = remindData.filter((v) => nodes.indexOf(v) === -1)
          // 把叶子节点的最近一级的父节点的半选去掉
          const remindHalfData = halfSelectChecked.filter((v) => childNodes.indexOf(v) === -1)
          this.checkedKeys = {
            checked: [...remindData2],
            halfChecked: [...remindHalfData, ...nodes] // 第一次在全选状态点击二级节点时,父节点变成空状态不是半选状态
          }
        }
      } else {
        // 无子节点
        const nodes = this.getFathersById(eventKey, this.treeData, 'id').filter(
          (v) => v !== eventKey
        ) // 获取当前节点的所有父节点且过滤自身
        let selectChecked1 = [...new Set(selectChecked)]
        let halfSelectChecked1 = halfSelectChecked
        let isSame
        let selectChecked2 = [] // 叶子节点全选反向选择的变量
        if (nodes && nodes?.length > 0) {
          nodes.forEach((item) => {
            // 获取item下的所有子节点且包括item
            const childNodes = this.getChildNode(this.treeData, item, [])
            // 子节点全选时,点击全选状态变成空状态的交互
            // const selectChecked2 = [...selectChecked1, ...nodes]
            // 这种会导致所有父节点都变成全选状态,但是实际爷节点是半选状态的时候,不需要变成全选
            isSame = this.includes(
              [...selectChecked, ...nodes], // 爷节点在孙子节点全选时没有变成全选状态
              childNodes.filter((v) => v !== item)
            ) // 比较两个数组是否相同,后面的数组有一个没有在前面数组里面就false
            // 子节点全选时只把当前父节点变成全选,爷节点不需要变成全选
            selectChecked2 = isSame
              ? [...selectChecked1, ...[item]]
              : selectChecked1.filter((v) => v !== item)
            selectChecked1 = selectChecked2
            halfSelectChecked1 = halfSelectChecked
            halfSelectChecked1.push(item)
            return item
          })
          this.checkedKeys = {
            checked: selectChecked1,
            halfChecked: isSame ? halfSelectChecked1 : halfSelectChecked
          }
        }
      }
      const checkedList = this.$lodash.uniq([
        ...this.checkedKeys.checked,
        ...this.checkedKeys.halfChecked
      ])
      this.$emit('onCheck', checkedList)
    }
  }
}
</script>
<style lang="scss">
.role-ant-tree {
  height: 80%;
}
</style>

js(roleTreeChecked.js):

javascript 复制代码
const childsNodeDeepWay = (nodes, arr) => {
  if (nodes) {
    nodes.forEach((ele) => {
      arr.push(ele.id)
      if (ele.children) {
        childsNodeDeepWay(ele.children, arr)
      }
    })
  }
}

export default {
  methods: {
    getChildNode(nodes, item, arr) {
      nodes.forEach((el) => {
        if (el.id === item) {
          arr.push(el.id)
          if (el.children) {
            childsNodeDeepWay(el.children, arr)
          }
        } else if (el.children) {
          this.getChildNode(el.children, item, arr)
        }
      })
      return arr
    },
    getFathersById(id, data, prop = 'id') {
      const arr = []
      const rev = (dataList, IDS) => {
        let flag = false
        for (let i = 0, { length } = dataList; i < length; i += 1) {
          const node = dataList[i]
          if (node[prop] === IDS) {
            arr.unshift(node[prop])
            flag = true
          } else if (node.children && node.children.length) {
            if (rev(node.children, IDS)) {
              arr.unshift(node[prop])
              flag = true
            }
          }
        }
        return flag
      }
      rev(data, id)
      return arr
    },
    includes(arr1, arr2) {
      return arr2.every((val) => arr1.includes(val))
    },
    flatTree(treeData = []) {
      let result = []
      treeData.forEach((ele) => {
        // 先克隆一份数据作为第一层级的填充
        const arr = JSON.parse(JSON.stringify(ele))
        delete arr?.children
        result.push(arr)
        if (ele?.children && ele?.children?.length > 0) {
          result = result.concat(this.flatTree(ele.children))
        }
      })
      return result
    }
  }
}

参考文章:blog.csdn.net/m0_71630007...

相关推荐
寻找沙漠的人29 分钟前
前端知识补充—CSS
前端·css
GISer_Jing40 分钟前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_7482455242 分钟前
吉利前端、AI面试
前端·面试·职场和发展
理想不理想v1 小时前
webpack最基础的配置
前端·webpack·node.js
pubuzhixing1 小时前
开源白板新方案:Plait 同时支持 Angular 和 React 啦!
前端·开源·github
2401_857600951 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js
2401_857600951 小时前
数字时代的医疗挂号变革:SSM+Vue 系统设计与实现之道
前端·javascript·vue.js
GDAL1 小时前
vue入门教程:组件透传 Attributes
前端·javascript·vue.js
2402_857583491 小时前
基于 SSM 框架的 Vue 电脑测评系统:照亮电脑品质之路
前端·javascript·vue.js
web150850966412 小时前
在uniapp Vue3版本中如何解决webH5网页浏览器跨域的问题
前端·uni-app