一、工具概述
该工具基于 xlsx 库封装,提供 Vue3 + Element Plus 场景下 el-table 数据导出 Excel 的通用解决方案,支持「数组数据导出」(推荐)和「DOM 表格导出」(兼容分页/筛选场景)两种模式,具备参数可配置、错误处理、格式兼容等特性。
1.1 核心优势
- 通用化:适配任意
el-table表格,支持自定义表头、文件名、工作表名等; - 易用性:提供默认配置,减少重复代码,支持自动解析表格表头;
- 健壮性:内置参数校验、错误提示,兼容数字格式(如手机号防科学计数法);
- 无强依赖:原生实现文件下载,无需额外依赖
file-saver。
1.2 依赖安装
# 核心依赖
npm install xlsx --save
# (可选)若使用Element Plus的消息提示,需确保已安装
npm install element-plus --save
二、工具文件结构
src/
└── utils/
└── exportExcel.js # 核心封装文件
三、核心 API 文档
3.1 导出数组数据(推荐)exportExcel
功能说明
直接导出前端维护的数组数据(不受分页/筛选影响),通过表头映射控制导出字段和显示名。
函数签名
export const exportExcel = async (options) => {}
参数配置(options)
| 参数名 | 类型 | 是否必传 | 默认值 | 说明 |
|---|---|---|---|---|
| data | Array | 是 | - | 源数据数组(如接口返回的完整列表、el-table 的 data 数据) |
| headerMap | Object | 是 | - | 表头映射对象,格式:{ 字段名: 显示名 },控制导出字段和表头显示 |
| sheetName | String | 否 | ‘数据表’ | Excel 工作表名称 |
| fileName | String | 否 | ‘导出数据’ | 导出文件名称(自动拼接时间戳,可通过 withTimestamp 关闭) |
| withTimestamp | Boolean | 否 | true | 文件名是否添加时间戳(避免重复) |
返回值
Promise<void>,无返回值,内部自动触发文件下载和消息提示。
示例
import { exportExcel } from '@/utils/exportExcel'
// 模拟表格数据
const userList = [
{ name: '张三', age: 20, phone: 13800138000, email: 'zhangsan@test.com' },
{ name: '李四', age: 22, phone: 13900139000, email: 'lisi@test.com' }
]
// 导出用户列表
exportExcel({
data: userList,
headerMap: {
name: '姓名', // 字段名: Excel显示名
age: '年龄',
phone: '手机号' // 仅导出这3个字段,email会被过滤
},
fileName: '用户列表',
sheetName: '2025年用户数据',
withTimestamp: false // 文件名不添加时间戳
})
3.2 导出DOM表格数据 exportTableDom
功能说明
导出 el-table 当前DOM显示的数据(含分页/筛选后的内容),适用于后端分页、前端仅显示部分数据的场景。
函数签名
export const exportTableDom = async (options) => {}
参数配置(options)
| 参数名 | 类型 | 是否必传 | 默认值 | 说明 |
|---|---|---|---|---|
| ele | String/HTMLElement | 是 | - | 表格DOM选择器(如 #export-table)或表格DOM元素(如 tableRef.value.$el) |
| sheetName | String | 否 | ‘当前页数据’ | Excel 工作表名称 |
| fileName | String | 否 | ‘当前页数据’ | 导出文件名称 |
| withTimestamp | Boolean | 否 | true | 文件名是否添加时间戳 |
返回值
Promise<void>,无返回值,内部自动触发文件下载和消息提示。
示例
import { exportTableDom } from '@/utils/exportExcel'
import { ref } from 'vue'
const orderTableRef = ref(null) // 绑定到el-table的ref
// 方式1:通过选择器导出
exportTableDom({
ele: '#order-table', // el-table需设置id="order-table"
fileName: '2025-12订单列表',
sheetName: '12月订单'
})
// 方式2:通过ref导出
exportTableDom({
ele: orderTableRef.value.$el,
fileName: '订单数据'
})
3.3 自动解析表格表头 getTableHeaderMap
功能说明
自动解析 el-table 的列配置,生成 headerMap(无需手动编写表头映射),适配不想手动定义 headerMap 的场景。
函数签名
export const getTableHeaderMap = (tableRef) => {}
参数说明
| 参数名 | 类型 | 是否必传 | 说明 |
|---|---|---|---|
| tableRef | Object | 是 | el-table 的 ref 引用对象 |
返回值
| 类型 | 说明 |
|---|---|
| Object | 解析后的 headerMap,格式:{ prop: label } |
示例
import { exportExcel, getTableHeaderMap } from '@/utils/exportExcel'
import { ref } from 'vue'
const userTableRef = ref(null) // 绑定到el-table的ref
// 自动解析表头并导出
const exportAutoHeader = () => {
const headerMap = getTableHeaderMap(userTableRef.value)
exportExcel({
data: userList.value,
headerMap, // 自动解析的表头映射
fileName: '用户列表'
})
}
四、完整工具代码(exportExcel.js)
import XLSX from 'xlsx'
import { ElMessage } from 'element-plus'
/**
* 通用Excel导出方法(支持数组数据导出)
* @param {Object} options 导出配置
* @param {Array} options.data 源数据数组(必传)
* @param {Object} options.headerMap 表头映射({ 字段名: 显示名 },必传)
* @param {String} [options.sheetName='数据表'] Excel工作表名称
* @param {String} [options.fileName='导出数据'] 导出文件名(自动加时间戳)
* @param {Boolean} [options.withTimestamp=true] 文件名是否带时间戳
* @returns {Promise<void>}
*/
export const exportExcel = async (options) => {
const {
data,
headerMap,
sheetName = '数据表',
fileName = '导出数据',
withTimestamp = true
} = options
// 校验必传参数
if (!data || !Array.isArray(data)) {
ElMessage.error('导出失败:源数据必须为数组!')
return
}
if (!headerMap || typeof headerMap !== 'object' || Object.keys(headerMap).length === 0) {
ElMessage.error('导出失败:表头映射不能为空!')
return
}
try {
// 处理数据:按headerMap过滤并映射表头,处理空值和数字格式
const exportData = data.map(item => {
const row = {}
Object.entries(headerMap).forEach(([key, label]) => {
let value = item[key] ?? ''
// 特殊处理:手机号/身份证等长数字转文本(避免科学计数法)
if (['phone', 'idCard', 'mobile', 'tel'].includes(key) && typeof value === 'number') {
value = `\t${value}` // 加制表符强制文本格式
}
row[label] = value
})
return row
})
// 创建工作簿和工作表
const ws = XLSX.utils.json_to_sheet(exportData)
const wb = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(wb, ws, sheetName)
// 生成文件并下载(原生实现)
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' })
const blob = new Blob([wbout], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
// 处理文件名
const timestamp = withTimestamp ? `_${new Date().getTime()}` : ''
a.download = `${fileName}${timestamp}.xlsx`
a.href = url
a.click()
URL.revokeObjectURL(url) // 释放URL对象
ElMessage.success(`《${fileName}》导出成功!`)
} catch (error) {
console.error('Excel导出失败:', error)
ElMessage.error('导出失败,请检查数据格式或重试!')
}
}
/**
* 通用DOM表格导出方法(导出当前页面显示的表格)
* @param {Object} options 配置
* @param {String|HTMLElement} options.ele 表格选择器/DOM元素(必传)
* @param {String} [options.sheetName='当前页数据'] 工作表名称
* @param {String} [options.fileName='当前页数据'] 文件名
* @param {Boolean} [options.withTimestamp=true] 文件名是否带时间戳
* @returns {Promise<void>}
*/
export const exportTableDom = async (options) => {
const {
ele,
sheetName = '当前页数据',
fileName = '当前页数据',
withTimestamp = true
} = options
// 获取表格DOM
let table = null
if (typeof ele === 'string') {
table = document.querySelector(ele)
} else if (ele instanceof HTMLElement) {
table = ele
}
if (!table) {
ElMessage.error('导出失败:未找到表格DOM!')
return
}
try {
// DOM转工作表(raw: true 保留原始格式)
const ws = XLSX.utils.table_to_sheet(table, { raw: true })
const wb = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(wb, ws, sheetName)
// 下载文件
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' })
const blob = new Blob([wbout], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
const timestamp = withTimestamp ? `_${new Date().getTime()}` : ''
a.download = `${fileName}${timestamp}.xlsx`
a.href = url
a.click()
URL.revokeObjectURL(url)
ElMessage.success(`《${fileName}》导出成功!`)
} catch (error) {
console.error('DOM表格导出失败:', error)
ElMessage.error('导出失败,请重试!')
}
}
/**
* 解析el-table的表头,自动生成headerMap
* @param {Object} tableRef el-table的ref引用对象
* @returns {Object} 表头映射 { prop: label }
*/
export const getTableHeaderMap = (tableRef) => {
if (!tableRef || !tableRef.columns) {
ElMessage.warning('解析表头失败:表格ref无效!')
return {}
}
const headerMap = {}
// 遍历表格列配置,过滤空prop/label
tableRef.columns.forEach(col => {
if (col.prop && col.label && !col.hidden) { // 排除隐藏列
headerMap[col.prop] = col.label
}
})
return headerMap
}
五、组件中使用示例
<template>
<div class="excel-export-demo">
<!-- 示例1:数组数据导出 -->
<el-table
ref="userTableRef"
:data="userList"
border
style="width: 100%; margin-bottom: 20px"
>
<el-table-column prop="name" label="姓名" />
<el-table-column prop="age" label="年龄" />
<el-table-column prop="phone" label="手机号" />
<el-table-column prop="email" label="邮箱" />
</el-table>
<el-button type="primary" @click="exportUserExcel">导出用户列表(手动headerMap)</el-button>
<el-button type="primary" @click="exportUserExcelAuto">导出用户列表(自动解析表头)</el-button>
<!-- 示例2:DOM表格导出(分页场景) -->
<el-table
:data="orderList"
border
id="order-table"
style="width: 100%; margin-bottom: 20px"
>
<el-table-column prop="orderNo" label="订单号" />
<el-table-column prop="amount" label="金额(元)" />
<el-table-column prop="createTime" label="创建时间" />
</el-table>
<el-button type="primary" @click="exportOrderDom">导出当前订单页</el-button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { exportExcel, exportTableDom, getTableHeaderMap } from '@/utils/exportExcel'
// 模拟用户数据
const userTableRef = ref(null)
const userList = ref([
{ name: '张三', age: 20, phone: 13800138000, email: 'zhangsan@test.com' },
{ name: '李四', age: 22, phone: 13900139000, email: 'lisi@test.com' },
{ name: '王五', age: 25, phone: 13700137000, email: 'wangwu@test.com' }
])
// 模拟订单数据(分页)
const orderList = ref([
{ orderNo: 'OD20251209001', amount: 199, createTime: '2025-12-09 10:00' },
{ orderNo: 'OD20251209002', amount: 299, createTime: '2025-12-09 11:00' }
])
// 1. 手动配置headerMap导出
const exportUserExcel = () => {
exportExcel({
data: userList.value,
headerMap: {
name: '姓名',
age: '年龄',
phone: '手机号' // 仅导出这3个字段
},
fileName: '用户列表',
sheetName: '2025用户数据',
withTimestamp: true
})
}
// 2. 自动解析表头导出
const exportUserExcelAuto = () => {
const headerMap = getTableHeaderMap(userTableRef.value)
exportExcel({
data: userList.value,
headerMap, // 自动解析所有列的prop和label
fileName: '用户列表_自动解析',
sheetName: '用户数据'
})
}
// 3. 导出DOM表格(当前页)
const exportOrderDom = () => {
exportTableDom({
ele: '#order-table',
fileName: '12月订单列表',
sheetName: '2025-12-09订单'
})
}
</script>
六、常见问题与解决方案
| 问题现象 | 原因分析 | 解决方案 |
|---|---|---|
| 手机号/身份证显示为科学计数法 | 数字过长被Excel自动格式化 | 工具已内置处理:给指定字段加 \t 强制文本格式;或手动将值转为字符串 |
| 导出后Excel内容为空 | 源数据为空或headerMap配置错误 | 检查 data 是否为非空数组;检查 headerMap 的字段名是否与数据匹配 |
| DOM导出找不到表格 | 选择器错误或DOM未挂载 | 确认选择器正确;或通过 ref 传递DOM元素(tableRef.value.$el) |
| 导出文件乱码 | Blob类型配置错误 | 工具已默认配置正确的 Blob 类型(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet) |
| 合并单元格导出异常 | xlsx库对DOM合并单元格支持有限 | 优先使用「数组数据导出」模式,避免依赖DOM解析 |
七、扩展说明
- 支持自定义格式:可在
exportExcel方法的exportData处理阶段,扩展日期、金额等字段的格式化逻辑; - 大文件优化:若导出数据量超1万条,可改用
XLSX.writeFile替代Blob下载,避免内存溢出; - 多工作表导出:可扩展
exportExcel方法,支持传入多个data + headerMap,生成多工作表的Excel文件; - 样式定制:
xlsx库基础版本不支持Excel样式,若需定制样式,可升级为xlsx-style或exceljs库。

4428

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



