解决 Vue3 + Element Plus 树形表格全选多选以及子节点勾选的问题

原文链接: 解决 Vue3 + Element Plus 树形表格全选多选以及子节点勾选的问题

前言

最近用到了 Element Plus 组件库的中的树形表格,但官网例子只能做到一层勾选,不能做到多层勾选,无法满足业务需求,所以研究了下,如何在子节点选满的情况下自动勾选上父节点? 勾选父节点时自动勾上全部子节点?

效果

从图中可看出,已支持父子节点联动,最后勾选的行数据保存在 multipleDevCreateList 。

代码

HTML 复制代码
<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>
Ts 复制代码
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 记得留言!

相关推荐
风骏时光牛马4 分钟前
大模型底层架构与推理流程AI源码深度剖析
前端
程序员Sunday9 小时前
JavaScript 事件循环面试题,宏任务与微任务怎么执行|Sunday面试指南
开发语言·javascript·面试·校招·事件循环·程序员sunday
Setsuna_F_Seiei9 小时前
前端转型 Agent 开发 06 之 Agent Memory 记忆系统(让 Agent 更智能,更懂你)
前端·agent·ai编程
志尊宝10 小时前
Vue3 零基础每日笔记(073):keep-alive 页面缓存实战——列表页返回不丢状态
vue.js·笔记·缓存·#vue #前端 #前端开发·#javascript·#vue.js
zhangzeyuaaa10 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
默_笙10 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
优选资讯11 小时前
标签打印软件怎么对接 Excel 数据
前端·excel
思无邪6611 小时前
用 AI 做 JS 逆向:从抓包到复现的完整方法论
开发语言·javascript·人工智能
一个风轻云淡11 小时前
GCC 和 GDB命令简单解读
java·linux·前端
u01110267512 小时前
图片处理接口如何统一错误信息 前端提示与后端错误码设计
前端·状态模式