在 Vue3 + Element Plus(Element-UI 的 Vue3 版本)中实现**动态多级表头表格的导出功能**,需要结合 `el-table` 的多级表头能力与前端 Excel 导出库(如 [SheetJS / xlsx](https://github.com/SheetJS/sheetjs))来完成。由于 Element Plus 表格支持嵌套 `el-table-column` 实现多级表头,但原生不支持导出,因此需手动解析表头结构并生成对应的 Excel 文件。
下面是一个完整的解决方案:
---
### ✅ 功能目标:
1. 支持 **动态配置的多级表头**(任意层级)
2. 使用 `Element Plus` 的 `<el-table>` 展示数据
3. 点击按钮将表格(含多级表头)导出为 `.xlsx` 文件
---
### ✅ 所需依赖
```bash
npm install xlsx
```
---
### ✅ 示例代码(Vue3 + Composition API + Element Plus)
```vue
<template>
<div>
<el-button @click="exportToExcel">导出为 Excel</el-button>
<el-table
:data="tableData"
border
style="width: 100%; margin-top: 20px"
ref="tableRef"
>
<!-- 动态渲染多级表头 -->
<template v-for="col in tableColumns" :key="col.prop">
<el-table-column
v-if="!col.children"
:prop="col.prop"
:label="col.label"
:width="col.width"
/>
<el-table-column
v-else
:label="col.label"
:width="col.width"
>
<template v-for="child in col.children" :key="child.prop">
<el-table-column
:prop="child.prop"
:label="child.label"
:width="child.width"
/>
</template>
</el-table-column>
</template>
</el-table>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import * as XLSX from 'xlsx'
// 模拟数据
const tableData = ref([
{ name: '张三', info_age: 25, info_city: '北京', score_chinese: 88, score_math: 92 },
{ name: '李四', info_age: 27, info_city: '上海', score_chinese: 76, score_math: 98 },
{ name: '王五', info_age: 24, info_city: '深圳', score_chinese: 90, score_math: 85 }
])
// 多级表头定义(动态可变)
const tableColumns = reactive([
{ prop: 'name', label: '姓名', width: 100 },
{
label: '个人信息',
children: [
{ prop: 'info_age', label: '年龄', width: 80 },
{ prop: 'info_city', label: '城市', width: 100 }
]
},
{
label: '成绩信息',
children: [
{ prop: 'score_chinese', label: '语文', width: 80 },
{ prop: 'score_math', label: '数学', width: 80 }
]
}
])
// 将多级表头扁平化,并记录每列的完整路径用于映射数据
function flattenHeaders(columns, parentPath = '', parentLabel = '') {
let result = []
columns.forEach(col => {
if (col.children && col.children.length > 0) {
const groupLabel = parentLabel ? `${parentLabel} - ${col.label}` : col.label
col.children.forEach(child => {
result.push({
prop: child.prop,
header: [groupLabel, child.label], // 用于生成两层表头
valuePath: `${parentPath}${child.prop}`
})
})
} else {
result.push({
prop: col.prop,
header: [col.label],
valuePath: col.prop
})
}
})
return result
}
// 导出为 Excel
const exportToExcel = () => {
const flatHeaders = flattenHeaders(tableColumns)
// 构建表头行(二维数组,支持多级)
const headerRows = [[], []] // 第一行:主分组;第二行:具体字段名
const data = []
flatHeaders.forEach(item => {
// 填充第一行(分组名)
if (item.header.length === 2) {
headerRows[0].push(item.header[0])
headerRows[1].push(item.header[1])
} else {
headerRows[0].push(item.header[0])
headerRows[1].push('') // 第二行为空
}
})
// 合并单元格逻辑(仅对第一行分组合并)
const merges = []
let lastGroup = null
let groupStartCol = 0
headerRows[0].forEach((groupLabel, index) => {
if (groupLabel !== lastGroup) {
if (lastGroup !== null) {
if (index - groupStartCol > 1) {
merges.push({
s: { r: 0, c: groupStartCol }, // start
e: { r: 0, c: index - 1 } // end
})
}
}
lastGroup = groupLabel
groupStartCol = index
}
})
// 添加最后一个组的合并
if (headerRows[0].length - groupStartCol > 1 && lastGroup) {
merges.push({
s: { r: 0, c: groupStartCol },
e: { r: 0, c: headerRows[0].length - 1 }
})
}
// 填充数据行
tableData.value.forEach(row => {
const rowData = []
flatHeaders.forEach(item => {
rowData.push(row[item.prop] || '')
})
data.push(rowData)
})
// 使用 SheetJS 创建工作簿和工作表
const worksheet = XLSX.utils.aoa_to_sheet([])
// 写入第一行(分组)
XLSX.utils.sheet_add_aoa(worksheet, [headerRows[0]], { origin: 'A1' })
// 写入第二行(字段)
XLSX.utils.sheet_add_aoa(worksheet, [headerRows[1]], { origin: 'A2' })
// 写入数据行(从第3行开始)
XLSX.utils.sheet_add_aoa(worksheet, data, { origin: 'A3' })
// 设置列宽(可选)
const colWidths = flatHeaders.map(item => ({ wch: 12 }))
worksheet['!cols'] = colWidths
// 设置合并单元格
worksheet['!merges'] = merges
// 创建工作簿并导出
const workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
// 触发下载
XLSX.writeFile(workbook, '动态多级表头导出.xlsx')
}
</script>
<style scoped>
.el-table {
margin-top: 20px;
}
</style>
```
---
### 🔍 代码解释
| 部分 | 说明 |
|------|------|
| `tableColumns` | 定义了支持嵌套 `children` 的多级结构,是动态可配置的核心 |
| `flattenHeaders()` | 将嵌套列结构展平,提取每个叶子节点的路径、标签和数据属性 |
| `headerRows` | 构造两行表头:第一行为“个人信息”、“成绩信息”等大类,第二行为具体字段 |
| `merges` | 记录需要合并的单元格范围(第一行中相同分组的连续列) |
| `XLSX.utils.aoa_to_sheet` | 使用数组 of 数组方式构建表格 |
| `sheet_add_aoa` | 分别写入不同行的数据 |
| `worksheet['!merges']` | 设置 Excel 中的合并单元格 |
---
### ✅ 输出效果(Excel)
| 个人信息 | 成绩信息 |
|----------------|--------------|
| 年龄 | 城市 | 语文 | 数学 |
| 25 | 北京 | 88 | 92 |
| 27 | 上海 | 76 | 98 |
其中“个人信息”跨两列合并,“成绩信息”也跨两列。
---
### ✅ 注意事项
- 如果表头超过两层(比如三级),需要递归处理并生成更多行。
- 当前方案适用于中小型数据量导出(千行以内)。大数据建议使用流式导出或后端服务。
- 可扩展支持样式、冻结窗格、数字格式等高级特性。
---
### ✅ 相关优化方向
- 抽离成通用组件 `DynamicTableExporter`
- 支持自定义导出字段过滤
- 支持时间格式、金额格式自动识别
- 添加 loading 和错误提示
---