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

news2024/11/22 20:10:22

原文链接: 解决 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 记得留言!

6289ec15a6149.jpg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1126325.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PHP 预定义超全局变量 笔记/练习

预定义超全局数组变量 $_FILES 练习在最后 其他练习跟在每条笔记后 概述 预定义&#xff1a;预定义变量是 PHP 已定义&#xff0c;可以直接使用超全局&#xff1a;作用域是全局&#xff0c;可以在脚本的任何地方&#xff08;包括函数内部、外部&#xff09;都可以进行访问 常…

CSS必学:你需要知道的盒子模型的秘密

作者:WangMin 格言:努力做好自己喜欢的每一件事 CSDN原创文章 博客地址 &#x1f449; WangMin 作为前端开发来说&#xff0c;要掌握的CSS基础一定很多&#xff0c;那么CSS中盒子模型肯定是必考必问必掌握的前端知识点&#xff0c;因为它是CSS基础中非常重要的内容&#xff0c;…

分步实现编写LED驱动,实现特备文件和设备的绑定

通过字符设备驱动的分步实现编写LED驱动&#xff0c;另外实现特备文件和设备的绑定&#xff0c;发布到CSDN head.h #ifndef __HEAD_H__ #define __HEAD_H__ typedef struct {unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int ID…

photoshop2024免费插件Portraiture3

随着手机摄影的普及&#xff0c;修图可以说是现代人的必备生活技能之一了&#xff0c;现在谁发个朋友圈不把自己的照片修的美美的呢&#xff1f;那么如何拥有一张氛围感满满的照片呢&#xff1f;这不得不提图片处理软件中的王牌——photoshop。作为专业的图片处理软件&#xff…

Node.js与npm版本比对

Node.js与npm版本比对 Node.js与npm版本比对版本对比表Node版本对比 Node.js与npm版本比对 我们在项目开发过程中&#xff0c;经常会遇到公司一些老的前端工程项目&#xff0c;而我们当前的node及npm版本都是相对比较新的了。 在运行以前工程时&#xff0c;会遇到相关环境不匹…

2023年中国汽车差速器需求量、竞争现状及行业市场规模分析[图]

差速器是汽车驱动系统的主要部件&#xff0c;它的作用就是在向两边半轴传递动力的同时&#xff0c;允许两边半轴以不同的转速旋转&#xff0c;满足两边车轮尽可能以纯滚动的形式作不等距行驶&#xff0c;减少轮胎与地面的摩擦。汽车差速器是驱动车轮差速转弯或复杂路面强力通过…

KNN(K近邻)水仙花的分类(含答案)

题目 以下采用K-NN算法来解决水仙花的分类问题&#xff0c;每个样本有两个特征&#xff0c;第一个为水仙花的花萼长度&#xff0c;第二个为水仙花 的花萼宽度&#xff0c;具体数据见表&#xff0c; 1&#xff09;设置k3&#xff0c; 采用欧式距离&#xff0c;分析分类精度为多少…

【强化学习】06 —— 基于无模型的强化学习(Control)

文章目录 简介On-policy Monte-Carlo ControlGLIEMC vs. TD Control On-policy Temporal Difference ControlSarsaExample1 Windy GridworldForward View Sarsa(λ)Backward View Sarsa(λ)Sarsa 代码 Off-Policy Learning重要性采样Importance Sampling使用重要性采样的离线策…

【计算机网络】应用层协议--HTTP协议及HTTP报文格式

目录 1、HTTP是什么 2、HTTP请求与响应 3、HTTP请求的两种方法(get和post)及区别 (面试题) 4、几种常见的错误的说法 5、HTTP协议的特点 6、应用场景 7、HTTP报文格式 8、面试题&#xff1a;HTTP常见的状态码都有哪些&#xff1f; 1、HTTP是什么 HTTP协议是在Web上进行…

Day08File类IO流字符集

File:代表文本和文件夹 File类只能对文件本身进行操作&#xff0c;不能读写文件里面存储的数据。 创建File类的对象 路径写法 绝对路径:从盘符开始 File file new File(“D:\\ceshi\\a.txt”); 相对路径:不带盘符&#xff0c;默认直接到当前工程下的目录寻找文件 F…

Ubuntu 安装 npm 和 node

前言 最近学习VUE&#xff0c;在ubuntu 2204 上配置开发环境&#xff0c;涉及到npm node nodejs vue-Cli脚手架等内容&#xff0c;做以记录。 一、node nodejs npm nvm 区别 &#xff1f; node 是框架&#xff0c;类似python的解释器。nodejs 是编程语言&#xff0c;是js语言的…

[AUTOSAR][诊断管理][ECU][$19] 读取ECU的DTC故障信息

一、简介 在车载诊断中常用的诊断协议有ISO 14229等&#xff0c;在协议中主要定义了诊断请求、诊断响应的报文格式及ECU该如何处理诊断请求的应用。其中ISO 14229系列标准协议定义了用于行业内诊断通信的需求规范&#xff0c;也就是UDS。UDS主要应用于OSI七层模型的第七层——…

LInux系统编程(3)

取得拓展属性 #include <sys/types.h> #include <attr/xattr.h>ssize_t getxattr(const char* path, const char* key, void* value, size_t size); ssize_t lgetxattr(const char* path, const char* key, void* value, size_t size); ssize_t fgetxattr(int fd,…

FreeRTOS 任务的创建与删除

目录 1. 什么是任务&#xff1f; 2. 任务创建与删除相关函数 任务创建与删除相关函数有如下三个&#xff1a; 任务动态创建与静态创建的区别&#xff1a; xTaskCreate 函数原型​编辑 vTaskDelete 函数原型 3. 创建两个任务进行点灯实操 使用CubeMX快速移植 1.增加两个…

【谢希尔 计算机网络】第4章 网络层

目录 网络层 网络层的几个重要概念 网络层提供的两种服务 网络层的两个层面 网际协议 IP 虚拟互连网络 IP 地址 IP 地址与 MAC 地址 地址解析协议 ARP IP 数据报的格式 IP 层转发分组的过程 基于终点的转发 最长前缀匹配 使用二叉线索查找转发 网际控制报文协议…

2023CSPS 种树 —— 二分+前缀和

This way 题意&#xff1a; 一开始以为是水题&#xff0c;敲了一个二分贪心检查的代码&#xff0c;20分。发现从根往某个节点x走的时候&#xff0c;一路走来的子树上的节点到已栽树的节点的距离会变短&#xff0c;那么并不能按照初始情况贪心。 于是就想着检查时候用线段树…

2023-1024‍节日(内含表白代码)

文章目录 一、前言二、代码实现三、动态展示四、总结 一、前言 1024可以是计算机操作系统的进制单位&#xff0c;也可以是&#x1f9d1;‍&#x1f4bb;程序员们的特殊纪念日。 每年10月24日被行业认定为“程序员节”。 今天&#xff0c;正是一年一度的“1024程序员节”在此纪…

LeetCode讲解篇之面试题 01.08. 零矩阵

文章目录 题目描述题解思路题解代码 题目描述 题解思路 遍历矩阵&#xff0c;若当前元素为零&#xff0c;则将该行和该列的第一个元素置零 遍历第一行&#xff0c;若当前元素为零&#xff0c;则将当前列置零 遍历第一列&#xff0c;若当前元素为零&#xff0c;则将当前行置零 …

Kong:高性能、插件化的云原生 API 网关 | 开源日报 No.62

Kong/kong Stars: 35.2k License: Apache-2.0 Kong 是一款云原生、平台无关且可扩展的 API 网关。它以高性能和插件化的方式脱颖而出&#xff0c;提供了代理、路由、负载均衡、健康检查和认证等功能&#xff0c;并成为编排微服务或传统 API 流量的中心层。 以下是 Kong 的核心…

浏览器面试题及答案【集合目录】

前言&#xff1a; 欢迎浏览和关注本专栏《 前端就业宝典 》&#xff0c; 不管是扭螺丝还是造火箭&#xff0c; 多学点知识总没错。 这个专栏是扭螺丝之上要造火箭级别的知识&#xff0c;会给前端工作学习的小伙伴带来意想不到的帮助。 本专栏将前端知识拆整为零&#xff0c;主要…