既然el-tree自带复选框有bug,那就自己实现复选框

前言

项目中使用el-tree带复选框的,如果数据中存在禁用的节点 则需要点击多次复选框才能取消或选中

issue地址:github.com/element-plu...

实现效果

预览效果:admin.occtai.com/unclassifie...

实现代码

把官网的示例复制过来自己写个el-checkbox

vue 复制代码
<el-tree :data="dataList" ref="treeRef" :props="treeProps" v-bind="attrs">
  <template #default="{ data }">
    <el-checkbox
      @click.stop
      :indeterminate="data.indeterminate"
      v-model="data.checked"
      :disabled="data[treeProps.disabled]"
      @change="handlchange($event, data)" />
    <slot :data="data"></slot>
  </template>
</el-tree>

script部分代码

vue 复制代码
<script setup>
import { computed, ref, useAttrs, watch } from 'vue'
import { ElTree } from 'element-plus'

import useCustomCheckBox from './useCustomCheckBox'
import {
  flattenTree,
  findAllParentNode,
  findAllSubNode,
} from '@/utils/dataProcessing.js'

const props = defineProps({
  props: {
    type: Object,
    default() {
      return {}
    },
  },
  data: {
    type: Array,
    default() {
      return []
    },
  },
})
const emit = defineEmits(['checkBoxChange', 'updNode'])
const attrs = useAttrs()
const dataList = ref([])
const treeRef = ref(null)

const treeProps = computed(() => {
  return {
    ...ElTree.props.props.default(),
    ...props.props,
  }
})

// 扁平数据
const flatDataList = computed(() => {
  return flattenTree(dataList.value)
})


const handlchange = (checked, data) => {
  emit('checkBoxChange', checked, data)
  checkBoxChange(checked, data)
}
const uniqueKey = computed(() => {
  return attrs['node-key'] || attrs['nodeKey']
})
const {
  checkBoxChange,
  getCheckedNodes,
  getCheckedKeys,
  setCheckedKeys,
  setCheckedNodes,
  clearAllSelection,
} = useCustomCheckBox({
  config: treeProps.value,
  attrs,
  uniqueKey,
  dataList,
})

// 这里实现用于给过滤后满足条件的节点包括对应父级打个标识,实现全选时只选择这批数据
const filter = async (val) => {
  flatDataList.value.forEach((item) => {
    if (
      val &&
      item[treeProps.value.label]
        .toLocaleLowerCase()
        .includes(val.toLocaleLowerCase())
    ) {
      item.display = true
    } else {
      item.display = !val || false
    }
  })

  flatDataList.value.forEach((item) => {
    const parents = findAllParentNode(
      dataList.value,
      item[uniqueKey.value],
      false,
      uniqueKey.value,
      treeProps.value.children
    )
    parents.forEach((parent) => {
      parent.display = parent.children.some((n) => n.display)
      parent[treeProps.value.disabled] = parent.children
        .filter((n) => n.display)
        .every((n) => n[treeProps.value.disabled])
    })
  })

  treeRef.value.filter(val)
  emit('updNode')
}

watch(
  () => props.data,
  (val) => {
    dataList.value = val
  },
  {
    immediate: true,
  }
)

defineExpose({
  treeRef,
  filter,
  getCheckedNodes,
  getCheckedKeys,
  setCheckedKeys,
  setCheckedNodes,
  clearAllSelection,
})

defineOptions({
  name: 'CustomElTreeCheckBox',
})
</script>

useCustomCheckBox的部分代码

js 复制代码
// 复选框change触发 处理父级选中或半选
const checkBoxChange = (checked, currentData) => {
  if (isCheckStrictlyTrue.value) return
  const parentData = findParentData(
    dataList.value,
    currentData[uniqueKey.value],
    uniqueKey.value,
    config.children
  )
  // 没有父级或有子级
  if (!parentData || currentData?.[config.children]?.length) {
    handleSubNodeCheckedAndIndeterminate(currentData, checked)
    currentData.indeterminate =
      hasAnyChildChecked(currentData) && !isAllChildrenChecked(currentData)
  }

  if (!parentData) return

  const allParentItems = findAllParentNode(
    dataList.value,
    currentData[uniqueKey.value],
    false,
    uniqueKey.value,
    config.children
  )
  allParentItems.reverse().forEach((item) => {
    const anyChecked = hasAnyChildChecked(item)
    item.indeterminate = anyChecked && !isAllChildrenChecked(item)
    item.checked = isAllChildrenChecked(item, anyChecked)
  })
}

// 清空时clearAll为true
const setCheckedKeys = (targetIds, checked = true, clearAll = false) => {
  // 扁平数据然后反转数据 反转主要为了处理对应父级的选中或半选状态
  let data = flattenTree(dataList.value)
  data.reverse().forEach((node) => {
    if (node.display !== false || clearAll) {
      if (checked === false) {
        // 如果targetIds存在则为false 否则为自身的值
        node.checked = targetIds.includes(node[uniqueKey.value])
          ? checked
          : node.checked
      } else {
        node.checked = targetIds.includes(node[uniqueKey.value])
      }

      if (node?.[config.children]?.length) {
        if (!isCheckStrictlyTrue.value) {
          if (node.checked) {
            handleSubNodeCheckedAndIndeterminate(
              node,
              checked,
              checked ? null : targetIds
            )
          }
          const anyChildChecked = hasAnyChildChecked(node)
          node.indeterminate = anyChildChecked && !isAllChildrenChecked(node)
          node.checked = isAllChildrenChecked(node, anyChildChecked)
        }
      }
    }
  })

总结

简单的实现了下自定义复选框,也就是递归处理数据,如有优解方式欢迎讨论学习

End~

相关推荐
时清云24 分钟前
【算法】合并两个有序链表
前端·算法·面试
小爱丨同学32 分钟前
宏队列和微队列
前端·javascript
持久的棒棒君1 小时前
ElementUI 2.x 输入框回车后在调用接口进行远程搜索功能
前端·javascript·elementui
2401_857297911 小时前
秋招内推2025-招联金融
java·前端·算法·金融·求职招聘
一 乐1 小时前
租拼车平台|小区租拼车管理|基于java的小区租拼车管理信息系统小程序设计与实现(源码+数据库+文档)
java·数据库·vue.js·微信·notepad++·拼车
undefined&&懒洋洋2 小时前
Web和UE5像素流送、通信教程
前端·ue5
大前端爱好者4 小时前
React 19 新特性详解
前端
小程xy4 小时前
react 知识点汇总(非常全面)
前端·javascript·react.js
随云6324 小时前
WebGL编程指南之着色器语言GLSL ES(入门GLSL ES这篇就够了)
前端·webgl
随云6324 小时前
WebGL编程指南之进入三维世界
前端·webgl