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>

三、效果

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

相关推荐
cronaldo911 分钟前
研发效能DevOps: Vite 使用 Element Plus
vue.js·vue·devops
编程百晓君2 小时前
一文解释清楚OpenHarmony面向全场景的分布式操作系统
vue.js
暴富的Tdy2 小时前
【CryptoJS库AES加密】
前端·javascript·vue.js
neeef_se2 小时前
Vue中使用a标签下载静态资源文件(比如excel、pdf等),纯前端操作
前端·vue.js·excel
z千鑫3 小时前
【前端】入门指南:Vue中使用Node.js进行数据库CRUD操作的详细步骤
前端·vue.js·node.js
生产队队长5 小时前
项目练习:element-ui的valid表单验证功能用法
前端·vue.js·ui
web137656076435 小时前
WebStorm 创建一个Vue项目
ide·vue.js·webstorm
秃头女孩y5 小时前
【React中最优雅的异步请求】
javascript·vue.js·react.js
小马哥编程8 小时前
原型链(Prototype Chain)入门
css·vue.js·chrome·node.js·原型模式·chrome devtools
娃哈哈哈哈呀11 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js