• vue blob实现自定义多sheet数据导出到excel文件


    背景:最近vue项目遇到一个需求,就是需要将多个表格分成不同sheet页并导出,之前的工具类只能导出一个sheet页,所以在原有的基础上,调整一下,让它支持多sheet导出。

    1. vue blob文件流,这个肯定要的,这个百度一下都有,下面贴出来
    
    (function (view) {
      view.URL = view.URL || view.webkitURL;
    
      if (view.Blob && view.URL) {
        try {
          new Blob;
          return;
        } catch (e) {}
      }
    
      // Internally we use a BlobBuilder implementation to base Blob off of
      // in order to support older browsers that only have BlobBuilder
      var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
        var
          get_class = function(object) {
            return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
          }
          , FakeBlobBuilder = function BlobBuilder() {
            this.data = [];
          }
          , FakeBlob = function Blob(data, type, encoding) {
            this.data = data;
            this.size = data.length;
            this.type = type;
            this.encoding = encoding;
          }
          , FBB_proto = FakeBlobBuilder.prototype
          , FB_proto = FakeBlob.prototype
          , FileReaderSync = view.FileReaderSync
          , FileException = function(type) {
            this.code = this[this.name = type];
          }
          , file_ex_codes = (
            "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
            + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
          ).split(" ")
          , file_ex_code = file_ex_codes.length
          , real_URL = view.URL || view.webkitURL || view
          , real_create_object_URL = real_URL.createObjectURL
          , real_revoke_object_URL = real_URL.revokeObjectURL
          , URL = real_URL
          , btoa = view.btoa
          , atob = view.atob
    
          , ArrayBuffer = view.ArrayBuffer
          , Uint8Array = view.Uint8Array
        ;
        FakeBlob.fake = FB_proto.fake = true;
        while (file_ex_code--) {
          FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
        }
        if (!real_URL.createObjectURL) {
          URL = view.URL = {};
        }
        URL.createObjectURL = function(blob) {
          var
            type = blob.type
            , data_URI_header
          ;
          if (type === null) {
            type = "application/octet-stream";
          }
          if (blob instanceof FakeBlob) {
            data_URI_header = "data:" + type;
            if (blob.encoding === "base64") {
              return data_URI_header + ";base64," + blob.data;
            } else if (blob.encoding === "URI") {
              return data_URI_header + "," + decodeURIComponent(blob.data);
            } if (btoa) {
              return data_URI_header + ";base64," + btoa(blob.data);
            } else {
              return data_URI_header + "," + encodeURIComponent(blob.data);
            }
          } else if (real_create_object_URL) {
            return real_create_object_URL.call(real_URL, blob);
          }
        };
        URL.revokeObjectURL = function(object_URL) {
          if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
            real_revoke_object_URL.call(real_URL, object_URL);
          }
        };
        FBB_proto.append = function(data/*, endings*/) {
          var bb = this.data;
          // decode data to a binary string
          if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
            var
              str = ""
              , buf = new Uint8Array(data)
              , i = 0
              , buf_len = buf.length
            ;
            for (; i < buf_len; i++) {
              str += String.fromCharCode(buf[i]);
            }
            bb.push(str);
          } else if (get_class(data) === "Blob" || get_class(data) === "File") {
            if (FileReaderSync) {
              var fr = new FileReaderSync;
              bb.push(fr.readAsBinaryString(data));
            } else {
              // async FileReader won't work as BlobBuilder is sync
              throw new FileException("NOT_READABLE_ERR");
            }
          } else if (data instanceof FakeBlob) {
            if (data.encoding === "base64" && atob) {
              bb.push(atob(data.data));
            } else if (data.encoding === "URI") {
              bb.push(decodeURIComponent(data.data));
            } else if (data.encoding === "raw") {
              bb.push(data.data);
            }
          } else {
            if (typeof data !== "string") {
              data += ""; // convert unsupported types to strings
            }
            // decode UTF-16 to binary string
            bb.push(unescape(encodeURIComponent(data)));
          }
        };
        FBB_proto.getBlob = function(type) {
          if (!arguments.length) {
            type = null;
          }
          return new FakeBlob(this.data.join(""), type, "raw");
        };
        FBB_proto.toString = function() {
          return "[object BlobBuilder]";
        };
        FB_proto.slice = function(start, end, type) {
          var args = arguments.length;
          if (args < 3) {
            type = null;
          }
          return new FakeBlob(
            this.data.slice(start, args > 1 ? end : this.data.length)
            , type
            , this.encoding
          );
        };
        FB_proto.toString = function() {
          return "[object Blob]";
        };
        FB_proto.close = function() {
          this.size = this.data.length = 0;
        };
        return FakeBlobBuilder;
      }(view));
    
      view.Blob = function Blob(blobParts, options) {
        var type = options ? (options.type || "") : "";
        var builder = new BlobBuilder();
        if (blobParts) {
          for (var i = 0, len = blobParts.length; i < len; i++) {
            builder.append(blobParts[i]);
          }
        }
        return builder.getBlob(type);
      };
    }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
    '
    运行
    1. Ex2ExcelUtils.js,导出工具类
    require('script-loader!file-saver');
    require('./Blob');
    require('script-loader!xlsx/dist/xlsx.core.min');
    
    /**
     * 支持多sheet
     * @param sheetNames
     * @param ths 表头
     * @param jsonDatas 数据
     * @param defaultTitle
     */
    export function export_data_to_excel(sheetNames, ths, jsonDatas, defaultTitle) {
      var wb = new Workbook()
      for(let index in sheetNames){
        let th = ths[index]
        var data = deepCopy(jsonDatas[index]);
        data.unshift(th);
        var ws_name = sheetNames[index];
    
        var ws = sheet_from_array_of_arrays(data);
        //================表格宽度自适应====================
        /*设置worksheet每列的最大宽度*/
          const colWidth = data.map(row =>
              row.map(val => {
                /*先判断是否为null/undefined*/
                if (val == null || val == undefined || val == "") {
                  return {
                    wch: 10
                  };
                } else if (val.toString().charCodeAt(0) > 255) {
                  /*再判断是否为中文*/
                  return {
                    wch: val.toString().length * 2
                  };
                } else {
                  return {
                    wch: val.toString().length * 1.4
                  };
                }
              })
          );
          /*以主表第二行为初始值,因为我的第一行是表格标题,会比较长,所以以主表第二行为初始值*/
          let result = colWidth[1];
          for (let i = 1; i < colWidth.length; i++) {
            for (let j = 0; j < colWidth[i].length; j++) {
              if (result[j]["wch"] < colWidth[i][j]["wch"]) {
                result[j]["wch"] = colWidth[i][j]["wch"];
              }
            }
          }
          ws["!cols"] = result;
        //====================表格宽度自适应-结束=================
    
        wb.SheetNames.push(ws_name);
        wb.Sheets[ws_name] = ws;
      }
    
      var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
      saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), defaultTitle + ".xlsx")
    }
    
    function datenum(v, date1904) {
      if (date1904) v += 1462;
      var epoch = Date.parse(v);
      return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
    }
    
    function sheet_from_array_of_arrays(data, opts) {
      var ws = {};
      var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
      for (var R = 0; R != data.length; ++R) {
        for (var 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;
          var cell = {v: data[R][C]};
          if (cell.v == null) continue;
          var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
          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 = datenum(cell.v);
          }
          else cell.t = 's';
    
          ws[cell_ref] = cell;
        }
      }
      if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
      return ws;
    }
    
    function Workbook() {
      if (!(this instanceof Workbook)) return new Workbook();
      this.SheetNames = [];
      this.Sheets = {};
    }
    
    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;
    }
    
    1. 上述两个文件,放在同一个目录下,以下是调用示例
    // 文件名
    const excelName = '多sheet页数据导出'
    // 声名每个sheet页的名称
    let sheetNames = ['sheet1', 'sheet2', 'sheet3']
    // 每个sheet页的表头
    let ths = [[], [], []]
    // 每个sheet页的数据内容
    let jsonDatas = [[], [], []]
    
    // sheet1
    ths[0] = ['表头A1', '表头A2', '表头A3', '表头A4']
    jsonDatas[0] = [['值A1', '值A2', '值A3', '值A4'],['值B1', '值B2', '值B3', '值B4']]
    // sheet2
    ths[1] = ['表头A11', '表头A21', '表头A31', '表头A41']
    jsonDatas[1] = [['值A11', '值A21', '值A31', '值A41'],['值B11', '值B21', '值B31', '值B41']]
    // sheet3
    ths[2] = ['表头A12', '表头A22', '表头A32', '表头A42']
    jsonDatas[2] = [['值A12', '值A22', '值A32', '值A42'],['值B12', '值B22', '值B32', '值B42']]
    
    const { export_data_to_excel} = require('./Ex2ExcelUtils')
    export_data_to_excel(sheetNames, ths, jsonDatas, excelName)
    

    最后:前端或后端导出文件,可以看自己需求。数据量比较少的时候,或页面展示的是所有数据的时候,使用前端js直接导出文件,比较方便,因为不用重查接口,减少了交互。若数据量比较大,或页面展示的是分页时,推荐走后端接口导出文件,因为没法拿到完整的数据,而且大数据量从接口返回到前端,对前端负担也不友好。

  • 相关阅读:
    瑞吉外卖项目(二)
    认识HTTP和HTTPS协议
    说一下JVM创建对象的流程?
    Allegro输出带等长规则的Excel操作指导
    Linux-1-冯诺依曼体系
    证书+证书链的简单理解
    LabVIEW项目规划和设计
    【算法】Hidden in Plain Sight
    pyalgotrade量化回测框架简单试用
    【antd】5分钟快速完成antd样式的按需引入以及自定义主题
  • 原文地址:https://blog.csdn.net/BluerCat/article/details/139351262