纯前端导出xlsx表格,使用
xlsx.js
可以直接导出xlsx表格,但是不能给表格添加样式,如果需要添加样式我们可以使用xlsx-style
来给表格添加。
先来看一下简单的导出效果:
不加样式我们可以获取table的dom直接导出;
安装
npm install file-saver --save
npm install xlsx --save
// 如不需要给表格添加样式,则不引入以下
npm install xlsx-style --save
npm i xlsx-style安装完在使用的时候会报错
解决方法:
方法1:
找到\node_modules\xlsx-style\dist\cpexcel.js
把var cpt = require(‘./cpt’ + ‘able’); 改为 var cpt = cptable;方法2(推荐):
chainWebpack: config => {
config.externals({ ‘./cptable’: ‘var cptable’});
}
代码
一,不加样式导出
1.创建 xlsx.js
import FileSaver from 'file-saver';
import * as XLSX from 'xlsx'
function downloadExcel(id, fileName) {
/* 从表生成工作簿对象 */
var wb = XLSX.utils.table_to_book(document.querySelector(`#${id}`));
/* 获取二进制字符串作为输出 */
var wbout = XLSX.write(wb, {
bookType: 'xlsx',
bookSST: true,
type: 'array',
});
try {
FileSaver.saveAs(
//Blob 对象表示一个不可变、原始数据的类文件对象。
//Blob 表示的不一定是JavaScript原生格式的数据。
//File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
//返回一个新创建的 Blob 对象,其内容由参数中给定的数组串联组成。
new Blob([wbout], {
type: 'application/octet-stream',
}),
//设置导出文件名称
fileName
);
} catch (e) {
if (typeof console !== 'undefined') console.log(e, wbout);
}
return wbout;
}
export default downloadExcel;
2.使用
<template>
<div>
<el-button @click="exportFile">默认按钮</el-button>
<el-table id="tableId" :data="tableData" style="width: 100%">
<el-table-column prop="date" label="日期" width="180" />
<el-table-column prop="name" label="姓名" width="180" />
<el-table-column prop="address" label="地址" />
</el-table>
</div>
</template>
<script>
import downloadExcel from "@/plugins/xlsx.js"; // 第一步创建的xlsx.js 引入
export default {
data() {
return {
tableData: [
{ date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄' },
{ date: '2016-05-04', name: '王小虎', address: '上海市普陀区金沙江路 1517 弄' },
{ date: '2016-05-01', name: '王小虎', address: '上海市普陀区金沙江路 1519 弄' },
{ date: '2016-05-03', name: '王小虎', address: '上海市普陀区金沙江路 1516 弄' }
]
}
},
methods: {
exportFile() {
downloadExcel("tableId", "名称.xls");
}
}
}
</script>
二,加样式导出
1.创建 xlsxStyle.js
// import XLSX from 'xlsx';
import XLSX from "xlsx-style";
/**
* 定制化导出excel(定制化:附加标题&&样式)
* @param { 表头 } headers
* @param { 数据源 } datasource
* @param { 表格副标题 } options
* @param { 配置文件类型 } type
* @param { 导出的文件名 } fileName
*/
function exportExcel(headers, datasource, options, type, fileName = "未命名") {
// 处理列宽
const cloWidth = headers.map((item) => ({ wpx: item.width || 60 }));
// 处理附加表头 这里处理副标题样式 s
const _options = options
.map((item, i) =>
Object.assign(
{},
{
s: item.s,
title: item.title,
position: String.fromCharCode(65) + (i + 1),
}
)
)
.reduce(
(prev, next) =>
Object.assign({}, prev, {
[next.position]: { v: next.title, s: next.s },
}),
{}
);
// 处理表头 这里修改表头样式 s
const _headers = headers
.map((item, i) =>
Object.assign(
{},
{
s: item.s,
ss: item.ss,
key: item.dataIndex,
title: item.title,
position: String.fromCharCode(65 + i) + (options.length + 1),
}
)
)
.reduce(
(prev, next) =>
Object.assign({}, prev, {
[next.position]: {
v: next.title,
key: next.key,
s: next.s,
ss: next.ss,
},
}),
{}
);
// 处理数据源 这里修改内容样式 ss
const _data = datasource
.map((item, i) =>
headers.map((col, j) =>
Object.assign(
{},
{
ss: col.ss,
content: item[col.dataIndex],
position: String.fromCharCode(65 + j) + (options.length + i + 2),
}
)
)
)
.reduce((prev, next) => prev.concat(next))
.reduce(
(prev, next) =>
Object.assign({}, prev, {
[next.position]: { v: next.content, s: next.ss },
}),
{}
);
const output = Object.assign({}, _options, _headers, _data);
const outputPos = Object.keys(output); // 设置表格渲染区域,如从A1到C8
// 合并单元格
const merges = options.map((item, i) => ({
s: { c: 0, r: i },
e: { c: headers.length - 1, r: i },
}));
const wb = {
SheetNames: ["mySheet"], // 保存的表标题
Sheets: {
mySheet: Object.assign(
{},
output, // 导出的内容
{
"!ref": `${outputPos[0]}:${outputPos[outputPos.length - 1]}`, // 设置填充区域(表格渲染区域)
"!cols": [...cloWidth],
"!merges": [...merges],
}
),
},
};
// 这种导出方法只适用于js-xlsx,且设置的单元格样式不生效,
// 直接打开下面这两行就行了,后面的可以省略
// XLSX.writeFile(wb,`${fileName}.xlsx`);
// return;
/**
* 以下这种导出方法对于js-xlsx/xlsx-style都适用
* 区别在于import XLSX from 'xlsx-style';可以设置单元格样式
* import XLSX from 'xlsx';不支持设置单元格样式
*
* new Blob转换成二进制类型的对象
*/
const tmpDown = new Blob(
[
s2ab(
XLSX.write(
wb,
{
bookType: type == undefined ? "xlsx" : type.bookType,
bookSST: false,
type: "binary",
} // 这里的数据是用来定义导出的格式类型
)
),
],
{
type: "",
}
);
// 数据都准备完成,可以开始下载excel了
downExcel(
tmpDown,
`${fileName + "."}${type.bookType == "biff2" ? "xls" : type.bookType}`
);
}
/**
* <a>标签下载excel
* @param { Blob对象:二进制的数据 } obj
* @param { 文件名+文件类型后缀 } fileName
*/
function downExcel(obj, fileName) {
const a_node = document.createElement("a");
a_node.download = fileName;
// 兼容ie
if ("msSaveOrOpenBlob" in navigator) {
window.navigator.msSaveOrOpenBlob(obj, fileName);
} else {
// URL.createObjectURL根据传入的参数创建一个指向该参数对象的URL. 这个URL的生命仅存在于它被创建的这个文档里.
// 新的对象URL指向执行的File对象或者是Blob对象.
a_node.href = URL.createObjectURL(obj);
}
a_node.click();
// 每次调用createObjectURL的时候,一个新的URL对象就被创建了.即使你已经为同一个文件创建过一个URL.
// 如果你不再需要这个对象,要释放它,需要使用URL.revokeObjectURL()方法.
// 当页面被关闭,浏览器会自动释放它,但是为了最佳性能和内存使用,当确保不再用得到它的时候,就应该释放它.
setTimeout(() => {
URL.revokeObjectURL(obj);
}, 100);
}
// 字符串转字符流---转化为二进制的数据流
function s2ab(s) {
if (typeof ArrayBuffer !== "undefined") {
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;
} else {
const buf = new Array(s.length);
for (let i = 0; i != s.length; ++i) buf[i] = s.charCodeAt(i) & 0xff;
return buf;
}
}
export default exportExcel;
2.使用
<script>
export default {
methods: {
exportList() {
// table 标题
const headers = [
{
title: "学员名字",
dataIndex: "name",
width: 140,
// 标题样式
s: {
font: { sz: 14, bold: true, vertAlign: true },
alignment: { vertical: "center", horizontal: "center" },
fill: { bgColor: { rgb: "E8E8E8" }, fgColor: { rgb: "E8E8E8" } },
},
// 标题对应列样式
ss: {
font: { sz: 12, vertAlign: true },
alignment: { vertical: "center", horizontal: "center" },
},
},
{ title: "联系方式", dataIndex: "phone", width: 140 },
{ title: "状态", dataIndex: "status", width: 140 },
{ title: "扣除课时", dataIndex: "deduct", width: 100 },
{ title: "已完成/总课时", dataIndex: "number", width: 100 },
];
// table List
const datasource = [
{
name: "张三",
phone: "12345678909",
status: "已签到",
deduct: 1,
number: "2/10",
},
{
name: "19876543210",
phone: "12345678909",
status: "未签到",
deduct: 1,
number: "1/10",
},
];
// table 副标题标题
const options = [
{
title: "一班s样式",
s: {
font: { sz: 14, bold: true, vertAlign: true },
alignment: { vertical: "center", horizontal: "center" },
fill: { bgColor: { rgb: "E8E8E8" }, fgColor: { rgb: "E8E8E8" } },
},
},
{ title: "班级:二班" },
{ title: "上课时间: 2020-11-11 14:30~16:30" },
{ title: "上课老师:胡sir" },
];
// type 可参考xlsx-style文档
const type = {
bookType: "xlsx",
bookSST: true,
type: "binary",
cellStyles: true,
};
exportExcel(headers, datasource, options, type, "考勤表");
}
};
};
</script>
s 属性可参考 xlsx-style.js 文档
三,使用 xlsx-js-style 导出
sheetjs
但是这个库基础版本无法实现带样式导出,可以使用基于sheetjs扩展的开源项目xlsx-js-style实现。
在导出文件的使用上与sheetjs无异,数据数组和数据表的生成都是沿用SheetJs的核心API,在导出文件前可以对数据表worksheet进行单元格级别的设置,以下提供一个简单的使用示例,具体可设置的样式可点击上线的xlsx-js-style官网查看。
安装
使用 xlsx-js-style 导出
npm install xlsx-js-style --save
全局引入,main.js
加入如下代码:
import XLSX from 'xlsx-js-style'
Vue.prototype.$xlsx = XLSX
这样后面就可以直接this.$xlsx
使用
示例文件
<template>
<div>
<input type="button" value="点击生成excel" @click="test">
</div>
</template>
<script>
export default {
data() {
return {};
},
methods: {
test() {
// excel的表数据,根据实际情况生成
let excelData = [["指标", "2021年", "2020年", "2019年", "2018年", "2017年", "2016年", "2015年", "2014年", "2013年", "2012年"], ["国民总收入(亿元)", "1133239.8", "1005451.3", "983751.2", "915243.5", "830945.7", "742694.1", "685571.2", "644380.2", "588141.2", "537329.0"], ["国内生产总值(亿元)", "1143669.7", "1013567.0", "986515.2", "919281.1", "832035.9", "746395.1", "688858.2", "643563.1", "592963.2", "538580.0"], ["第一产业增加值(亿元)", "83085.5", "78030.9", "70473.6", "64745.2", "62099.5", "60139.2", "57774.6", "55626.3", "53028.1", "49084.6"], ["第二产业增加值(亿元)", "450904.5", "383562.4", "380670.6", "364835.2", "331580.5", "295427.8", "281338.9", "277282.8", "261951.6", "244639.1"], ["第三产业增加值(亿元)", "609679.7", "551973.7", "535371.0", "489700.8", "438355.9", "390828.1", "349744.7", "310654.0", "277983.5", "244856.2"], ["人均国内生产总值(元)", "80976", "71828", "70078", "65534", "59592", "53783", "49922", "46912", "43497", "39771"], ["指标", "2021年", "2020年", "2019年", "2018年", "2017年", "2016年", "2015年", "2014年", "2013年", "2012年"]]; // excel表数据
let workbook = this.$xlsx.utils.book_new(); // 工作簿
let worksheet = this.$xlsx.utils.aoa_to_sheet(excelData); // 数据表
let cols = []; // 设置每列的宽度
// wpx 字段表示以像素为单位,wch 字段表示以字符为单位
for (let i = 0; i <= excelData[0].length; i++) {
let col = {};
if (i == 0) {
col.wch = 30;
} else {
col.wch = 18;
}
cols.push(col)
}
worksheet['!cols'] = cols; // 设置列宽信息到工作表
//以下是样式设置,样式设置放在组织完数据之后,xlsx-js-style的核心API就是SheetJS的
Object.keys(worksheet).forEach(key => {
// 非!开头的属性都是单元格
if (!key.startsWith("!")) {
worksheet[key].s = {
font: {
sz: "12"
},
alignment: {
horizontal: "left",
vertical: "center",
wrapText: true
},
border: {
top: { style: 'thin' },
right: { style: 'thin' },
bottom: { style: 'thin' },
left: { style: 'thin' }
}
};
}
})
this.$xlsx.utils.book_append_sheet(workbook, worksheet, "Sheet1");
this.$xlsx.writeFile(workbook, "国内生产总值.xlsx");
},
},
};
</script>
导出效果:
参考文献:
js-xlsx/xlsx-style 纯前端数据导出Excel且支持自定义样式
使用xlsx-js-style实现sheetjs的导出样式设置