原文鏈接: 解決 Vue3 + Element Plus 樹(shù)形表格全選多選以及子節(jié)點(diǎn)勾選的問(wèn)題
前言
最近用到了 Element Plus
組件庫(kù)的中的樹(shù)形表格齿拂,但官網(wǎng)例子只能做到一層勾選抹缕,不能做到多層勾選腔丧,無(wú)法滿足業(yè)務(wù)需求咖为,所以研究了下踱启,如何在子節(jié)點(diǎn)選滿的情況下自動(dòng)勾選上父節(jié)點(diǎn)徽级? 勾選父節(jié)點(diǎn)時(shí)自動(dòng)勾上全部子節(jié)點(diǎn)苏章?
效果
從圖中可看出酿雪,已支持父子節(jié)點(diǎn)聯(lián)動(dòng)铸抑,最后勾選的行數(shù)據(jù)保存在 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="設(shè)備名" width="240" />
<el-table-column property="TypStr" label="類型" />
<el-table-column property="Mount" label="掛載點(diǎn)" />
<el-table-column property="Capacity" label="容量" />
</el-table>
interface nodeItem {
Path: string //路徑
Capacity: string // 空間
Parent: string // 父節(jié)點(diǎn)(如果空就是根節(jié)點(diǎn))
Mount: string // 掛載點(diǎn)
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()
}
// 轉(zhuǎn)化前數(shù)據(jù):
/* [
{
"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"
}
] */
// 轉(zhuǎn)化后的數(shù)據(jù)
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 }) => {
// 被使用了的設(shè)備 顏色加深 原生UI 不太明顯
if (row.IsUsed === true) {
return 'disabled-row'
} else {
return ''
}
}
const selectable = (row: nodeItem) => {
return row.IsUsed === false
}
const setChildren = (children: nodeItem[], type: boolean) => {
// 編輯多個(gè)子層級(jí)
children.map((j: nodeItem) => {
toggleSelection(j, type)
if (j.Children) {
setChildren(j.Children, type)
}
})
}
// 設(shè)置父級(jí)選中/取消
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 是當(dāng)前選中節(jié)點(diǎn)的所有父節(jié)點(diǎn)的一個(gè)字符串形式的數(shù)據(jù),這個(gè)很關(guān)鍵
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) => {
// 編輯多個(gè)子層級(jí)
if (row) {
multipleDevCreateRef.value?.toggleRowSelection(row, select)
}
}
// 選中父節(jié)點(diǎn)時(shí)鹊汛,子節(jié)點(diǎn)一起選中/取消
const select = (selection: nodeItem[], row: nodeItem) => {
const hasSelect = selection.some((el: nodeItem) => {
return row.Path === el.Path
})
if (hasSelect) {
if (row.Children) {
// 解決子組件沒(méi)有被勾選到
setChildren(row.Children, true)
}
// 子節(jié)點(diǎn)被全勾選蒲赂,父節(jié)點(diǎn)也勾上
setParent(row, true, [], selection)
} else {
if (row.Children) {
setChildren(row.Children, false)
}
// 子級(jí)取消選中, 傳入當(dāng)前選中節(jié)點(diǎn), 所有父級(jí)取消選中
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) {
// 解決子組件沒(méi)有被勾選到
setChildren(el.Children, false)
}
})
}
if (isSelect) {
selection.map(el => {
if (el.Children) {
// 解決子組件沒(méi)有被勾選到
setChildren(el.Children, true)
}
})
}
}
結(jié)語(yǔ)
應(yīng)該沒(méi)什么 bug ,遇到 bug 記得留言刁憋!