• vue前端导出复杂表头Excel


    vue前端导出复杂表头Excel

    最近因为业务需求,需要做一个导出复杂表头为Excel的功能,需要要导出的页面如下:

    字段实在是太多了,截不完。。。这是一个二级表头的

    1、安装

     npm install file-saver xlsx -S
     npm install script-loader -D
     这是生成excel表格所需要的的依赖
    

    2、在src目录下建立一个vendor的文件夹,里面存放Blob.js和Export2Excel2.js两个JS文件,这两个包的具体内容:

    • Blob.js
    /* eslint-disable */
    /* Blob.js
     * A Blob implementation.
     * 2014-05-27
     *
     * By Eli Grey, http://eligrey.com
     * By Devin Samarin, https://github.com/eboyjr
     * License: X11/MIT
     *   See LICENSE.md
     */
    
    /*global self, unescape */
    /*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
     plusplus: true */
    
    /*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
    
    (function (view) {
      "use strict";
    
      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(/^[objects(.*)]$/)[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));
    
    
    • Export2Excel2.js

      其实就是主要引用了Export2Excel2.js这个文件里面export_json_to_excel方法

    /* eslint-disable */
    import { saveAs } from 'file-saver'
    import XLSX from 'xlsx'
    
    function generateArray(table) {
        var out = [];
        var rows = table.querySelectorAll('tr');
        var ranges = [];
        for (var R = 0; R < rows.length; ++R) {
            var outRow = [];
            var row = rows[R];
            var columns = row.querySelectorAll('td');
            for (var C = 0; C < columns.length; ++C) {
                var cell = columns[C];
                var colspan = cell.getAttribute('colspan');
                var rowspan = cell.getAttribute('rowspan');
                var cellValue = cell.innerText;
                if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
    
                //Skip ranges
                ranges.forEach(function (range) {
                    if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
                    for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
                    }
                });
    
                //Handle Row Span
                if (rowspan || colspan) {
                    rowspan = rowspan || 1;
                    colspan = colspan || 1;
                    ranges.push({
                    s: {
                        r: R,
                        c: outRow.length
                    },
                    e: {
                        r: R + rowspan - 1,
                        c: outRow.length + colspan - 1
                    }
                    });
                };
    
                //Handle Value
                outRow.push(cellValue !== "" ? cellValue : null);
    
                //Handle Colspan
                if (colspan)
                    for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
            }
            out.push(outRow);
        }
        return [out, ranges];
    };
    
    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;
    }
    
    export function export_table_to_excel(id) {
        var theTable = document.getElementById(id);
        var oo = generateArray(theTable);
        var ranges = oo[1];
    
        /* original data */
        var data = oo[0];
        var ws_name = "SheetJS";
    
        var wb = new Workbook(),
            ws = sheet_from_array_of_arrays(data);
    
        /* add ranges to worksheet */
        // ws['!cols'] = ['apple', 'banan'];
        ws['!merges'] = ranges;
    
        /* add worksheet to workbook */
        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"
        }), "test.xlsx")
    }
    // 对此方法进行修改,如下:
    export function export_json_to_excel({
        multiHeader2 = [],	// 第一行表头
        multiHeader = [], // 第二行表头
        header,	// 第三行表头
        data,
        filename, //文件名
        merges = [], // 合并
        autoWidth = true,
        bookType = 'xlsx'
    } = {}) {
        /* original data */
        filename = filename || '列表';
        data = [...data]
        data.unshift(header);
    
        for (let i = multiHeader2.length - 1; i > -1; i--) {
            data.unshift(multiHeader2[i])
        }
    
        for (let i = multiHeader.length - 1; i > -1; i--) {
            data.unshift(multiHeader[i])
        }
    
        var ws_name = "SheetJS";
        var wb = new Workbook(),
            ws = sheet_from_array_of_arrays(data);
    
        if (merges.length > 0) {
            if (!ws['!merges']) ws['!merges'] = [];
                merges.forEach(item => {
                ws['!merges'].push(XLSX.utils.decode_range(item))
            })
        }
    
        if (autoWidth) {
            /*设置worksheet每列的最大宽度*/
            const colWidth = data.map(row => row.map(val => {
            /*先判断是否为null/undefined*/
                if (val == null) {
                    return {
                        'wch': 10
                    };
                }
                /*再判断是否为中文*/
                else if (val.toString().charCodeAt(0) > 255) {
                    return {
                        'wch': val.toString().length * 2
                    };
                } else {
                    return {
                        'wch': val.toString().length
                    };
                }
            }))
            /*以第一行为初始值*/
            let result = colWidth[0];
            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;
        }
    
        /* add worksheet to workbook */
        wb.SheetNames.push(ws_name);
        wb.Sheets[ws_name] = ws;
    
        var wbout = XLSX.write(wb, {
            bookType: bookType,
            bookSST: false,
            type: 'binary'
        });
        saveAs(new Blob([s2ab(wbout)], {
            type: "application/octet-stream"
        }), `${filename}.${bookType}`);
    }
    

    3. 在你要导出excel文件的vue组件中添加两个方法

    handleExportMarketingList()formatJson,再将导出的按钮添加一个点击事件,绑定handleExportMarketingList

    <div style="margin-left: 20px;margin-top: 6px; margin-bottom: 6px; ">
          <el-button type="info" size="small" @click="handleExportMarketingList">Export</el-button>
        </div>
    
    // 导出复杂表头
        handleExportMarketingList() {
          require.ensure([], () => {
            const {export_json_to_excel} = require('@/vendor/Export2Excel2');
    		//multiHeader数组里面添加的是一级表头的名称,要按顺序添加每个表头的的名称,如果遇到横向合并单元格的地方,合并了多少个单元格,就要留几个空字符串占位
            const multiHeader = [
              [
                'Project ID#', 'Project/Product Name', 'Customer', 'Product Type', 'Project Type', 'Resource Availability', 'System Owner',
                'Architecture', 'Form Factor', 'Platform', 'When To Start', 'Project Complete Target', 'Project Total Expense', '', '', '',
                'FY2022 (K$)--Q1 FCST', '', '', '', 'FY2022 (K$)--Q2 FCST', '', '', '', 'FY2022 (K$)--Q3 FCST', '', '', '', 'FY2022 (K$)--Q4 FCST',
                '', '', '', 'FY2022 (K$)--FY2022 Outlook', '', '', '', 'Actual Spending Before FY2022', '', '', '', 'Spending After FY2022', '', '', '', 'Comment',
              ],
            ];
         //tHeader数组里面放的是二级表头的名称,也要按顺序添加每个表头的的名称,添加的时候要注意,如果是一级表头上下单元格合并地方,要用空字符串补上占位。
            const tHeader = [
              '', '', '', '', '', '', '', '', '', '', '', '', 'Material', 'NRE', 'Cert', 'TTL', 'Material', 'NRE', 'Cert', 'TTL',
              'Material', 'NRE', 'Cert', 'TTL', 'Material', 'NRE', 'Cert', 'TTL', 'Material', 'NRE', 'Cert', 'TTL', 'Material', 'NRE', 'Cert', 'TTL',
              'Material', 'NRE', 'Cert', 'TTL', 'Material', 'NRE', 'Cert', 'TTL', '',
            ];
    	//filterVal数组里面放的是表头title对应的字段名称
            const filterVal = ['id', 'name', 'customer', 'product_type', 'project_type', 'resource_availability', 'system_owner',
              'architecture', 'form_factor', 'platform', 'when_to_start', 'project_complete_target', 'total_material', 'total_nre',
              'total_cert', 'total_ttl', 'q1_material', 'q1_nre', 'q1_cert', 'q1_ttl', 'q2_material', 'q2_nre', 'q2_cert', 'q2_ttl',
              'q3_material', 'q3_nre', 'q3_cert', 'q3_ttl', 'q4_material', 'q4_nre', 'q4_cert', 'q4_ttl', 'outlook_material',
              'outlook_nre', 'outlook_cert', 'outlook_ttl', 'actual_material', 'actual_nre', 'actual_cert', 'actual_ttl', 'spending_material',
              'spending_nre', 'spending_cert', 'spending_ttl', 'comment',
            ];
    // this.cash是后端返回来的数组,用map方法遍历,这里你可以改你想要的数据,最后用return将每个字段一次返回就好啦
            const list = this.cash.map((item, key) => {
              var product_type = '';
              if (item.product_type === 0) {
                this.product_type = 'Sustaining' || null
              } else if (item.product_type === 1) {
                this.product_type = 'NPI'
              } else if (item.product_type === 2) {
                this.product_type = 'Proposal'
              } else if (item.product_type === 3) {
                this.product_type = 'POC'
              } else {
                this.product_type = null
              }
              var project_type = '';
              if (item.project_type === 0) {
                this.project_type = 'Ongoing'
              } else if (item.project_type === 1) {
                this.project_type = 'Evaluating'
              } else if (item.project_type === 2) {
                this.project_type = 'Upcoming'
              } else {
                this.project_type = null
              }
              var resource_availability = '';
              if (item.resource_availability === 0) {
                this.resource_availability ='Staffed'
              } else if (item.resource_availability === 1) {
                this.resource_availability ='Un-staffed'
              } else if (item.move_resource_availability === 2) {
                this.resource_availability = 'Cancel'
              } else if (item.move_resource_availability === 3) {
                this.resource_availability = 'Done'
              }else {
                this.resource_availability = null
              }
    
              return {
                id: item.id || null,
                name: item.name || null,
                customer: item.customer || null,
                product_type: this.product_type || null,
                project_type: this.project_type || null,
                resource_availability: this.resource_availability || null,
                system_owner: item.system_owner || null,
                architecture: item.architecture || null,
                form_factor: item.form_factor || null,
                platform: item.platform || null,
                when_to_start: item.when_to_start || null,
                project_complete_target: item.project_complete_target || null,
                total_material: item.total_material || null,
                total_nre: item.total_nre || null,
                total_cert: item.total_cert || null,
                total_ttl: item.total_ttl || null,
                q1_material: item.q1_material || null,
                q1_nre: item.q1_nre || null,
                q1_cert: item.q1_cert || null,
                q1_ttl: item.q1_ttl || null,
                q2_material: item.q2_material || null,
                q2_nre: item.q2_nre || null,
                q2_cert: item.q2_cert || null,
                q2_ttl: item.q2_ttl || null,
                q3_material: item.q3_material || null,
                q3_nre: item.q3_nre || null,
                q3_cert: item.q3_cert || null,
                q3_ttl: item.q3_ttl || null,
                q4_material: item.q4_material || null,
                q4_nre: item.q4_nre || null,
                q4_cert: item.q4_cert || null,
                q4_ttl: item.q4_ttl || null,
                outlook_material: item.outlook_material || null,
                outlook_nre: item.outlook_nre || null,
                outlook_cert: item.outlook_cert || null,
                outlook_ttl: item.outlook_ttl || null,
                actual_material: item.actual_material || null,
                actual_nre: item.actual_nre || null,
                actual_cert: item.actual_cert || null,
                actual_ttl: item.actual_ttl || null,
                spending_material: item.spending_material || null,
                spending_nre: item.spending_nre || null,
                spending_cert: item.spending_cert || null,
                spending_ttl: item.spending_ttl || null,
                comment: item.comment || null,
    
              }
              });
            // merges是进行所有表头的单元格合并,数组里面的每个元素的填写规则是:按从左往右的顺序依次填写,字母是指excel里面的第几列,数字表示第几行,如果是上下的单元格合并,就是按从上往下填写,比如'A1:A2'代表的是A列的第一个单元格和A列的第二个单元格合并,如果是左右的单元格合并,也是从左往右填写,同理,
            const merges = [
              'A1:A2', 'B1:B2', 'C1:C2', 'D1:D2', 'E1:E2', 'F1:F2', 'G1:G2', 'H1:H2', 'I1:I2', 'J1:J2', 'K1:K2', 'L1:L2', 'M1:P1',
              'Q1:T1', 'U1:X1', 'Y1:AB1', 'AC1:AF1', 'AG1:AJ1', 'AK1:AN1', 'AO1:AR1', 'AS1:AS2'
            ];
            if(list) {
              const data = this.formatJson(filterVal, list); // 生成json数据
              export_json_to_excel({
              multiHeader, // 这里是第一行的表头
              header: tHeader, // 这里是第二行的表头
              data,
              filename: 'Cash plan',  //这里填的是导出excel的名称
              merges,
            });
            } else {
              alert("暂无无数据");
            }
          });
        },
         formatJson(filterVal, jsonData) {
          return jsonData.map(v => filterVal.map(j => v[j]))
        },
    

    4. 导出的excel表格长这样,撒花 ~

    一张图截不下,只能截两次了,大概就是这样。。

  • 相关阅读:
    ‘Host’ is not allowed to connect to this mysql server
    centos7安装mysql
    further configuration avilable 不见了
    Dynamic Web Module 3.0 requires Java 1.6 or newer
    hadoop启动 datanode的live node为0
    ssh远程访问失败 Centos7
    Linux 下的各种环境安装
    Centos7 安装 python2.7
    安装scala
    Centos7 安装 jdk 1.8
  • 原文地址:https://www.cnblogs.com/maqian/p/14116509.html
Copyright © 2020-2023  润新知