vue3+ts+elementui-plus二次封装树形表格实现不同层级展开收起的功能

一、TableTreeLevel组件 

<template>
      <div class='main'>
            <div class="btns">
                  <el-button type="primary" @click="expandLevel(1)">展开一级</el-button>
                  <el-button type="primary" @click="expandLevel(2)">展开二级</el-button>
                  <el-button type="primary" @click="expandLevel(3)">展开三级</el-button>
                  <el-button type="primary" @click="expandLevel(4)">展开四级</el-button>
                  <el-button type="warning" @click="putAwayLevel(0)">全部收起</el-button>
            </div>
            <div>
                  <el-table ref="multipleTableRef" :height="height" :default-expand-all="isExpend" :data="treeTableData"
                        style="width: 100%; margin-bottom: 20px" row-key="id" border>
                        <el-table-column :width="item.width" :fixed="item.fixed" show-overflow-tooltip align="center"
                              v-for="(item, i) in treeTableProps" :key="i" :label="item.label">
                              <template #default="scope">
                                    <!-- 自定义插槽展示 -->
                                    <slot v-if="item.slot" :name="item.prop" :scope="scope"></slot>
                                    <!-- 非自定义处理(判空) -->
                                    <span v-else-if="scope.row[item.prop] === '' || scope.row[item.prop] === null">--</span>
                                    <!-- 非自定义处理(正常展示) -->
                                    <span v-else>{{ scope.row[item.prop] }}</span>
                              </template>
                        </el-table-column>
                  </el-table>
            </div>
      </div>
</template>

<script lang="ts" setup>
import { ref, reactive, getCurrentInstance, onMounted, watch } from 'vue'
interface Props {
      // 属性名
      prop: string,
      // 属性标签
      label: string,
      // 是否固定(非必填)
      fixed?: boolean,
      // 行宽(非必填)
      width?: number,
      // 是否显示插槽(非必填)
      slot?: boolean,
}
const props = defineProps({
      /** 表格数据 */
      treeTableData: {
            type: Array,
            default: null,
            required: true
      },
      /** 表格属性 */
      treeTableProps: {
            type: Array<Props>,
            default: null,
            required: true
      },
      /** 是否默认全部展开 */
      isExpend: {
            type: Boolean,
            default: false,
            required: false
      },
      /** 表格高度 */
      height: {
            type: String,
            default: '60vh',
            required: false
      }
})
const multipleTableRef = ref() //获取table的ref
const expandNum = ref(0) //展开层级的数字

/** 监听展开的层级数,如果当前选择的层级小于上一次选择的层级,就收起 */
watch(expandNum, (newValue, oldValue) => {
      if (newValue < oldValue) {
            putAwayLevel(newValue + 1)
      }
}, { deep: true })
/** 收起 */
const putAwayLevel = (num: number) => {
      let arr = ref(treeToArray(props.treeTableData))//将树形数据转为一维数组,方便一层遍历
      // 遍历收起当前层级
      arr.value.map((row: any) => {
            if (num == row.level) {
                  multipleTableRef.value.toggleRowExpansion(row, false);
            }
      })
      // 下面递归调用目的是:假如你展开了4级,又点击展开2级,这时需要收起的是3级和4级,
      // 不然它只收起的2级,点开2级的时候,3级其实也是展开的.
      // 因此细节一点就是:展开2级,需要收起3、4级;展开1级,需要收起2、3、4级;展开3级,需要收起4级;
      if (++num < 4) {
            putAwayLevel(num)
      }
}
/** 展开 */
const expandLevel = (num: number) => {
      expandNum.value = num
      setExpandKeys(props.treeTableData, num)
}

/** 递归设置展开层级 */
const setExpandKeys = (dataList: any, level: number) => {
      // level为要展开的层级,先减一后使用,不然会多展开一级
      --level;
      // 当num大于0时,就对数组里面每一层依次进行展开
      if (level >= 0) {
            for (var i = 0; i < dataList.length; i++) {
                  // toggleRowExpansion 用于可扩展的表格或树表格, 第二个参数为true则为展开该行,false为折叠。
                  multipleTableRef.value.toggleRowExpansion(dataList[i], true);
                  if (dataList[i].children) {
                        setExpandKeys(dataList[i].children, level);
                  }
            }
      }
}
/** 将树形数据转为一维数组的函数*/
const treeToArray = (arr: any) => {
      let data = JSON.parse(JSON.stringify(arr))
      let newData = ref([] as any)
      const callback = (item: any) => {
            (item.children || (item.children = [])).map((v: any) => {
                  callback(v)
            })
            delete item.children
            newData.value.push(item)
      }
      data.map((v: any) => callback(v))
      return newData
}
onMounted(() => {
})

</script>
<style scoped lang='less'>
.btns {
      margin-bottom: 20px;
}

@media screen and (min-width: 200px) and (max-width: 1600px) {}

@media screen and (min-width: 1601px) {}
</style>

二、使用

<!----------------------------BaseTableTreeLevel的使用------------------------------->
		<BaseTableTreeLevel :treeTableData="tableData" height="50vh" :treeTableProps="treeTableProps">
		</BaseTableTreeLevel>
<script lang='ts' setup>
import TableTree from '@/components/BaseTableTree/index.vue'
import { ref, reactive, getCurrentInstance, onMounted } from 'vue'
// 定义表格数据接口
interface dataList {
	id: number
	date: string
	name: string
	address: string
	hasChildren?: boolean
	level: number,
	children?: dataList[]
}
// 定义表格头部属性名
const treeTableProps = [
	{ prop: 'date', label: '日期', width: 300, fixed: true, },
	{ prop: 'name', label: '名称', },
	{ prop: 'address', label: '地址', slot: true, },
]
// 定义表格假数据
const tableData: dataList[] = [
	{
		id: 1,
		date: '2016-05-04',
		name: '',
		address: 'No. 189, Grove St, Los Angeles',
		level: 1,
		children: [
			{
				id: 11,
				date: '2016-05-01',
				name: '小明',
				address: 'No. 189, Grove St, Los Angeles',
				level: 2,
				children: [
					{
						id: 111,
						date: '2016-05-01',
						name: '小明',
						address: 'No. 189, Grove St, Los Angeles',
						level: 3,
					},
					{
						id: 112,
						date: '2016-05-01',
						name: '小明',
						address: 'No. 189, Grove St, Los Angeles',
						level: 3,
					}
				]
			},
			{
				id: 12,
				date: '2016-05-01',
				name: '小明',
				address: 'No. 189, Grove St, Los Angeles',
				level: 2,
			},
		],
	},
	{
		id: 2,
		date: '2016-05-04',
		name: '小明',
		address: 'No. 189, Grove St, Los Angeles',
		level: 1,
		children: [
			{
				id: 21,
				date: '2016-05-01',
				name: '小明',
				address: 'No. 189, Grove St, Los Angeles',
				level: 2,
				children: [
					{
						id: 211,
						date: '2016-05-01',
						name: '小明',
						address: 'No. 189, Grove St, Los Angeles',
						level: 3,
					},
					{
						id: 212,
						date: '2016-05-01',
						name: '小明',
						address: 'No. 189, Grove St, Los Angeles',
						level: 3,
					}
				]
			},
			{
				id: 32,
				date: '2016-05-01',
				name: '小明',
				address: 'No. 189, Grove St, Los Angeles',
				level: 2,
			},
		],
	},
	{
		id: 3,
		date: '2016-05-01',
		name: '小明',
		level: 1,
		address: 'No. 189, Grove St, Los Angeles',
		children: [
			{
				id: 31,
				date: '2016-05-01',
				name: '小明',
				address: 'No. 189, Grove St, Los Angeles',
				level: 2,
				children: [
					{
						id: 311,
						date: '2016-05-01',
						name: '小明',
						address: 'No. 189, Grove St, Los Angeles',
						level: 3,
					},
					{
						id: 312,
						date: '2016-05-01',
						name: '小明',
						address: 'No. 189, Grove St, Los Angeles',
						level: 3,
						children: [
							{
								id: 3121,
								date: '2016-05-01',
								name: '小明',
								address: 'No. 189, Grove St, Los Angeles',
								level: 4,
							}, {
								id: 3122,
								date: '2016-05-01',
								name: '小明',
								address: 'No. 189, Grove St, Los Angeles',
								level: 4,
							},
						]
					}
				]
			},
			{
				id: 32,
				date: '2016-05-01',
				name: '小明',
				address: 'No. 189, Grove St, Los Angeles',
				level: 2,
			},
		],
	},
	{
		id: 4,
		date: '2016-05-03',
		name: '小明',
		address: 'No. 189, Grove St, Los Angeles',
		level: 1,
	},
]

onMounted(() => {
})
 
</script>

三、效果

我只定义了三层数据,就只演示展开了三层,还可以多层,自己设置即可 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JVM-提问纯享版

一、内存区域 介绍下 Java 内存区域&#xff08;运行时数据区&#xff09;内存分配方式内存分配并发问题对象的访问定位的两种方式&#xff08;句柄和直接指针两种方式&#xff09; 二、垃圾回收 如何判断对象是否死亡&#xff08;两种方法&#xff09;。简单的介绍一下强引…

Moshi Vs Gson Vs Kotlin Serialisation性能PK

Moshi Vs Gson Vs Kotlin Serialisation 定义 Gson Gson 是一个Java序列化/反序列化库&#xff0c;用于将Java对象转换为JSON格式&#xff0c;以及将JSON格式转换回Java对象。 Moshi Moshi 是一个现代化的JSON库&#xff0c;适用于Android和Java。它使得将JSON解析为Java对…

国内好用的企业级在线文档有哪些?

在当今数字化时代&#xff0c;企业级在线文档已经成为了现代办公环境中不可或缺的一部分。它不仅能够提高工作效率&#xff0c;还能够实现多人协同编辑&#xff0c;满足团队协作的需求。那么&#xff0c;在国内市场上&#xff0c;哪些企业级在线文档产品备受企业青睐呢&#xf…

25.1 Knife4j-Swagger的增强插件

1.Knife4j概述 Knife4j是一款基于Swagger UI的增强插件&#xff0c;它可以为Spring Boot项目生成美观且易于使用的API文档界面。它是Swagger UI的增强版&#xff0c;提供了更多的功能和定制选项&#xff0c;使API文档更加易读和易于理解。 2.Knife4j使用 Knife4j 集Swagger2…

嵌入式系统中的GPIO控制:从理论到实践与高级应用

本文将探讨嵌入式系统中的GPIO(通用输入输出)控制,着重介绍GPIO的原理和基本用法。我们将使用一个实际的示例项目来演示如何通过编程配置和控制GPIO引脚。将基于ARM Cortex-M微控制器,并使用C语言进行编写。 GPIO是嵌入式系统中最常见且功能最强大的接口之一。它允许硬件工…

LLM-Blender:大语言模型也可以进行集成学习

最近在看arxiv的时候发现了一个有意思的框架&#xff1a;LLM-Blender&#xff0c;它可以使用Ensemble 的方法来对大语言模型进行集成。 官方介绍如下&#xff1a;LLM-Blender是一个集成框架&#xff0c;可以通过利用多个开源大型语言模型(llm)的不同优势来获得始终如一的卓越性…

golang利用go mod巧妙替换使用本地项目的包

问题 拉了两个项目下来&#xff0c;其中一个项目依赖另一个项目&#xff0c;因为改动了被依赖的项目&#xff0c;想重新导入测试一下。 解决办法 go.mod文件的require中想要被代替的包名在replace中进行一个替换&#xff0c;注意&#xff1a;用来替换的需要用绝对路径&#xf…

机器视觉初步14:相机标定原理及应用

相机标定是指通过已知的相机参数&#xff0c;解算相机内部参数矩阵和外部参数矩阵。 文章目录 1.为什么要标定&#xff1f;2.工业场景中常见的标定方法2.1. 使用棋盘格标定板&#xff08;Checkerboard Markers&#xff09;2.2 使用相机自标定2.3. 使用三维物体标定2.4.九孔标…

【人工智能】神经网络、前向传播、反向传播、梯度下降、局部最小值、多层前馈网络、缓解过拟合的策略

神经网络、前向传播、反向传播 文章目录 神经网络、前向传播、反向传播前向传播反向传播梯度下降局部最小值多层前馈网络表示能力多层前馈网络局限缓解过拟合的策略前向传播是指将输入数据从输入层开始经过一系列的权重矩阵和激活函数的计算后,最终得到输出结果的过程。在前向…

【分布式】分布式唯一 ID 的 几种生成方案以及优缺点snowflake优化方案

在互联网的业务系统中&#xff0c;涉及到各种各样的ID&#xff0c;如在支付系统中就会有支付ID、退款ID等。那一般生成ID都有哪些解决方案呢&#xff1f;特别是在复杂的分布式系统业务场景中&#xff0c;我们应该采用哪种适合自己的解决方案是十分重要的。下面我们一一来列举一…

@monaco-editor/react组件CDN加载失败解决办法

monaco-editor/react引入这个cdn资源会load失败 网上很多例子都是这样写的&#xff0c;我这样写monaco会报错 import * as monaco from monaco-editor; import { loader } from monaco-editor/react;loader.config({ monaco });改成这样 import * as monaco from monaco-edi…

基于Centos 7虚拟机的磁盘操作(添加磁盘、分区、格式分区、挂载)

目录 一、添加硬盘 二、查看新磁盘 三、磁盘分区 3.1新建分区 3.2 格式分区 3.3 挂载分区 3.4 永久挂载新分区 3.5 取消挂载分区 一、添加硬盘 1.在虚拟机处选择编辑虚拟机设置&#xff0c;然后选择添加 2.选择硬盘&#xff0c;然后选择下一步 3.默认即可&#xff0c;下一步…

TCP连接管理与UDP协议

“三次握手”与“四次挥手” TCP建立连接的过程叫做握手 采用三报文握手&#xff1a;在客户和服务器之间交换三个TCP报文段&#xff0c;以防止已失效的连接请求报文段突然又传送到了&#xff0c;因而产生TCP连接建立错误。 第一次握手 连续释放——“四次挥手” TCP连续释放…

Zabbix监控之分布式部署

文章目录 Zabbix监控之分布式部署zabbix proxy概述部署zabbix-proxy节点规划基础环境准备安装proxy以及数据库配置数据库添加服务端host解析修改zabbix-proxy配置文件启动代理服务器 zabbix页面(1)在zabbix页面添加代理(2)zabbix-agent连接proxy Zabbix监控之分布式部署 zabbi…

使用Nacos将单体服务注册成微服务的步骤以及相关问题解决

目录 1.改造单体服务的配置文件。 2.添加Nacosw相关的pom依赖 3.在nacos的配置列表中创建配置列表 4.相关问题的解决 1.改造单体服务的配置文件。 &#x1f516;创建一个bootstrap.yml的配置文件该文件通常放置在src/main/resources目录中&#xff0c;并且优先于applicati…

ping命令

上图为IA ping B的过程&#xff0c;在此过程中&#xff1a; 包传送在x位置时&#xff0c;DestMacB左&#xff0c;SourceMacMac_A&#xff0c;Dest ipIp_B,Src_ipIp_A包传送在y位置时&#xff0c;DestMacB右&#xff0c;SourceMacB左 &#xff0c; Dest ipIp_B,Src_ipIp_A MAC…

多线程(Java系列6)

目录 前言&#xff1a; 1.什么是线程池 2.标准库中的线程池 3.实现线程池 结束语&#xff1a; 前言&#xff1a; 在上一节中小编带着大家了解了一下Java标准库中的定时器的使用方式并给大家实现了一下&#xff0c;那么这节中小编将分享一下多线程中的线程池。给大家讲解一…

三言两语说透process.stdout.write和console.log的区别

Node.js中的process.stdout.write和console.log都是用于向标准输出流(stdout)打印输出的方法&#xff0c;但二者在使用场景和实现方式上有些区别。本文将详细介绍process.stdout.write和console.log的区别。 process.stdout.write介绍 process.stdout.write是Node.js中的一个…

vue解决跨域访问问题(个人学习笔记六)

目录 友情提醒第一章、跨越问题解决1.1&#xff09;什么是跨域问题&#xff1f;1.2&#xff09;第一种解决方式&#xff1a;后端设置允许跨域访问1.3&#xff09;第二种解决方式&#xff1a;前端配置代理 第二章、配置代理服务器2.1&#xff09;配置简单代理服务器2.2&#xff…

Langchain 集成 Milvus

Langchain 集成 Milvus 1. 安装 Docker2. 部署 Milvus3.4. Langchain 集成 Milvus 1. 安装 Docker refer: https://docs.docker.com/engine/install/centos/ Milvus 会以容器方式启动&#xff0c;所以先安装 Docker。(本示例使用的是 Alma Linux 9.2) 卸载旧版本&#xff0c…