美文网首页
Vue项目中使用xlsx-style导出有格式的表格

Vue项目中使用xlsx-style导出有格式的表格

作者: ted4kra | 来源:发表于2019-12-24 20:02 被阅读0次

    配套demo在这里

    SheetJS是前端导出Excel文件的利器,通过很简单的方法就可以实现导出xls、xlsx、odd等格式的文件。

    官方github:https://github.com/SheetJS/js-xlsx

    但是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的方式

    1. 直接手动安装

      // 下载项目到本地
      $ git clone https://github.com/protobi/js-xlsx
      

    然后把 js-xlsx-master/dist/ 路径下的 xlsx.core.min.jsxlsx.jsjszip.js 三个文件拖拽到项目里。你也可以直接使用 xlsx.full.min.js

    然后在index.html中引入就可以用了。

    下面是我放的位置,供参考。


    image-20191213161055737.png

    使用

    请先阅读基础教程

    1. 定义文字字体、大小、居中和换行:
     // 设置表格中cell默认的字体,居中,颜色等
     defaultCellStyle: {
       font: { name: "宋体", sz: 11, color: { auto: 1 } },
         border: {
           color: { auto: 1 }
         },
         alignment: {
            /// 自动换行
           wrapText: 1,
             // 居中
           horizontal: "center",
           vertical: "center",
           indent: 0
      }
     }
    
    1. 在生成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;
    },
    
    1. 冻结列表
       // 冻结前6行和第1列,右方下方 可以滑动
       // 这里xSplite和ySplite不能设置为0,否则下载下来Excel解析会出现错误
       sheet["!freeze"] = {
         xSplit: "1",
         ySplit: "6",
         // 坐上角是哪个cell
         topLeftCell: "B7",
         activePane: "bottomRight",
         state: "frozen",
       }
    
    1. 列宽设置
       /// 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文件,文章里的很多属性也都是通过许多测试文件找到的。

    最后,用餐愉快。

    相关文章

      网友评论

          本文标题:Vue项目中使用xlsx-style导出有格式的表格

          本文链接:https://www.haomeiwen.com/subject/kmwfoctx.html