前言
项目中使用el-tree带复选框的,如果数据中存在禁用的节点 则需要点击多次复选框才能取消或选中
issue地址:github.com/element-plu…
实现效果
预览效果:admin.occtai.com/unclassifie…
实现代码
把官网的示例复制过来自己写个el-checkbox
<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部分代码
<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的部分代码
// 复选框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~