效果如图:
思路
- 参照官方文档,安装xlsx-0.20.3
- 简单表格根据数据导出,将对象数组整理成二维数组(Array of Arrays, aoa),调用
aoa_to_sheet创建工作表,将工作表加入新建的工作簿,导出xlsx文件 - 复杂表格根据DOM导出,获取
<table>元素(若使用组件库,可能需要自行拼接),调用table_to_book创建工作簿,导出xlsx文件
代码
Vue函数、生命周期钩子等已通过unplugin-auto-import自动导入,Element Plus已通过unplugin-vue-components自动导入
// src/utils/xlsx.js
import * as XLSX from 'xlsx'
export const elt2xlsx = (elt, filename = 'export') => {
const wb = XLSX.utils.table_to_book(elt)
XLSX.writeFile(wb, `${filename}.xlsx`, { bookType: 'xlsx', bookSST: true })
}
export const aoa2xlsx = (aoa, filename = 'export') => {
const ws = XLSX.utils.aoa_to_sheet(aoa)
const wb = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(wb, ws)
XLSX.writeFile(wb, `${filename}.xlsx`, { bookType: 'xlsx', bookSST: true })
}
以上为封装导出函数
<template>
<div>
<div style="margin-block: 8px">
<el-button @click="exportByAoa">导出(数据)</el-button>
</div>
<el-table :data="tableData">
<el-table-column prop="date" label="日期" width="150" />
<el-table-column prop="name" label="姓名" width="120" />
<el-table-column prop="address" label="地址" />
</el-table>
<div style="margin-block: 8px">
<el-button @click="exportByDom">导出(DOM)</el-button>
</div>
<el-table id="table" :data="tableData2">
<el-table-column v-for="col in tableCols" :key="col[0]" :label="col[0]">
<el-table-column
v-for="subCol in col[1]"
:key="subCol"
:prop="subCol"
:label="subCol.split('_')[1]"
/>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import { elt2xlsx, aoa2xlsx } from '@/utils/xlsx'
const tableData = Array.from({ length: 3 }, (_, i) => ({
date: `2024-08-1${i}`,
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
}))
const exportByAoa = () => {
const headMap = new Map([
['日期', 'date'],
['姓名', 'name'],
['地址', 'address']
])
const cols = Array.from(headMap.values())
const aoa = tableData.map((row) => cols.map((col) => row[col]))
aoa.unshift([...headMap.keys()])
aoa2xlsx(aoa, 'ExportByAoa')
}
const mockData = Array.from({ length: 3 }, () => ({
条目1: {
值1: Math.random() * 100,
值2: Math.random() * 100
},
条目2: {
值1: Math.random() * 100
},
条目3: {
值1: Math.random() * 100,
值2: Math.random() * 100
}
}))
const tableData2 = shallowRef([])
const tableCols = shallowRef([])
const renderTable = () => {
const colMap = new Map()
tableData2.value = mockData.map((di) => {
const row = {}
Object.entries(di).forEach(([key, subItems]) => {
const subCols = colMap.get(key) ?? []
Object.entries(subItems).forEach(([subKey, val]) => {
const newKey = `${key}_${subKey}` // 条目1.值1 -> '条目1_值1'
row[newKey] = val.toFixed(2)
if (subCols.includes(newKey)) return
subCols.push(newKey)
})
colMap.set(key, subCols)
})
return row
})
tableCols.value = Array.from(colMap) // ['条目1', ['条目1_值1', '条目1_值2']][]
}
const exportByDom = () => {
const tableEl = document.querySelector('#table')
const tHeadEl = tableEl.querySelector('.el-table__header').querySelector('thead').cloneNode(true)
const tBodyEl = tableEl.querySelector('.el-table__body').querySelector('tbody').cloneNode(true)
const elt = document.createElement('table')
elt.appendChild(tHeadEl)
elt.appendChild(tBodyEl)
elt2xlsx(elt, 'ExportByDom')
}
onBeforeMount(() => {
renderTable()
})
</script>
<style scoped lang="scss"></style>