原文链接: 解决 Vue3 + Element Plus 树形表格全选多选以及子节点勾选的问题
前言
最近用到了 Element Plus
组件库的中的树形表格,但官网例子只能做到一层勾选,不能做到多层勾选,无法满足业务需求,所以研究了下,如何在子节点选满的情况下自动勾选上父节点? 勾选父节点时自动勾上全部子节点?
效果
从图中可看出,已支持父子节点联动,最后勾选的行数据保存在 multipleDevCreateList
。
代码
<el-table ref="multipleDevCreateRef" v-model:selected-row-keys="multipleDevCreateList" :data="tableData"style="width: 100%" row-key="Path" default-expand-all @select="select" @select-all="selectAll"@selection-change="handleSelectionChange" :tree-props="{ children: 'Children' }":row-class-name="tableRowClassName"><el-table-column type="selection" width="55" :selectable="selectable" /><el-table-column property="Path" label="设备名" width="240" /><el-table-column property="TypStr" label="类型" /><el-table-column property="Mount" label="挂载点" /><el-table-column property="Capacity" label="容量" />
</el-table>
interface nodeItem {Path: string //路径Capacity: string // 空间Parent: string // 父节点(如果空就是根节点)Mount: string // 挂载点Typstr: string // 类型IsUsed: boolean // 是否使用Children?: nodeItem[]
}const multipleDevCreateRef = ref<InstanceType<typeof ElTable>>()
const multipleDevCreateList = ref<nodeItem[]>([])
const handleSelectionChange = (value: nodeItem[]) => {multipleDevCreateList.value = multipleDevCreateRef.value?.getSelectionRows()
}// 转化前数据:
/* [{"Capacity": "20.0GB","IsUsed": false,"Mount": "","Parent": "","Path": "/dev/sdb","TypStr": "disk"},{"Capacity": "19.9GB","IsUsed": false,"Mount": "","Parent": "/dev/sdb","Path": "/dev/sdb1","TypStr": "part"},{"Capacity": "200.0GB","IsUsed": false,"Mount": "","Parent": "","Path": "/dev/sdc","TypStr": "disk"},{"Capacity": "190.0GB","IsUsed": false,"Mount": "","Parent": "/dev/sdc","Path": "/dev/sdc1","TypStr": "part"},{"Capacity": "9.9GB","IsUsed": false,"Mount": "","Parent": "/dev/sdc","Path": "/dev/sdc2","TypStr": "part"},{"Capacity": "20.0GB","IsUsed": false,"Mount": "","Parent": "","Path": "/dev/sdd","TypStr": "disk"},{"Capacity": "19.9GB","IsUsed": false,"Mount": "","Parent": "/dev/sdd","Path": "/dev/sdd1","TypStr": "part"}
] */
// 转化后的数据
const tableData = ref<any[]>([{"Capacity": "200.0GB","IsUsed": false,"Mount": "","Parent": "","Path": "/dev/sdc","TypStr": "disk","Children": [{"Capacity": "190.GB","IsUsed": false,"Mount": "","Parent": "/dev/sdc","Path": "/dev/sdc1","TypStr": "part"},{"Capacity": "9.9GB","IsUsed": false,"Mount": "","Parent": "/dev/sdc","Path": "/dev/sdc2z","TypStr": "part"}]},{"Capacity": "20.0GB","IsUsed": false,"Mount": "","Parent": "","Path": "/dev/sdd","TypStr": "disk","Children": [{"Capacity": "19.9GB","IsUsed": false,"Mount": "","Parent": "/dev/sdd","Path": "/dev/sdd1","TypStr": "part"}]},{"Capacity": "20.0GB","IsUsed": false,"Mount": "","Parent": "","Path": "/dev/sdb","TypStr": "disk","Children": [{"Capacity": "19.9GB","IsUsed": false,"Mount": "","Parent": "/dev/sdb","Path": "/dev/sdb1","TypStr": "part"}]}
])const tableRowClassName = ({ row }: { row: nodeItem }) => {// 被使用了的设备 颜色加深 原生UI 不太明显if (row.IsUsed === true) {return 'disabled-row'} else {return ''}
}const selectable = (row: nodeItem) => {return row.IsUsed === false
}const setChildren = (children: nodeItem[], type: boolean) => {// 编辑多个子层级children.map((j: nodeItem) => {toggleSelection(j, type)if (j.Children) {setChildren(j.Children, type)}})
}// 设置父级选中/取消
const setParent = (currentRow: any, type: boolean, parent: nodeItem[], selectionLists: nodeItem[]) => {if (!parent.length) {parent = tableData.value}let allSelect: any[] = []parent.forEach((item: nodeItem) => {if (item.Children) {// 注:Parent 是当前选中节点的所有父节点的一个字符串形式的数据,这个很关键if (currentRow.Parent === item.Path) {// 选中if (type) {selectionLists.forEach((k: nodeItem) => {item.Children?.forEach((j: nodeItem) => {if (k.Path == j.Path) {allSelect.push(j)}})})if (allSelect.length == item.Children.length) {toggleSelection(item, type)selectionLists.push(item)select(selectionLists, item)} else {setParent(currentRow, type, item.Children, selectionLists)}} else {// 取消选中toggleSelection(item, type)setParent(currentRow, type, item.Children, [])}}}})
}const toggleSelection = (row: nodeItem, select: boolean) => {// 编辑多个子层级if (row) {multipleDevCreateRef.value?.toggleRowSelection(row, select)}
}// 选中父节点时,子节点一起选中/取消
const select = (selection: nodeItem[], row: nodeItem) => {const hasSelect = selection.some((el: nodeItem) => {return row.Path === el.Path})if (hasSelect) {if (row.Children) {// 解决子组件没有被勾选到setChildren(row.Children, true)}// 子节点被全勾选,父节点也勾上setParent(row, true, [], selection)} else {if (row.Children) {setChildren(row.Children, false)}// 子级取消选中, 传入当前选中节点, 所有父级取消选中setParent(row, false, [], [])}
}// 选择全部
const selectAll = (selection: nodeItem[]) => {// tabledata第一层只要有在selection里面就是全选const isSelect = selection.some((el: nodeItem) => {const tableDataPaths = tableData.value.map((j: nodeItem) => j.Path)return tableDataPaths.includes(el.Path)})// tableDate第一层只要有不在selection里面就是全不选const isCancel = !tableData.value.every((el: nodeItem) => {const selectPaths = selection.map(j => j.Path)return selectPaths.includes(el.Path)})if (isCancel) {tableData.value.map((el: nodeItem) => {if (el.Children) {// 解决子组件没有被勾选到setChildren(el.Children, false)}})}if (isSelect) {selection.map(el => {if (el.Children) {// 解决子组件没有被勾选到setChildren(el.Children, true)}})}
}
结语
应该没什么 bug ,遇到 bug 记得留言!