1.安装所需依赖
npm install xlsx-js-style
2.创建公共js库 exportExcel.js
import XLSX from 'xlsx-js-style'
/**
* 递归获取所有表头配置(扁平化)
* @param {Array} columns 多级表头配置
* @param {Array} parentList 父级表头信息
* @returns {Array} 扁平化表头列表
*/
function getAllColumns(columns, parentList = []) {
let result = []
columns.forEach(col => {
const newParentList = [...parentList]
if (col.children && col.children.length) {
// 有子级,继续递归
result = result.concat(getAllColumns(col.children, [...newParentList, col]))
} else {
// 叶子节点,组装完整路径
result.push({
...col,
parentList: newParentList,
fullTitle: [...newParentList.map(p => p.title), col.title]
})
}
})
return result
}
/**
* 计算表头合并信息(rowspan/colspan)
* @param {Array} flatColumns 扁平化后的表头
* @returns {Object} { headerRowCount, merges, headerTitles }
*/
function calcHeaderInfo(flatColumns) {
const headerTitles = []
const merges = []
let maxLevel = 1
// 1. 计算最大层级(表头行数)
flatColumns.forEach(col => {
const level = col.parentList.length + 1
if (level > maxLevel) maxLevel = level
})
// 2. 初始化表头二维数组
for (let i = 0; i < maxLevel; i++) {
headerTitles.push([])
}
// 3. 填充表头数据,同时记录合并信息
flatColumns.forEach((col, colIndex) => {
const titlePath = col.fullTitle
titlePath.forEach((title, level) => {
headerTitles[level].push(title)
})
// 补充层级不足的空值(为了保持表头矩阵结构)
for (let i = titlePath.length; i < maxLevel; i++) {
headerTitles[i].push('')
}
// 4. 计算 rowspan(垂直合并)
const rowspan = maxLevel - col.parentList.length
if (rowspan > 1) {
merges.push({
s: { r: col.parentList.length, c: colIndex },
e: { r: col.parentList.length + rowspan - 1, c: colIndex }
})
}
})
// 5. 计算 colspan(水平合并)
const colSpanMap = new Map()
flatColumns.forEach((col, colIndex) => {
col.parentList.forEach((parent, level) => {
const key = `${level}-${parent.title}`
if (!colSpanMap.has(key)) {
colSpanMap.set(key, {
startCol: colIndex,
count: 1
})
} else {
const info = colSpanMap.get(key)
info.count++
}
})
})
// 处理水平合并
colSpanMap.forEach((info, key) => {
const [level] = key.split('-')
const levelNum = Number(level)
if (info.count > 1) {
merges.push({
s: { r: levelNum, c: info.startCol },
e: { r: levelNum, c: info.startCol + info.count - 1 }
})
}
})
return {
headerRowCount: maxLevel,
merges,
headerTitles
}
}
/**
* 处理表格数据,按表头顺序映射字段
* @param {Array} flatColumns 扁平化后的表头
* @param {Array} tableData 表格数据
* @returns {Array} 格式化后的二维数组
*/
function formatTableData(flatColumns, tableData) {
return tableData.map((row, rowIndex) => {
return flatColumns.map(col => {
// 支持 dataIndex 为字符串或数组(多级嵌套字段)
if (Array.isArray(col.dataIndex)) {
return col.dataIndex.reduce((acc, key) => acc?.[key], row) ?? ''
}
if(col.dataIndex === 'index'){
return rowIndex + 1
}
return row[col.dataIndex] ?? ''
})
})
}
/**
* 处理合计行数据
* @param {Array} flatColumns 扁平化后的表头
* @param {Array} tableData 表格数据
* @returns {Array} 合计行数据数组
*/
function handleSumRow(flatColumns, sumColumns, tableData) {
if (sumColumns.length === 0) return []
return flatColumns.map((col, colIndex) => {
if (sumColumns.includes(col.dataIndex)) {
//计算合计值
const sum = tableData.reduce((acc, row) => acc + Number(row[col.dataIndex] || 0), 0)
return sum.toFixed(2)
} else {
return colIndex === 0 ? '合计' : ''
}
})
}
/**
* 导出多级表头的 Excel 文件
* @param {Object} options 配置项
* @param {Array} options.columns Ant Design Vue 的 a-table 多级表头配置
* @param {Array} options.data 表格数据
* @param {string} [options.filename='导出数据.xlsx'] 导出文件名
* @param {string} [options.sheetName='Sheet1'] 工作表名称
* @param {Array} [options.sumColumns=[]] 要计算合计的列 dataIndex 列表 如['字段1', '字段2']
*/
export function exportMultiLevelTable(options) {
const {
columns,
data,
filename = '导出数据',
sheetName = 'Sheet1',
sumColumns = []
} = options
if (!columns || !columns.length) throw new Error('columns 不能为空')
if (!data || !Array.isArray(data)) throw new Error('data 必须为数组')
// 1. 扁平化多级表头
const flatColumns = getAllColumns(columns)
// 2. 计算表头合并信息和标题
const { merges, headerTitles } = calcHeaderInfo(flatColumns)
// 3. 格式化表格数据
const tableBody = formatTableData(flatColumns, data)
const sumRow = handleSumRow(flatColumns, sumColumns, data)
// 4. 构建完整表格
const fullTable = [...headerTitles, ...tableBody]
if (sumRow.length) fullTable.push(sumRow)
// 5. 带样式单元格(居中+边框)
const styledData = fullTable.map(row => {
return row.map(cell => ({
v: cell ?? '',
s: {
alignment: { horizontal: 'center', vertical: 'center' },
border: {
top: { style: 'thin', color: { rgb: '000000' } },
bottom: { style: 'thin', color: { rgb: '000000' } },
left: { style: 'thin', color: { rgb: '000000' } },
right: { style: 'thin', color: { rgb: '000000' } }
}
}
}))
})
// 6. 生成工作表
const worksheet = XLSX.utils.json_to_sheet([], { skipHeader: true })
XLSX.utils.sheet_add_aoa(worksheet, styledData)
// 7. 设置合并单元格
worksheet['!merges'] = merges
// 8. 自设置列宽
worksheet['!cols'] = flatColumns.map(col => ({
wch: Math.max(col.title.length * 2, 12)
}))
// 9. 导出 Excel 文件
const workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)
XLSX.writeFile(workbook, `${filename}.xlsx`)
}
3.引用导出
<template>
<div @click="fn_export "></div>
</template>
<script setup>
import {exportMultiLevelTable} from '@/utils/exportTable'
const fn_export = async() => {
//调用接口拿到数据
const tableData = [.......]
//列
const columns = [
{
title: '单价',
dataIndex: 'price',
},
{
title: '金额',
dataIndex: 'totalPrice',
},
{
title: '产出',
children: [
{
title: '数量1',
dataIndex: 'number1'
},
{
title: '数量2',
dataIndex: 'number2'
}
]
},
]
exportMultiLevelTable({
columns: columns,
data: tableData,
sumColumns: ['number1', 'number2'],
filename: "导出文件名",
})
btnLoad.value = false
}
</script>
<style scoped lang="less">
</style>
最后导出效果

2296

被折叠的 条评论
为什么被折叠?



