一、工具简介
本工具基于 xlsx 库进行封装,专为 Vue3 与 Element Plus 技术栈设计,提供高效便捷的 Excel 数据导出功能。支持两种主要模式:「数组数据导出」(推荐使用)和「DOM 表格导出」(适用于存在分页或筛选逻辑的场景)。具备高度可配置性、良好的错误处理机制以及格式兼容能力。
xlsx
1.1 主要特性
- 通用性强:可适配任意结构的表格组件,支持自定义表头内容、文件名称及工作表名称;
- 操作简便:内置默认配置项,减少重复编码工作,支持自动提取表格列标题;
- 稳定可靠:包含参数合法性校验与异常提示机制,有效避免数字格式被转为科学计数法(如手机号码等敏感字段);
- 无额外依赖:采用原生方式实现文件下载流程,仅需引入核心库即可运行,不强制依赖其他插件。
el-table
file-saver
二、依赖安装
项目中使用该工具前,需先安装必要依赖包:
# 核心库
npm install xlsx --save
# (可选)若需使用 Element Plus 的消息提示功能,请确保已安装
npm install element-plus --save
三、文件目录结构
工具整体结构清晰,便于集成与维护:
src/
└── utils/
└── exportExcel.js # 核心封装文件
四、核心 API 使用说明
4.1 数组数据导出(推荐方式)
适用于导出前端完整持有的数据列表(不受当前页面显示范围限制),通过字段映射控制输出内容与表头显示名称。
exportExcel
函数定义
export const exportExcel = async (options) => {}
参数说明(options 对象)
| 参数名 | 类型 | 是否必传 | 默认值 | 说明 |
|---|---|---|---|---|
| data | Array | 是 | - | 原始数据数组,例如接口返回的完整用户列表或订单集合 |
| headerMap | Object | 是 | - | 字段映射关系对象,格式为 { key: '显示名' },用于指定导出字段及其在 Excel 中的列标题 |
| sheetName | String | 否 | '数据表' | 生成的工作表名称 |
| fileName | String | 否 | '导出数据' | 导出文件的基础名称,系统将自动附加时间戳以区分版本 |
| withTimestamp | Boolean | 否 | true | 是否在文件名后添加时间戳,设置为 false 可关闭此功能 |
el-table
data
{ 字段名: 显示名 }
withTimestamp
返回值
无实际返回值。调用后会自动触发浏览器的文件下载行为,并根据执行结果弹出相应提示信息。
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: '姓名',
age: '年龄',
phone: '手机号' // 仅导出这三个字段,email 将被忽略
},
fileName: '用户列表',
sheetName: '2025年用户数据',
withTimestamp: false // 禁用时间戳
})
4.2 DOM 表格内容导出
用于导出当前页面上已渲染的表格 DOM 内容,特别适合后端分页、前端仅展示部分记录的业务场景。
exportTableDom
函数定义
export const exportTableDom = async (options) => {}
参数说明(options 对象)
| 参数名 | 类型 | 是否必传 | 默认值 | 说明 |
|---|---|---|---|---|
| ele | String / HTMLElement | 是 | - | 目标表格的选择器字符串(如 #table-id)或直接传入 DOM 元素实例 |
| sheetName | String | 否 | '当前页数据' | Excel 工作表名称 |
| fileName | String | 否 | '当前页数据' | 导出文件名称 |
| withTimestamp | Boolean | 否 | true | 是否在文件名中加入时间戳 |
#export-table
tableRef.value.$el
返回值
无返回值。内部自动完成数据抓取、转换和文件下载流程。
Promise<void>
使用示例
import { exportTableDom } from '@/utils/exportExcel'
import { ref } from 'vue'
const orderTableRef = ref(null)
// 方法一:通过 CSS 选择器定位表格
exportTableDom({
ele: '#order-table', // 需确保 el-table 设置了 id="order-table"
fileName: '2025-12订单列表',
sheetName: '12月订单'
})
// 方法二:通过 ref 引用获取 DOM 节点
exportTableDom({
ele: orderTableRef.value.$el,
fileName: '订单数据'
})
4.3 自动解析表格列头
支持从 el-table 组件的列配置中自动提取表头信息,动态生成字段映射关系,无需手动编写 headerMap,适用于希望简化配置流程的开发者。
getTableHeaderMap
el-table
headerMap函数签名
export const getTableHeaderMap = (tableRef) => {}
参数说明
| 参数名 | 类型 | 是否必传 | 说明 |
|---|---|---|---|
| tableRef | Object | 是 | el-table 的 ref 引用对象 |
返回值
| 类型 | 说明 |
|---|---|
| Object |
解析后的 headerMap,格式如下:
|
示例代码
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='数据表'] - 工作表名称
* @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)
}/**
* 通用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
// 获取目标表格元素
let table = null
if (typeof ele === 'string') {
table = document.querySelector(ele)
} else if (ele instanceof HTMLElement) {
table = ele
}
// 若未找到对应DOM,则提示错误并终止操作
if (!table) {
ElMessage.error('导出失败:未找到表格DOM!')
return
}
try {
// 将DOM表格转换为工作表对象,保留原始格式
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对象
URL.revokeObjectURL(url)
ElMessage.success(`《${fileName}》导出成功!`)
} catch (error) {
console.error('DOM表格导出失败:', error)
ElMessage.error('导出失败,请重试!')
}
}
/**
* Excel数据导出核心方法
* 将传入的数据和配置转化为Excel文件并触发浏览器下载
* @param {Array<Object>} data 要导出的数据列表
* @param {Array<Object>} columns 列配置,需包含prop和label字段
* @param {String} [fileName='导出数据'] 文件名称
* @param {String} [sheetName='数据表'] 工作表名称
* @param {Boolean} [withTimestamp=true] 文件名是否附加时间戳
*/
export const exportExcel = async (data, columns, fileName = '导出数据', sheetName = '数据表', withTimestamp = true) => {
try {
// 构建表头映射:字段名 → 显示标题
const headerMap = {}
columns.forEach(col => {
if (col.prop && col.label) {
headerMap[col.prop] = col.label
}
})
// 转换数据结构以匹配表头顺序
const headerKeys = Object.keys(headerMap)
const headerLabels = Object.values(headerMap)
const exportData = [
headerLabels, // 第一行为表头
...data.map(row => headerKeys.map(key => row[key] || ''))
]
// 创建工作表与工作簿
const ws = XLSX.utils.aoa_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)
ElMessage.success(`《${fileName}》导出成功!`)
} catch (error) {
console.error('Excel导出失败:', error)
ElMessage.error('导出失败,请检查数据格式或重试!')
}
}
/**
* 自动解析 el-table 组件的列配置,生成字段与标签的映射关系
* @param {Object} tableRef 对 el-table 的 ref 引用对象
* @returns {Object} 返回格式为 { prop: label } 的表头映射对象
*/
export const getTableHeaderMap = (tableRef) => {
if (!tableRef || !tableRef.columns) {
ElMessage.warning('解析表头失败:表格ref无效!')
return {}
}
const headerMap = {}
// 遍历所有列,提取有效字段与标签
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的字段映射配置有误
解决方案:确认源数据为非空数组;检查
中的字段名称是否与实际数据结构完全匹配dataheaderMap -
问题现象:DOM导出时无法找到目标表格
原因分析:可能是选择器书写错误,或目标DOM尚未完成挂载
解决方案:确保使用的选择器正确无误;或通过传入实际的DOM元素对象来避免选择器问题
(可参考ref
的方式)tableRef.value.$el -
问题现象:导出文件出现乱码
原因分析:Blob的数据类型配置不正确
解决方案:工具已在底层默认配置了正确的MIME类型,确保输出编码正常
(具体类型见Blob
)application/vnd.openxmlformats-officedocument.spreadsheetml.sheet -
问题现象:合并单元格在导出后显示异常
原因分析:xlsx库对从DOM直接解析的合并单元格支持能力有限
解决方案:建议优先采用「数组数据导出」模式,减少对DOM结构的依赖,提升兼容性
七、扩展说明
-
支持自定义格式:可在
方法内的exportExcel
处理阶段,增加对日期、金额等特殊字段的格式化逻辑,实现灵活输出。exportData -
大文件导出优化:当导出数据量超过一万条时,建议使用
替代传统的XLSX.writeFile
下载方式,有效防止浏览器内存溢出问题。Blob -
多工作表导出功能:可通过扩展
方法,支持接收多个数据集exportExcel
,从而生成包含多个工作表的Excel文件,满足复杂报表需求。data + headerMap -
样式定制能力:当前所用的
库基础版本不支持Excel样式设置。如需实现字体、边框、背景色等样式控制,可考虑升级至xlsx
或xlsx-style
等更高级的库。exceljs


雷达卡


京公网安备 11010802022788号







