vue3+element plus +js 实现树形和末级展开是表格
1、实现一个树形和末级展开是表格,需要支持大数据量,因此使用Virtualized Table 虚拟化表格 el-table-v2
2、效果图
3、代码
<template>
<el-table-v2
:header-height="0"
v-model:expanded-row-keys="expandedRowKeys"
:columns="columns"
:data="treeData"
:width="1000"
:expand-column-key="expandColumnKey"
:height="800"
:estimated-row-height="200"
@row-expand="onRowExpanded"
@expanded-rows-change="onExpandedRowsChange">
<template #row="props">
<Row v-bind="props"/>
<div class="p-6" v-if="!props.rowData.children.length">
<el-table :data="props.rowData.tableData" :height="40*props.rowData.tableData.length+40" style="width: 100%" border header-cell-class-name="table-header">
<el-table-column prop="name" label="文件名称" />
<el-table-column prop="size" label="文件大小" />
<el-table-column prop="date" label="上传日期" />
<el-table-column align="center" label="操作" width="80" >
<template #default="scope">
<el-icon @click="handleDownload(scope.$index, scope.row)"><Download /></el-icon>
</template>
</el-table-column>
</el-table>
</div>
<div style="height: 50px;line-height: 50px;margin-left: 5px;">
<el-button v-if="props.rowData.children.length" :icon="Download" type="primary" plain
@click="handleDownload(null, props.rowData)"
>下载目录</el-button>
</div>
</template>
</el-table-v2>
</template>
<script setup>
import { computed, ref } from 'vue'
import { Download } from '@element-plus/icons-vue'
const expandColumnKey = 'column-0'
const generateColumns = (length = 10, prefix = 'column-', props) =>
Array.from({ length }).map((_, columnIndex) => ({
...props,
key: `${prefix}${columnIndex}`,
dataKey: `${prefix}${columnIndex}`,
title: `Column ${columnIndex}`
}))
const generateData = (
columns,
length = 200,
prefix = 'row-'
) =>
Array.from({ length }).map((_, rowIndex) => {
return columns.reduce(
(rowData, column, columnIndex) => {
rowData[column.dataKey] = `文件文件文件文件文件${rowIndex}`
return rowData
},
{
id: `${prefix}${rowIndex}`,
parentId: null,
}
)
})
const columns = generateColumns(1).map((column, columnIndex) => {
let fixed
return { ...column, fixed }
})
const data = generateData(columns, 200)
for (let i = 0; i < 200; i++) {
data.push(
{
...data[i],
id: `${data[i].id}-sub-${i}`,
parentId: data[i].id,
[expandColumnKey]: `202${i}`,
},{
...data[i],
id: `${data[i].id}-sub-sub-${i}`,
parentId: `${data[i].id}-sub-${i}`,
[expandColumnKey]: `Sub-202${i}`,
}
)
}
function unflatten(
data,
rootId = null,
dataKey = 'id',
parentKey = 'parentId'
) {
const tree = []
const childrenMap = {}
for (const datum of data) {
const item = { ...datum }
const id = item[dataKey]
const parentId = item[parentKey]
if (Array.isArray(item.children)) {
childrenMap[id] = item.children.concat(childrenMap[id] || [])
} else if (!childrenMap[id]) {
childrenMap[id] = []
}
item.children = childrenMap[id]
if(!item.children.length){
item.tableData = [
{
date: '2016-05-03',
name: 'Tom',
size: '18kb',
},
{
date: '2016-05-02',
name: 'Tom',
size: '18kb',
},
{
date: '2016-05-02',
name: 'Tom',
size: '18kb',
},
{
date: '2016-05-02',
name: 'Tom',
size: '18kb',
},
{
date: '2016-05-02',
name: 'Tom',
size: '18kb',
}
]
}
if (parentId !== undefined && parentId !== rootId) {
if (!childrenMap[parentId]) childrenMap[parentId] = []
childrenMap[parentId].push(item)
} else {
tree.push(item)
}
}
return tree
}
const treeData = computed(() => unflatten(data))
const expandedRowKeys = ref([])
const curParent = ref(null)
const onRowExpanded = ( prop ) => {
//改变根节点自动收缩其他节点
// if(curParent.value!=prop.rowData.id&&!prop.rowData.parentId){
// expandedRowKeys.value=[prop.rowData.id]
// curParent.value = prop.rowData.id
// }
}
const onExpandedRowsChange = (
expandedKeys
) => {
console.log(expandedKeys)
}
const Row = ({
cells,
rowData
}) => {
if (rowData.children.length) {
return cells;
}
}
const handleDownload = (index, row) => {
console.log(index, row)
}
</script>
<style>
.el-table-v2{
--el-table-border-color:none;
}
.el-table-v2__row-depth-0,
.el-table-v2__row-depth-1,
.el-table-v2__row-depth-2,
.el-table-v2__row-depth-3,
.el-table-v2__row-depth-4,
.el-table-v2__row-depth-5,
.el-table-v2__row-depth-6 {
min-height: 50px;
}
.el-table-v2__row-cell{
width: fit-content !important;
}
.el-table-v2__cell-text{
height: 32px;
min-width: 100px;
line-height: 32px;;
background-color: #F7F8FC;
border: var(--el-border);
border-color: #dcdfe6;
padding: 0 15px;
font-size: var(--el-font-size-base);
border-radius: var(--el-border-radius-base);
margin-top: 1px;
}
.el-table-v2__cell-text:hover {
color: #409eff;
border-color: #409eff;
background-color: #ecf5ff;
}
.el-table-v2__cell-text::before {
content: '';
/* position: absolute; */
display: inline-block;
width: 16px;
height: 16px;
margin-right: 5px;
vertical-align: text-top;
background-image: url('data:image/svg+xml;utf8,<svg data-v-d2e47025="" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="grey" d="M878.08 448H241.92l-96 384h636.16l96-384zM832 384v-64H485.76L357.504 192H128v448l57.92-231.744A32 32 0 0 1 216.96 384zm-24.96 512H96a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h287.872l128.384 128H864a32 32 0 0 1 32 32v96h23.04a32 32 0 0 1 31.04 39.744l-112 448A32 32 0 0 1 807.04 896"></path></svg>'); /* 替换为你的 SVG Data URI */
background-size: cover; /* 确保 SVG 图像覆盖整个伪元素区域 */
}
.el-table-v2__cell-text:hover::before {
background-image: url('data:image/svg+xml;utf8,<svg data-v-d2e47025="" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="CornflowerBlue" d="M878.08 448H241.92l-96 384h636.16l96-384zM832 384v-64H485.76L357.504 192H128v448l57.92-231.744A32 32 0 0 1 216.96 384zm-24.96 512H96a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h287.872l128.384 128H864a32 32 0 0 1 32 32v96h23.04a32 32 0 0 1 31.04 39.744l-112 448A32 32 0 0 1 807.04 896"></path></svg>'); /* 替换为你的 SVG Data URI */
}
.p-6 {
margin:0 20px 0 36px;
width: 98%;
}
.el-button:focus, .el-button:hover {
color: var(--el-button-text-color);
border-color: var(--el-button-hover-border-color);
background-color: #ecf5ff;
outline: 0;
}
.table-header{
background-color: #F8F9FB !important;
}
</style>