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>
 

 

posted @ 2024-11-11 11:05  WinnieIns  阅读(263)  评论(0编辑  收藏  举报