• vue项目+xlsx+xlsx-style 实现table导出为excel的功能——技能提升


    vue项目+xlsx+xlsx-style 实现table导出为excel的功能

    最近遇到一个需求,后端提供一组数据,根据这组数据,导出为一个excel表格。

    步骤如下:

    1. 导出内容的预览如下:包含单元格的合并等操作。

    在这里插入图片描述

    2. 点击右下角导出按钮,可以直接下载一个excel表格。

    在这里插入图片描述
    由于我之前有用过csdn版本的xlsx-style,因此首先想到的就是使用xlsx来实现此功能。

    下面介绍详细步骤:

    1.安装xlsxxlsx-style

    依次安装以下的内容:
    npm install xlsx --save
    npm install xlsx-style --save
    在这里插入图片描述

    2.局部注册使用

    由于我这边是单个页面使用此功能,因此并没有在main.js中全局注册

    局部注册的方法:

    import * as XLSX from 'xlsx';
    import * as XLSX2 from 'xlsx-style';
    
    • 1
    • 2

    注意:一定要重命名其中的方法,就是用as 重命名,因为两个插件导出的名字默认都叫XLSX,如果不进行重命名,则会报错

    3. vue.config.js中需要添加以下配置

    在这里插入图片描述
    如果不添加下面的代码,则运行时,代码会报错。

    config.externals = {
      './cptable': 'var cptable',
    };
    
    • 1
    • 2
    • 3

    4.html预览用table组件来实现(VUE)

    在这里插入图片描述
    我对上面的表格进行了拆分,一个table里面包含了4组
    合并单元格 我使用的是colspan="n"

    <table border="1" width="100%" cellspacing="0" cellpadding="0" id="tableId">
          <thead>
            <tr>
              <th colspan="7" class="headerCls">
                {{ info.saleOrderCode }}采购需求清单
              </th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>计划套数</td>
              <td colspan="2">{{ info.producePlansCount }}</td>
              <td>付款时间</td>
              <td colspan="3">{{ info.lastPaidTime }}</td>
            </tr>
            <tr>
              <td>申请时间</td>
              <td colspan="2">{{ info.applicantTime }}</td>
              <td>申请人员</td>
              <td colspan="3">{{ info.applicantName }}</td>
            </tr>
            <tr>
              <td>计划交期</td>
              <td colspan="2">{{ info.deliveryTime }}</td>
              <td>打印时间</td>
              <td colspan="3">{{ info.printTime }}</td>
            </tr>
          </tbody>
          <thead>
            <tr>
              <th colspan="5" class="leftTitCls">板材需求明细</th>
              <th>板材系数</th>
              <th>1.2</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>序号</td>
              <td>物品名称</td>
              <td>型号规格</td>
              <td>数量</td>
              <td>单位</td>
              <td>到货时间</td>
              <td>备注</td>
            </tr>
            <tr v-for="(item, index) in list1" :key="item.materialName">
              <td>{{ index + 1 }}</td>
              <td>{{ item.materialName }}</td>
              <td>{{ item.specification }}</td>
              <td>{{ item.procurementCount }}</td>
              <td>{{ item.unit }}</td>
              <td>{{ item.arrivalTime }}</td>
              <td>{{ item.remark }}</td>
            </tr>
          </tbody>
          <thead>
            <tr>
              <th colspan="5" class="leftTitCls">塑粉需求明细</th>
              <th>喷涂系数</th>
              <th>0.8</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>序号</td>
              <td>物品名称</td>
              <td>型号规格</td>
              <td>数量</td>
              <td>单位</td>
              <td>到货时间</td>
              <td>备注</td>
            </tr>
            <tr v-for="(item, index) in list2" :key="item.materialName">
              <td>{{ index + 1 }}</td>
              <td>{{ item.materialName }}</td>
              <td>{{ item.specification }}</td>
              <td>{{ item.procurementCount }}</td>
              <td>{{ item.unit }}</td>
              <td>{{ item.arrivalTime }}</td>
              <td>{{ item.remark }}</td>
            </tr>
          </tbody>
          <thead>
            <tr>
              <th colspan="7" class="leftTitCls">外购件需求明细</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>序号</td>
              <td>物品名称</td>
              <td>型号规格</td>
              <td>数量</td>
              <td>单位</td>
              <td>到货时间</td>
              <td>备注</td>
            </tr>
            <tr v-for="(item, index) in list3" :key="item.materialName">
              <td>{{ index + 1 }}</td>
              <td>{{ item.materialName }}</td>
              <td>{{ item.specification }}</td>
              <td>{{ item.procurementCount }}</td>
              <td>{{ item.unit }}</td>
              <td>{{ item.arrivalTime }}</td>
              <td>{{ item.remark }}</td>
            </tr>
          </tbody>
        </table>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108

    5.导出功能(!!!重要)

    数据渲染是通过数组遍历的。
    在这里插入图片描述

    //获取当前时间,用于命名excel表格
    let time = this.moment(new Date()).format('YYYY-MM-DD');
    //获取整个表格的行数。三个数组长度+其他固定行数(10)
    let len = this.list1.length + this.list2.length + this.list3.length + 10;
    this.tableToExcel(
      'tableId',
      `${this.info.saleOrderCode}采购清单需求表${time}`,
      7,
      len,
      1
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5.1 tableToExcel方法——入参(表格id,导出文档名称,列数,行数,表头行数)

    this.tableToExcel( 'tableId', `采购清单需求表${time}`, 7,len, 1);
    
    • 1
    tableToExcel(tableId, fileName, headLength, colsLength, headColsLength) {
          var sheet = XLSX.utils.table_to_sheet(
            document.querySelector('#' + tableId)
          );
          //根据tableId获取到元素,通过XLSX.utils.table_to_sheet 获取到sheet表格属性
          console.log(sheet);
          //下面是固定写法,可以直接照搬
          var arr = [];
          for (let i = 0; i < headLength; i++) {
            if (i < 26) {
              arr.push(String.fromCharCode(65 + i).toUpperCase());
            } else {
              arr.push('A' + String.fromCharCode(65 + (i - 26)).toUpperCase());
            }
          }
          for (let i = 0; i < arr.length; i++) {
          //如果是多级表头的话,设置单元格宽度
            if (headColsLength > 1) {
              for (let j = 0; j < headColsLength; j++) {
                if (j < headColsLength && sheet[arr[i] + j]) {
                  if (
                    !sheet[arr[i] + (j + 1)] ||
                    !sheet[arr[i] + (j - 1)] ||
                    (!sheet[arr[i] + (j - 1)] && !sheet[arr[i] + (j + 1)])
                  ) {
                    sheet['!cols'].push({
                      wch: sheet[arr[i] + j].v.length * 2 + 3,
                    });
                  }
                }
              }
            } else {
            //由于我这边是单个表头,因此走这块代码
              if (!sheet['!cols']) {
                sheet['!cols'] = [];
              }
              //我这边是对 A D E三列要求宽度窄一些,其他的宽一些,主要是根据内容的多少来,也可以根据内容的长度来处理
              if (
                (arr[i] + '1').indexOf('A') > -1 ||
                (arr[i] + '1').indexOf('D') > -1 ||
                (arr[i] + '1').indexOf('E') > -1
              ) {
                sheet['!cols'].push({
                  wch: 8,
                });
              } else {
                sheet['!cols'].push({
                  wch: 14,
                });
              }
            }
    		//由于我这边合并单元格后,导致部分边框不展示了,因此通过这个方法来补充边框。
            sheet = this.addRangeBorder(sheet['!merges'], sheet);
            //如果仅仅用上面的方法,发现还是有缺失的边框,因此下面又进行了一遍处理。添加边框及其他样式的处理。
            for (let key in sheet) {
              if (sheet[key] instanceof Object) {
                sheet[key].s = {
                  border: {
                    top: {
                      style: 'thin',
                    },
                    bottom: {
                      style: 'thin',
                    },
                    left: {
                      style: 'thin',
                    },
                    right: {
                      style: 'thin',
                    },
                  },
                  alignment: {
                    horizontal: 'center',
                    vertical: 'center',
                    wrap_text: true,
                  },
                  font: {
                    sz: 11,
                  },
                  bold: true,
                  numFmt: 0,
                };
              }
            }
          }
          this.downloadExcel(this.sheet2blob(sheet), `${fileName}.xlsx`); //下载
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    5.2 解决合并单元格后部分边框消失问题——addRangeBorder

    addRangeBorder(range, ws) {
          // s:起始位置,e:结束位置
          var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
          range.forEach((item) => {
            var startRowNumber = Number(item.s.r),
              startColumnNumber = Number(item.s.c),
              endColumnNumber = Number(item.e.c),
              endRowNumber = Number(item.e.r);
            console.log(
              'startColumnNumber',
              startColumnNumber,
              endColumnNumber,
              startRowNumber,
              endRowNumber
            );
            // 合并单元格时会丢失边框样式,例如A1->A4 此时内容在A1 而range内获取到的是从0开始的,所以开始行数要+2
            for (var i = startColumnNumber; i <= endColumnNumber + 2; i++) {
              for (var j = startRowNumber; j <= endRowNumber + 2; j++) {
                if (ws[arr[i] + j]) {
                  ws[arr[i] + j].s = {
                    border: {
                      top: { style: 'thin' },
                      left: { style: 'thin' },
                      bottom: { style: 'thin' },
                      right: { style: 'thin' },
                    },
                    alignment: {
                      horizontal: 'center',
                      vertical: 'center',
                      wrap_text: true,
                    },
                    font: {
                      sz: 11,
                    },
                    bold: true,
                    numFmt: 0,
                  };
                } else {
                  ws[arr[i] + j] = {
                    s: {
                      border: {
                        top: { style: 'thin' },
                        left: { style: 'thin' },
                        bottom: { style: 'thin' },
                        right: { style: 'thin' },
                      },
                      alignment: {
                        horizontal: 'center',
                        vertical: 'center',
                        wrap_text: true,
                      },
                      font: {
                        sz: 11,
                      },
                      bold: true,
                      numFmt: 0,
                    },
                    t: '',
                    v: '',
                  };
                }
              }
            }
          });
          return ws;
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    5.3 sheet表格转化为文档流blob

     sheet2blob(sheet, sheetName) {
          sheetName = sheetName || 'sheet1';
          var workbook = {
            SheetNames: [sheetName],
            Sheets: {},
          };
          workbook.Sheets[sheetName] = sheet;
    
          var wopts = {
            bookType: 'xlsx',
            bookSST: false,
            type: 'binary',
          };
          var wbout = XLSX2.write(workbook, wopts);
          var blob = new Blob([this.s2ab(wbout)], {
            type: 'application/octet-stream',
          });
          return blob;
        },
        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;
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    5.4 下载excel文档

     downloadExcel(url, saveName) {
          var blob = new Blob([url], {
            type: 'application/octet-stream',
          });
          var a = document.createElement('a');
          a.id = 'downloadFtsetBtn';
          a.style.display = 'none';
          a.target = '_blank';
          document.body.appendChild(a);
          try {
            var URL = window.URL || window.webkitURL;
            a.href = URL.createObjectURL(blob);
            a.download = saveName;
            if (typeof navigator.msSaveBlob == 'function') {
              //IE
              navigator.msSaveBlob(blob, saveName);
            }
            a.click();
          } catch (e) {
            console.log(e);
          }
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    完成!!!多多积累,多多收获!!!

  • 相关阅读:
    Docker 容器数据卷
    商贸进销存软件怎么选?
    电脑不小心删除的文件怎么恢复?
    Android 11.0 禁用导航栏Recent键(任务键)
    0基础学习VR全景平台篇第119篇:利用蒙版航拍补天 - PS教程
    [Python] 面向对象(一)
    GBase 8s共享内存中的常驻内存段
    C++ 基础面试题总结(一)
    Java---多线程04:线程优先级、守护线程、线程同步、死锁
    02 SpringMVC 参数获取
  • 原文地址:https://blog.csdn.net/yehaocheng520/article/details/127769511