SheetJS是前端导出Excel文件的利器,通过很简单的方法就可以实现导出xls、xlsx、odd等格式的文件。
但是SheetJS免费版本支持的格式太少,包括居中、自动换行、冻结等都不行。github上有一个开源的库:xlsx-style。该库是从SheetJS fork出来的,也就是在SheetJS的基础上进行开发的,所以使用xlsx-style就不需要再在项目中引入SheetJS了。
NOTE: That project is a fork of the original (and awesome) SheetJS/xlsx project.
基础
如果你刚开始写SheetJS,强烈推荐你先看SheetJS基础教程。
看完该教程,你已经学会了合并单元格,xlsx文件读取与导出,xlsx与其他格式的转换等。
如果需要增加格式,可以使用另一个开源的库:xlsx-style
成果
我最后导出的Excel的样子:
resultExcel.png
使用到的功能:
文字居中
自动换行
列宽设置
单元格合并
冻结表头等
安装
xlsx-style的README文档上的安装方式:
使用 npm:
$ npm install xlsx-style --save
但是我安装后,总是会报错
This relative module was not found:
./cptable in ./node_modules/xlsx-style@0.8.13@xlsx-style/dist/cpexcel.js
如果你安装的时候没问题,最好直接使用npm的方式
直接手动安装
// 下载项目到本地
$ git clone https://github.com/protobi/js-xlsx
然后把 js-xlsx-master/dist/ 路径下的 xlsx.core.min.js、xlsx.js、jszip.js 三个文件拖拽到项目里。你也可以直接使用 xlsx.full.min.js。
然后在index.html中引入就可以用了。
下面是我放的位置,供参考。
image-20191213161055737.png
使用
请先阅读基础教程
定义文字字体、大小、居中和换行:
// 设置表格中cell默认的字体,居中,颜色等
defaultCellStyle: {
font: { name: "宋体", sz: 11, color: { auto: 1 } },
border: {
color: { auto: 1 }
},
alignment: {
/// 自动换行
wrapText: 1,
// 居中
horizontal: "center",
vertical: "center",
indent: 0
}
}
在生成CellObject的时候添加这个defaultStyle。
// 从json转化为sheet,xslx中没有aoaToSheet的方法,该方法摘自官方test
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;
/// 这里生成cell的时候,使用上面定义的默认样式
const cell = {v: data[R][C], s: this.defaultCellStyle};
if(cell.v == null) continue;
const cell_ref = XLSX.utils.encode_cell({c:C,r:R});
/* TEST: proper cell types and value handling */
if(typeof cell.v === 'number') cell.t = 'n';
else if(typeof cell.v === 'boolean') cell.t = 'b';
else if(cell.v instanceof Date) {
cell.t = 'n'; cell.z = XLSX.SSF._table[14];
cell.v = this.dateNum(cell.v);
}
else cell.t = 's';
ws[cell_ref] = cell;
}
}
/* TEST: proper range */
if(range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
return ws;
},
冻结列表
// 冻结前6行和第1列,右方下方 可以滑动
// 这里xSplite和ySplite不能设置为0,否则下载下来Excel解析会出现错误
sheet["!freeze"] = {
xSplit: "1",
ySplit: "6",
// 坐上角是哪个cell
topLeftCell: "B7",
activePane: "bottomRight",
state: "frozen",
}
列宽设置
/// wch不是像素宽度!!!这个数值具体换算不太清楚,在Excel里拉动的时候会显示
/// https://docs.sheetjs.com/#column-properties 这里有解释
const sheetCols = [
{ wch: 8} , // 序号
{ wch: 10 }, // 市别
{ wch: 20 }, // 示范工程项目名称
{ wch: 9 }, // 联运线路(条)
];
下面是我最后的代码
导出用的是FileSaver
// 将一个sheet转成最终的excel文件的blob对象,然后利用URL.createObjectURL下载
sheet2blob(sheet, sheetName) {
sheetName = sheetName || 'sheet1';
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
workbook.Sheets[sheetName] = sheet
window.console.log(workbook)
// 生成excel的配置项
const wopts = {
bookType: 'xlsx', // 要生成的文件类型
bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
type: 'binary'
};
const wbout = XLSX.write(workbook, wopts, { defaultCellStyle: this.defaultCellStyle });
const blob = new Blob([s2ab(wbout)], {type: "application/octet-stream"});
// 字符串转ArrayBuffer
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;
}
return blob;
},
transformF() {
// 表头信息 要合并的字段用null代替
let aoa = [
['江苏省多式联运示范工程项目2019年第二季度动态监测信息表'],
['序号', '市别', '示范工程项目名称', '示范工程项目运行情况'],
[null, null, null, '联运组织',null, null, null, null, null, null, '企业总货物运输量(万吨/万标箱)', null, '枢纽场站建设投资额(万元)', '装备配备建设投资额(万元)', '信息化建设投资额(万元)', '其他'],
[null, null, null, '联运线路(条)','联运线路运营情况', null, null, null, null, null],
[null, null, null, null, '线路', '联运路线', '联运模式', '联运量', null, '联运周转量(万吨公里)', null, null, null, null, null, null],
[null, null, null, null, null, null, null, '万吨', '万标箱', null, '万吨', '万标箱', null, null, null],
]
// json => sheet
const sheet = this.sheet_from_array_of_arrays(aoa);
// 表头合并: r: row 行;c:column 列
const mergeTitle = [
// 江苏省多式联运示范工程项目2019年第二季度动态监测信息表
{
s: {r: 0, c: 0},
e: {r: 0, c: 15}
},
// 序号
{
s: {r: 1, c: 0},
e: {r: 5, c: 0}
},
// 市别
{
s: {r: 1, c: 1},
e: {r: 5, c: 1}
},
// 示范工程名称
{
s: {r: 1, c: 2},
e: {r: 5, c: 2}
},
// 示范工程项目运行情况
{
s: {r: 1, c: 3},
e: {r: 1, c: 15}
},
// 联运组织
{
s: {r: 2, c: 3},
e: {r: 2, c: 9}
},
// 联运线路(条)
{
s: {r: 3, c: 3},
e: {r: 5, c: 3}
},
// 联运线路运营情况
{
s: {r: 3, c: 4},
e: {r: 3, c: 9}
},
// 线路
{
s: {r: 4, c: 4},
e: {r: 5, c: 4}
},
// 联运路线
{
s: {r: 4, c: 5},
e: {r: 5, c: 5}
},
// 联运模式
{
s: {r: 4, c: 6},
e: {r: 5, c: 6}
},
// 联运量
{
s: {r: 4, c: 7},
e: {r: 4, c: 8}
},
// 联运周转量(万吨公里)
{
s: {r: 4, c: 9},
e: {r: 5, c: 9}
},
// 企业总货物运输量(万吨/万标箱)
{
s: {r: 2, c: 10},
e: {r: 4, c: 11}
},
// 枢纽场站建设投资额(万元)
{
s: {r: 2, c: 12},
e: {r: 5, c: 12}
},
//装备配备建设投资额(万元)
{
s: {r: 2, c: 13},
e: {r: 5, c: 13}
},
// 信息化建设投资额(万元)
{
s: {r: 2, c: 14},
e: {r: 5, c: 14}
},
// 其他
{
s: {r: 2, c: 15},
e: {r: 5, c: 15}
},]
sheet['!merges'] = mergeTitle.concat(mergeContent);
// 冻结前6行和第一列,右下可以滑动
sheet["!freeze"] = {
xSplit: "1",
ySplit: "6",
topLeftCell: "B7",
activePane: "bottomRight",
state: "frozen",
}
sheet["!margins"] = { left: 1.0, right: 1.0, top: 1.0, bottom: 1.0, header: 0.5, footer: 0.5 }
// 列宽 使用的不是像素值
const sheetCols = [
{ wch: 8} , // 序号
{ wch: 10 }, // 市别
{ wch: 20 }, // 示范工程项目名称
{ wch: 9 }, // 联运线路(条)
{ wch: 8 }, // 线路
{ wch: 18 }, // 联运路线
{ wch: 15 }, // 联运模式
{ wch: 9 }, // 联运量-万吨
{ wch: 9 }, // 联运量-万标箱
{ wch: 12 }, // 联运周转量
{ wch: 9 }, // 企业万吨
{ wch: 9 }, // 企业 万标箱
{ wch: 10 }, // 枢纽站
{ wch: 10 }, // 装备
{ wch: 10 }, // 信息化
{ wch: 27 }, // 备注
];
sheet['!cols'] = sheetCols;
// 设置行高,但是没起作用
/*
* type RowInfo = {
hidden?: boolean; // if true, the row is hidden
/// row height is specified in one of the following ways:
hpx?: number; // height in screen pixels
hpt?: number; // height in points
level?: number; // 0-indexed outline / group level
};*/
// sheet['!rows'] = [
// { hidden: true },
// { hpx: 44 },
// { hpt: 12 },
// { level: 1 },
// { level: 2 },
// { level: 3 },
// { level: 4 },
// null,null,null,null,null,
// null,null,null,null,null,
// null,null,null,null,null,
// null,null,null,null,null,
// ];
// sheet => bolb
const wbBlob = this.sheet2blob(sheet, '1')
// 保存下载
FileSaver.saveAs(wbBlob, 'd.xlsx')
},
几乎所有相关代码都已经贴出来了,希望有帮到你。
如果有其他格式我没有用到而你迫切需要,首先你可以看免费的SheetJS是否能满足你的需求。不够的话,可以去js-xlsx的查看文档或者下载官方的demo,查看是否有相应的test文件,文章里的很多属性也都是通过许多测试文件找到的。
最后,用餐愉快。