基于XLSX-STYLE通过纯前端的方式导出Excel,并且能够设置打印的边距和纸张大小

最近在开发过程中遇到一个需求,具体如下:

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纸的尺寸,美国标准纸张的尺寸如下:


image.png

而A4尺寸为210mm*297mm

这就比较尴尬了,如果按照需求,将内容填充整个A4纸,那么用户在点击打印时,会发现文档默认是显示不完整的,只有手动调整纸张尺寸为A4纸才能正常打印。这显然是不行的。

在继续摸索之后,这篇文章给了我启发:
# Excel~文件结构初窥

按文章所述,只需要把Excel的后缀名改为zip或者rar,就可以查看它的文件结构。于是我用Excel创建了一个空的xlsx文件,在打印参数里面将纸张尺寸设置为A4,保存后,再修改后缀名为zip,解压以后看到下面的内容:


image.png

在xl目录下,有一个worksheets文件夹,再打开worksheets文件夹,会发现一个一个以表名命名的xml文件:


image.png

打开这个xml文件,果然有了新的发现:


image.png

原来在前面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
  }

这样问题就得到了完美解决。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353