el-table的导出下载并设置格式

一、背景

  1. Vue3+Ts+Element plus的项目,其中的表格是利用el-table创建的。目的是将该表格导出为Excel表格并且在表格导出后自带样式,方便查看。
  2. 需要安装file-saver、xlsx、xlsx-style包。file-saver是一个nodejs常用的文件保存包,支持多种浏览器。xlsx用于Excel表格中读取数据、生成数据的包。xlsx-style是xlsx的一个分支,主要目的是设置表格样式。

二、安装file-saver、xlsx和xlsx-style包

  1. 安装,在终端中分别输入以下代码。我用的包管理器是npm,如果你用的是pnpm或其他包管理器将npm换成pnpm即可。以下是三个包的文档链接:
    file-saver:
    https://www.npmjs.com/package/file-saver/v/2.0.0-rc.1
    xlsx:
    https://www.npmjs.com/package/xlsx
    xlsx-style:
    https://www.npmjs.com/package/xlsx-style

    //file-saver
    npm install file-saver --save
    //xlsx
    npm install xlsx
    //xlsx-style
    npm install xlsx-style --save
    
  2. xlsx-style包中存在一些问题,安装后会报错。这里以本次导出表格为目的对其中的报错进行修改xlsx-style中的代码。

    node_modules\xlsx-style\dist\cpexcel.js

    //修改其中的807行为以下代码
    /* var cpt = require('./cpt' + 'able'); */
    var cpt = cptable; //修改
    

    node_modules\xlsx-style\xlsx.js

    //修改其中的第7行
    /* var cptable, style_builder, QUOTE, _hashIndex; */
    var cptable, style_builder, QUOTE, _hashIndex, _listIndex, style_builder;//修改
    
    //修改其中的1340行
    if(typeof jszip === 'undefined') jszip = require('./jszip.js');//修改
    /*if(typeof jszip === 'undefined') jszip = require('./js'+'zip').JSZip; */
    

    一般修改以上问题就可以使用了,但是这样直接修改包文件,在重装依赖即npm install后会报错,改起来也比较麻烦,所以尽量别改原文件而是直接在配置文件中进行修改会好很多,我这里只是一个测试所以没有在配置文件中改。

三、表格导出

  1. 创建一个表格导出按钮并添加点击事件

    //<template>中
    <el-button type="primary" @click="export">导出</el-button>
    
  2. 表格导出的前提是需要在标签中添加id属性

    //<template>中
    <el-table id="Table" :data="TableData">
    ...
    </el-table>
    
  3. 导出方法,单元格样式属性见末尾

    //<script setup lang="ts">中
    import FileSaver from "file-saver";
    import {utils} from "xlsx";
    import XLSX from "xlsx-style";
    function export() {
      //如果导出后多出空白行则修改rowspan,否则可不用
      var tds = document.querySelectorAll(".el-table__footer td");
      tds.forEach(td => td.setAttribute("rowspan", "1"));
      //从el-table表生成工作簿对象
      //使用原始的格式,保留表格中的格式如%、小数末尾的0等
      var workbook = utils.table_to_book(document.querySelector("#Table"), {
        raw: true
      });
      //列宽,需要导出的表格有多少列这里的i就小于多少
      for (let i = 1; i < 15; i++) {
        workbook.Sheets.Sheet1["!cols"].push({ wpx: 80 });
      }
      //设置单元格样式
      for (const key in workbook.Sheets.Sheet1) {
        if (
          key !== "!cols" &&
          key !== "!fullref" &&
          key !== "!merges" &&
          key !== "!ref" &&
          key !== "!rows"
        ) {
          //这里的s就是具体的样式,如果想设置不一样的样式可以看xlsx-style文档
          workbook.Sheets.Sheet1[key].s = {
            //边框
            border: {
              top: {style: "thin"},
              bottom: {style: "thin"},
              left: {style: "thin",},
              right: {style: "thin",}
            },
            //对齐
            alignment: {
              horizontal: "center",
              vertical: "center",
              wrapText: true
            }
          };
        }
      }
      //修改合并单元格样式
      let arr = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N",
        "O","P","Q","R","S","T","U","V","W","X","Y","Z"];
      //由于导出时合并单元格只识别左上角的单元格,合并单元格中其他单元格
      //并不会存在,所以需要识别合并单元格中除左上角单元格外的单元格并添加
      //带样式的单元格到其中,不理解可以看四中的第2点。
      for (let item of workbook.Sheets.Sheet1["!merges"]) {
        let style = {border: {
              top: {style: "thin"},
              bottom: {style: "thin"},
              left: {style: "thin",},
              right: {style: "thin",}
            },
            alignment: {
              horizontal: "center",
              vertical: "center",
              wrapText: true
            }};
        let merge_s = { t: "s", v: "", s: style};
        if (item.s.c == item.e.c) {
          //纵向合并,其中c为字母r为数字
          let star = item.s.r;
          let end = item.e.r;
          for (let i = star + 1; i <= end; i++) {
            workbook.Sheets.Sheet1[arr[item.s.c] + (i + 1)] = merge_s;
          }
        } else {
          //横向合并
          let star = item.s.c;
          let end = item.e.c;
          for (let i = star; i < end; i++) {
            workbook.Sheets.Sheet1[arr[i + 1] + Number(item.s.r + 1)] = merge_s;
          }
        }
      }
      //将表格数据中的字符串转ArrayBuffer
      function s2ab(s) {
        var buf = new ArrayBuffer(s.length);
        var view = new Uint8Array(buf);
        for (var i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
        return buf;
      }
      //这里的属性可以参考xlsx-style文档
      var wbout = XLSX.write(workbook, {
        bookType: "xlsx",
        bookSST: false,
        type: "binary"
      });
      try {
        FileSaver.saveAs(
          new Blob([s2ab(wbout)], { type: "application/octet-stream" }),
          "数据总览.xlsx"
        );
      } catch (e) {
        if (typeof console !== "undefined") console.log(e, wbout);
      }
    }
    

四、对表格导出中workbook的认识

  1. 见以下代码,workbook就是一个数据对象,其中包含表格的所有数据、以及各种属性,修改其中的各种属性就可以达到修改Excel表格的目的。

  2. workbook对象结构如下,其中的Sheet1是对映Excel文件的第一个Sheet。
    —!cols属性主要用于对列宽的调整;
    —!rows属性是对行的调整;
    —!fullref属性是指Sheet1中的有数据的区域;
    —!merges属性用于记录合并单元格。如{e: {r: 2, c: 0},s: {r: 0, c: 0}}其中的s 指开始单元格,e指结束单元格;r为行号数字,c为列号字母;这里表示的是合并单元格A1到A3;
    —A1: {t: ‘数据类型’, v: ‘内容’, s: {样式}},该属性为单元格属性包含的属性如其中所示,t通常为s即字符串,v指具体表格内容,s保存样式修改s就可以达到修改样式的目的。

  3. 注意:下面的workbook对象中Sheet1中A1过后直接就到A4,而没有A2、A3,这是因为它是!merges中记录的合并单元格A1-A3。所以我们在设置单元格样式时如果仅仅是对Sheet1中存在的单元格设置样式,会导致合并单元格的样式没有。所以在设置样式时我们需要单独对合并单元格进行处理,在Sheet1中添加因为合并而不存在的单元格A2和A3然后设置样式并将内容设置为空,这样就完成了表格样式的修改。

    //workbook对象的具体内容
    {
    Props: {
    	Application: "SheetJS"
    	SheetNames: ['Sheet1']
    	Worksheets: 1}
    SSF: {
    	0: "General"
    	1: "0"
    	...}
    SheetNames: {[
    	"Sheet1"]}
    Sheets: {
    	Sheet1: {
    		!cols: [{wpx: 50}, ... {wpx: 50}]
    		!fullref: "A1:D5"
    		!merges: [{e: {r: 2, c: 0},s: {r: 0, c: 0}}, ... {}]
    		!ref: "A1:D5"
    		!rows: []
    		A1: {t: '数据类型', v: '内容', s: {样式}}
    		A4: {t: 's', v: '', s: {}}
    		...
    		D5: {t: 's', v: '', s: {}}}
    		}
    	}
    }
    

五、单元格样式属性

图片来自官网文档

Logo

前往低代码交流专区

更多推荐