阅读 832

前端导出Excel,让后端刮目相看(前端导出Excel的方法)

前言

今天再开发中遇到一件事情,就是怎样用查询到的表格导出excel文件?网上有许多方法,最多的就是用数据流的方式。后端给出的方案也是提供一个借口,访问后的到一个文件,然后前端建立虚拟dom进行下载。 我看到网上有说这一块工作也可以前端去实现便使我灵光一闪。

导入依赖

// npm npm install -S file-saver xlsx npm install -D script-loader 复制代码

导入Bolb.js与Export2Excel.js

首先拷贝以下两个文件代码: Export2Excel.js

/* eslint-disable */ require('script-loader!file-saver'); require('script-loader!vendor/Blob'); require('script-loader!xlsx/dist/xlsx.core.min'); 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);     console.log('a')     var oo = generateArray(theTable);     var ranges = oo[1];     /* original data */     var data = oo[0];     var ws_name = "SheetJS";     console.log(data);     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") } function formatJson(jsonData) {     console.log(jsonData) } export function export_json_to_excel(th, jsonData, defaultTitle) {     /* original data */     var data = jsonData;     data.unshift(th);     var ws_name = "SheetJS";     var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);     /* 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'});     var title = defaultTitle || '列表'     saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx") } 复制代码

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(/^\[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)); 复制代码

因为我们在Export2Excel.js中设置的Blob路径为vendor,所以我们要在src下建立相关文件夹并放入这两个js。

G01.png

G02.png

设置导出表格数据

假设我们需要导出一个这样的表格

G03.png

数据

// data dataList: [     { name: "张明", loginTime: 16, id: 1, department: "生产部", sex: "男" },     { name: "小金", loginTime: 11, id: 2, department: "生产部", sex: "女" },     { name: "小凌", loginTime: 21, id: 3, department: "生产部", sex: "男" },     { name: "盖伦", loginTime: 5, id: 4, department: "测试部", sex: "男" } ] 复制代码

html

<table border>       <tr>         <th>ID</th>         <th>名称</th>         <th>登陆次数</th>         <th>部门</th>       </tr>       <tr v-for="item in dataList" :key="item.id">         <td>{{ item.id }}</td>         <td>{{ item.name }}</td>         <td>{{ item.loginTime }}</td>         <td>{{ item.department }}</td>       </tr>     </table>     <button @click="exportExcel">导出信息</button> 复制代码

methods

 // 导出excel exportExcel() {   // 引入文件   const { export_json_to_excel } = require("vendor/Export2Excel.js");   // 表头   const tHeader = ["ID", "名称", "登陆次数", "部门"];   // table表格中对应的属性名   const filterVal = ["id", "name", "loginTime", "department"];   // 表格绑定数据转json   const data = this.formatJson(filterVal, this.dataList);   export_json_to_excel(     tHeader,     data,     "部门登陆信息" + new Date().toLocaleDateString()   ); // 对应下载文件的名字 }, // 导出列表格式化数据的方法 formatJson(filterVal, jsonData) {   return jsonData.map(v => filterVal.map(j => v[j])); } 复制代码

展示效果

G1.gif

到此,前端导出功能实现。这种方式的优势在于:1.不用请求后端获取文件。2.所见即所得,前端可有灵活地把控输出的数据,不用因为导出的数据有差错而拉着后端一起联调。

多sheet导出

我们可能有时候会遇到对Excel多sheet的操作,比如部门的多季度绩效分多sheet导出。

G04.png

G05.png

我们需要在Export2Excel.js中添加一个方法:

/**  * 多sheet导出  * @param {Array} th 表头  * @param {Array} jsonDatas 数据集   * @param {String} defaultTitle 导出的excel名称  * @param {Array} sheetNames sheet名称集  */ export function export_season_to_excel(th, jsonDatas, defaultTitle, sheetNames) {   var wb = new Workbook()   jsonDatas.forEach((item, index) => {     var data = item;     data.unshift(th);     var ws_name = sheetNames[index];     var ws = sheet_from_array_of_arrays(data);     /* 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'   });   var title = defaultTitle || '列表'   saveAs(new Blob([s2ab(wbout)], {     type: "application/octet-stream"   }), title + ".xlsx") } 复制代码

页面中的data内容:

// data 季度绩效数据 seasonDatas: [         [           { name: "张明", score: 72 },           { name: "小金", score: 21 },           { name: "小凌", score: 16 },           { name: "盖伦", score: 84 }         ],         [           { name: "张明", score: 32 },           { name: "小金", score: 54 },           { name: "小凌", score: 45 },           { name: "盖伦", score: 26 }         ],         [           { name: "张明", score: 67 },           { name: "小金", score: 87 },           { name: "小凌", score: 45 },           { name: "盖伦", score: 78 }         ],         [           { name: "张明", score: 54 },           { name: "小金", score: 34 },           { name: "小凌", score: 26 },           { name: "盖伦", score: 34 }         ]       ] 复制代码

html

<div class="season">       <template v-for="(item, index) in seasonDatas">         <div :key="index">           {{ `第${index + 1}季度绩效` }}           <table border>             <tr>               <th>名称</th>               <th>绩效分</th>             </tr>             <tr v-for="(scoreItem, scoreindex) in item" :key="scoreindex">               <td>{{ scoreItem.name }}</td>               <td>{{ scoreItem.score }}</td>             </tr>           </table>         </div>       </template>     </div>     <button @click="exportSeason">导出绩效信息</button> 复制代码

执行方法

     // methods 导出绩效信息excel     exportSeason() {       // 引入文件       const { export_season_to_excel } = require("vendor/Export2Excel.js");       // 表头       const tHeader = ["名称", "绩效分"];       // table表格中对应的属性名       const filterVal = ["name", "score"];       let datas = [];       let sheets = [];       this.seasonDatas.forEach((item, index) => {         // 表格绑定数据转json         datas.push(this.formatJson(filterVal, item));         sheets.push(`第${index + 1}季度绩效`);       });       export_season_to_excel(         tHeader,         datas,         "部门季度绩效" + new Date().toLocaleDateString(),         sheets       ); // 对应下载文件的名字     }, 复制代码

展示效果:

G2.gif

至此,多Sheet导出我们也完成了。


作者:有趣的老凌
链接:https://juejin.cn/post/7030291455243452429


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐