/plugins/excel-export-plugin.js
import * as XLSX from 'xlsx'
import * as XLSX_STYLE from 'xlsx-style'
const ExcelExportPlugin = {
install(Vue) {
Vue.prototype.$exportExcel = function (name, dataMap, data) {
const { headerList, keyList, colWidths } = parseHeader(dataMap)
const list = [[name], headerList, ...data.map(row => keyList.map(key => row[key] ?? ''))]
// 新建一个工作簿
const wb = XLSX.utils.book_new()
// 使用二维数组生成一个工作表
const ws = sheet_from_array_of_arrays(list)
// 合并单元格 => row为0 col0 至 col(headerList.length - 1)
ws['!merges'] = [{ s: { r: 0, c: 0 }, e: { r: 0, c: headerList.length - 1 } }]
// 设置每列的宽度(单位:px)
ws['!cols'] = colWidths
// 设置每行的高度(单位:px)
const wsRows = []
for (const i in list) {
if (i == 0) {
wsRows.push({ hpx: 100 }) // 首行高度为 100px
} else if (i == 1) {
wsRows.push({ hpx: 60 }) // 第二行高度为 60px
} else {
wsRows.push({ hpx: null }) // 其他行高度为 30px
}
}
ws['!rows'] = wsRows
// 设置单元格样式
for (const key in ws) {
if (['!ref', '!merges', '!cols', '!rows'].includes(key)) continue
// 匹配表格第一行(注意 A1 单元已合并为一个单元),设置其样式
if (key === 'A1') {
ws[key] = {
t: 's', // 设置单元格类型(type: b Boolean, e Error, n Number, d Date, s Text, z Stub)
v: name, // 设置单元格内容(raw value (number, string, Date object, boolean))
s: {
// 设置单元格样式
fill: {
// 设置背景色
fgColor: { rgb: '85ce61' }
},
font: {
// 设置字体
name: '等线', // 字体名称
sz: 18, // 字体大小
bold: true, // 字体是否加粗
color: { rgb: '5e7ce0' } // 文字颜色
},
alignment: {
// 设置居中
horizontal: 'center', // 水平(向左、向右、居中)
vertical: 'center', // 上下(向上、向下、居中)
wrapText: false, // 设置单元格自动换行,目前仅对非合并单元格生效
indent: 0 // 设置单元格缩进
}
}
}
} else if (key.match(/\d+/g)?.join('') === '2') {
// 设置th单元格样式
ws[key].s = getCellStyle('header')
} else {
// 设置td单元格样式
ws[key].s = getCellStyle('body')
}
}
// 在工作簿中添加工作表
XLSX.utils.book_append_sheet(wb, ws, '第一页')
// 使用 xlsx-style 写入文件方式,使得自定义样式生效
const blob = new Blob([s2ab(XLSX_STYLE.write(wb, {
bookType: 'xlsx', type: 'binary', cellStyles: true
}))])
// 导出 Excel 文件
download(blob, `${name} - ${formatDate(new Date())}.xlsx`)
}
}
}
function parseHeader(dataMap) {
const headerList = dataMap.map(item => item.label)
const keyList = dataMap.map(item => item.key)
const colWidths = dataMap.map(item => ({ wch: item.wch || 20 })) // 设置列宽 默认为20
return { headerList, keyList, colWidths }
}
// 设置单元格样式
function getCellStyle(type) {
return {
// 单元格边框
border: {
top: { style: 'thin' }, bottom: { style: 'thin' },
left: { style: 'thin' }, right: { style: 'thin' }
},
// 单元格背景色
fill: { fgColor: { rgb: type === 'header' ? '85ce61' : 'ffffff' } },
// 单元格字体
font: { name: '微软雅黑', sz: type === 'header' ? 12 : 10 },
// 设置单元格对齐方式
alignment: { horizontal: 'center', vertical: 'center', wrapText: true }
}
}
// 获取时间格式
function formatDate(date) {
return date.getFullYear() +
String(date.getMonth() + 1).padStart(2, '0') +
String(date.getDate()).padStart(2, '0') +
String(date.getHours()).padStart(2, '0') +
String(date.getMinutes()).padStart(2, '0') +
String(date.getSeconds()).padStart(2, '0')
}
// 文件流转换
function s2ab(s) {
const buf = new ArrayBuffer(s.length)
const view = new Uint8Array(buf)
for (let i = 0; i < s.length; i++) view[i] = s.charCodeAt(i) & 0xff
return buf
}
// 使用 a 标签下载文件
function download(blob, fileName) {
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = fileName
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
window.URL.revokeObjectURL(url)
}
// 使用二维数组生成一个工作表
function sheet_from_array_of_arrays(data) {
const ws = {}
const range = {
s: { c: 10000000, r: 10000000 },
e: { c: 0, r: 0 }
}
for (let R = 0; R !== data.length; ++R) {
for (let C = 0; C !== data[R].length; ++C) {
if (range.s.r > R) range.s.r = R
if (range.s.c > C) range.s.c = C
if (range.e.r < R) range.e.r = R
if (range.e.c < C) range.e.c = C
const cell = { v: data[R][C] }
if (cell.v == null) continue
const cell_ref = XLSX.utils.encode_cell({ c: C, r: R })
cell.t = typeof cell.v === 'number' ? 'n' : 's'
ws[cell_ref] = cell
}
}
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range)
return ws
}
export default ExcelExportPlugin
在 main.js 中注册插件
import Vue from 'vue'
import App from './App.vue'
import ExcelExportPlugin from './plugins/excel-export-plugin'
Vue.use(ExcelExportPlugin)
new Vue({
render: h => h(App)
}).$mount('#app')
在组件中使用
this.$exportExcel(
'订单理赔数据报表',
[
{ label: '商品名称', key: 'goods_name', wch: 15 },
{ label: '商品价格', key: 'price' }, // 默认列宽 20
{ label: '商品数量', key: 'goods_num', wch: 10 },
],
this.tableData
)