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

相关推荐
韩曙亮3 分钟前
【Web APIs】浏览器本地存储 ① ( window.sessionStorage 本地存储 | window.localStorage 本地存储 )
服务器·前端·javascript·本地存储·localstorage·sessionstorage·web apis
吃杠碰小鸡5 分钟前
前端Mac快速搭建开发环境
前端·macos
前端大波9 分钟前
使用webpack-bundle-analyzer 对 react 老项目进行打包优化
前端·react.js·webpack·性能优化
前端 贾公子17 分钟前
(catalog协议) == pnpm (5)
前端·javascript·react.js
JarvanMo21 分钟前
深度解析:如何彻底终结 Flutter 异步操作中的 BuildContext 崩溃?
前端
wxr061626 分钟前
部署Spring Boot项目+mysql并允许前端本地访问
前端·spring boot·mysql·持续部署
假装我不帅32 分钟前
jquery-validation使用
前端·javascript·jquery
怕浪猫37 分钟前
React从入门到出门第六章 事件代理机制与原生事件协同
前端·javascript·react.js
天府之绝40 分钟前
uniapp 中使用uview表单验证时,自定义扩展的表单,在改变时无法触发表单验证处理;
开发语言·前端·javascript·vue.js·uni-app
be or not to be42 分钟前
Html-CSS动画
前端·css·html