最近在开发过程中遇到一个需求,具体如下:
1、需要将数据以Excel的格式导出
2、可以定义Excel内单元格的宽高和字体、边框样式
3、确保Excel的数据可以以一张横版A4纸的方式打印,并且内容可以自动调整宽高,保证其始终填满整张A4纸
4、上述功能在前端完成,不依赖后端处理
经过一番调研后,发现上述需求可以借助xlsx.js搭配xlsx-style部分实现。
关于xlsx.js和xlsx-style的详细介绍可见:https://www.jianshu.com/p/74d405940305
相关的开发文档可以参考:https://www.npmjs.com/package/js-xlsx
看起来很复杂,事实上也的确很复杂,核心要点是需要以json的格式创建一个Excel的配置文档,然后通过XLSX.write的方法生成为Excel文档并保存。创建Excel的数据结构如下:
const workbook = {
SheetNames: ["表1","表2"], //EXCEL表名,数组格式,如果需要创建多个Excel表,需要在这里填写对应的表名
Sheets: { //每个表的具体样式和内容
"表1": {
'!ref': 'A1:G30', //数据所占单元格的范围
'!rows': [ //每一行的行高
{hpx: 40},
{hpx: 40},
{hpx: 30},
{hpx: 24},
{hpx: 24}
...
],
'!cols':[ //每一列的列宽
{wpx:100},
{wpx:200}
],
'!pageSetup': {scale: 67, orientation: 'landscape'}, //数据表打印时的页面配置,scale表示缩放比例,orientation是打印时的纸张方向landscape为横向打印
'!margins': {left:0.2, right: 0.2, top: 0.2, bottom: 0.2, header: 0.2, footer: 0.2}, //打印时的页面边距
'!merges': [ //需要合并的单元格
{
s: {c: 0, r: 0},
e: {c: 0, r: 1}
},
{
s: {c: 1, r: 2},
e: {c: 7, r: 2}
}
...
],
A1: { //具体单元格的样式和内容
v: "单元格文字", //文字
t: 's', //类型,s表示string
s:{ //样式
alignment:{
vertical:"center",
horizontal:"center"
},
font:{
sz:24,
name:"黑体"
}
}
}
...
}
}
}
在实际的使用中发现,xlsx-style由于是根据xlsx.js的早期版本开发的一个开源样式插件,很多配置项都是无效的,比如无法通过!rows和!cols设置单元格宽高,无法通过!pageSetup和!margins来配置页面打印时的样式。在网上找了很多教程也无济于事。直到我找到这个项目:
LAY-EXCEL 简单快捷的导出插件
https://github.com/wangerzi/layui-excel
具体修改过程可参考JeffreyWang的个人博客:令最新JS-XLSX支持样式的改造方法
这是一个基于layui的Excel导出插件,如果项目没用到layui也可以正常使用,它把xlsx.js和xlsx-style很好地结合在一起,上面的几个问题都可以很好地解决掉。
但是——
在打印时发现与打印机匹配的纸张默认是按照美国标准纸张的尺寸来打的,而不是按照A4纸的尺寸,美国标准纸张的尺寸如下:
而A4尺寸为210mm*297mm
这就比较尴尬了,如果按照需求,将内容填充整个A4纸,那么用户在点击打印时,会发现文档默认是显示不完整的,只有手动调整纸张尺寸为A4纸才能正常打印。这显然是不行的。
在继续摸索之后,这篇文章给了我启发:
# Excel~文件结构初窥
按文章所述,只需要把Excel的后缀名改为zip或者rar,就可以查看它的文件结构。于是我用Excel创建了一个空的xlsx文件,在打印参数里面将纸张尺寸设置为A4,保存后,再修改后缀名为zip,解压以后看到下面的内容:
在xl目录下,有一个worksheets文件夹,再打开worksheets文件夹,会发现一个一个以表名命名的xml文件:
打开这个xml文件,果然有了新的发现:
原来在前面workbook的配置中!pageSetup对应的内容就是这个xml里面pageSetup的内容,而pageSize对应的就是打印时纸张的大小,当数值为9时,就表示以A4纸的尺寸来打印。不过在原始的js中,并没有相应的配置项。
所以接下来的事情就简单了,只需要在原来的!pageSetup中增加这部分配置内容即可。
在经过改良的layui-excel插件中,xlsx.js里面是没有相应的配置方法的,但是在原始的xlsx.core.min.js文件中,可以找到一个write_ws_xml_pagesetup方法,这个方法可以用来配置页面在打印时的一些属性,例如页边距、纸张设置等,只是原始代码也没有定义纸张尺寸的内容而已。所以我将xlsx.core.min.js解析后,提取出了几段相关的代码,作了一些修改后,添加到xlsx.js中:
/*这段代码置于 write_ws_xml 方法内,可以粘贴到if (ws['!margins'] != null) o[o.length] = write_ws_xml_margins(ws['!margins']); 后面*/
/* pageSetup */
if (ws['!pageSetup'] != null) o[o.length] = write_ws_xml_pagesetup(ws['!pageSetup']);
/*这段代码置于 LAY_EXCEL 下即可,例如直接粘贴到write_ws_xml_merges方法后 */
function write_ws_xml_pagesetup(setup) {
var pageSetup = writextag("pageSetup", null, {
paperSize: setup.pagesize || "9", //重点是这个参数
scale: setup.scale || "100",
orientation: setup.orientation || "portrait",
horizontalDpi: setup.horizontalDpi || "4294967293",
verticalDpi: setup.verticalDpi || "4294967293"
});
return pageSetup
}
这样问题就得到了完美解决。