项目需求,需要在前端生成excel,后端仅返回数据。一开始我觉得前端是不能做这种读写文件的事情,然后去github上找了找,发现前端库也越来越强大了。目前github上前端操作excel的包就是 sheetjs了,也就是 js-xlsx
,达到了22K多start,功能十分强大,关于sheetjs有个社区版本,可以导入导出excel,但不支持单元格合并背景色字体等这些功能。它还有个pro版,也就是收费版本,收费版对于单元格的样式设置则没有限制。不过本着开源精神 (主要还是穷….) ,对于这种要收费的包我只能果断放弃了,然后在npm的仓库中,在谷歌上搜啊找啊,发现另一个免费的excel包 xlsx-populate。它不仅免费,而且在API的使用上也比 sheetjs 要简单的多。不过本着学习的态度,对于 sheetjs 的社区版的使用,我也会放使用demo,毕竟先踩了它的坑。
js-xlsx
1.项目根目录安装
npm install xlsx
2.前端页面导入
import XLSX from 'xlsx';
3.导出excel
// 表头
headers : {
A1: {v: '2019年学生成绩表'},
A3: { v: '编号' },
B3: { v: '姓名' },
C3: { v: '年龄' },
D3: { v: '邮箱' }
},
// 数据
data : {
A4: { v:'100' },
B4: { v:'张三' },
C4: { v:'28' },
D4: { v:'sanzhang@outlook.com' },
A5: { v:'200' },
B5: { v:'李四' },
C5: { v:'26' },
D5: { v:'sili@sina.com' }
}
// 按钮
<Button type="primary" danger onClick={e => this.exportExcel(this.state.headers,this.state.data)}>
导出Excel
</Button>
// 导出excel点击事件
// headers 表头
// data 数据
// fileName 文件名
exportExcel = (headers, data, fileName = '请假记录表.xlsx') => {
// 合并 headers 和 data
const output = Object.assign({}, headers, data);
// 获取所有单元格的位置
const outputPos = Object.keys(output);
// 计算出范围 ,["A1",..., "H2"]
const ref = `${outputPos[0]}:${outputPos[outputPos.length - 1]}`;
// 构建 workbook 对象
const wb = {
SheetNames: ['mySheet1','mySheet2'],
Sheets: {
mySheet1: Object.assign(
{},
output,
{
'!ref': ref,
// 列宽度
'!cols': [{ wpx: 65 }, { wpx: 100 }, { wpx: 200 }, { wpx: 80 }],
// 合并单元格 0到1行,0到3列合并
'!merges': [{
s: { // s开始
c: 0, // 开始列
r: 0 // 开始行
},
e: { // e结束
c: 3, // 结束列
r: 1 // 结束行
}
}]
},
),
mySheet2: Object.assign(
{},
output,
{
'!ref': ref,
'!cols': [{ wpx: 65 }, { wpx: 100 }, { wpx: 200 }, { wpx: 80 }],
// 合并单元格
'!merges': [{
s: { // s开始
c: 0, // 开始列
r: 0 // 开始行
},
e: { // e结束
c: 3, // 结束列
r: 1 // 结束行
}
}]
},
)
},
};
// 导出 Excel
XLSX.writeFile(wb, fileName);
};
4.导出结果
xlsx-populate
1.项目根目录安装
npm install xlsx-populate
2.前端页面导入
import XlsxPopulate from 'xlsx-populate';
3.导出excel
<Button type="primary" style={{marginLeft:'20px'}} onClick={e => this.exportXlsxPopulate()}>
导出Excel2
</Button>
exportXlsxPopulate = () => {
XlsxPopulate.fromBlankAsync()
.then(workbook => {
// Modify the workbook.
// 第一个sheet页
let wb = workbook.sheet("Sheet1");
// 设置A列宽
wb.column("A").width(40);
// 设置第一行高
wb.row(1).height(50);
// 标题合并单元格 A列到D列,1行到2行合并
wb.range("A1:D2").merged(true)
.value("月考成绩\n2019年12月")
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center")//垂直居中
.style("wrapText", true) //自动换行
.style("border",true);
// 表头
wb.cell("A3")
.value("姓名")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
wb.cell("B3")
.value("语文")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
wb.cell("C3")
.value("数学")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
wb.cell("D3")
.value("英语")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
// 数据
// 张三
wb.cell("A4")
.value("张三")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
wb.cell("B4")
.value("84")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
wb.cell("C4")
.value("88")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
wb.cell("D4")
.value("92")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
// 李四
wb.cell("A5")
.value("李四")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
wb.cell("B5")
.value("77")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
wb.cell("C5")
.value("98")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
wb.cell("D5")
.value("87")
.style("border",true)
.style("verticalAlignment", "center")//水平居中
.style("horizontalAlignment", "center");//垂直居中;
// Write to file.
workbook.outputAsync().then(function(blob) {
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
// If IE, you must uses a different method.
window.navigator.msSaveOrOpenBlob(blob, 'test.xlsx');
} else {
let url = window.URL.createObjectURL(blob);
let a = document.createElement('a');
document.body.appendChild(a);
a.href = url;
a.download = 'test.xlsx';
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}
})
});
};
4.导出结果
更多样式属性的设置,可看官方文档。对于导出上面的例子简单明了,常规操作,下面来点不一样的
解析excel
// 从一个路径读取文件
XlsxPopulate.fromFileAsync("./Book1.xlsx")
.then(workbook => {
const value = workbook.sheet("Sheet1").cell("A1").value();
console.log(value);
});
// 从data中获取文件,data可以是blob ,buffer
XlsxPopulate.fromDataAsync();
单元格设置为富文本
const RichText = require('xlsx-Populate').RichText;
const cell = workbook.sheet(0).cell('A1');
cell.value(new RichText());
cell.value()
.add('hello ', { italic: true, bold: true })
.add('world!', { fontColor: 'FF0000' });
单元格添加超链接
cell.value("Link Text")
.style({ fontColor: "0563c1", underline: true })
.hyperlink("http://example.com");
获取单元格背景
const XlsxPopulate = require('xlsx-populate');
XlsxPopulate.fromFileAsync("./Book1.xlsx")
.then(workbook => {
const background = workbook.sheet("Sheet1").cell("A1").style("fill");
console.log(background);
});