既然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~

相关推荐
Wiktok15 分钟前
pureadmin的动态路由和静态路由
前端·vue3·pureadmin
devii6615 分钟前
html.
前端
掘金安东尼19 分钟前
为什么浏览器要限制 JavaScript 定时器?
前端·javascript·github
学前端搞口饭吃23 分钟前
react context如何使用
前端·javascript·react.js
GDAL28 分钟前
为什么Cesium不使用vue或者react,而是 保留 Knockout
前端·vue.js·react.js
IT_陈寒30 分钟前
《Java 21新特性实战:5个必学的性能优化技巧让你的应用快30%》
前端·人工智能·后端
小谭鸡米花40 分钟前
uni小程序中使用Echarts图表
前端·小程序·echarts
芜青43 分钟前
【Vue2手录11】Vue脚手架(@vue_cli)详解(环境搭建+项目开发示例)
前端·javascript·vue.js
a别念m1 小时前
前端架构-CSR、SSR 和 SSG
前端·架构·前端框架
麦麦大数据2 小时前
J002 Vue+SpringBoot电影推荐可视化系统|双协同过滤推荐算法评论情感分析spark数据分析|配套文档1.34万字
vue.js·spring boot·数据分析·spark·可视化·推荐算法