前端匯出Excel,讓後端刮目相看

前言

這幾天在開發中遇到一個事情,做一個匯出的功能,但是後臺是新來的菜鳥,等了三天沒反應,介面做不出來,我內心一萬個。。。。,於是乎我就打算前端做匯出了,網上覆制了一些程式碼就出來了。

匯入依賴

// npmnpm install -S file-saver xlsxnpm 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。

前端匯出Excel,讓後端刮目相看

前端匯出Excel,讓後端刮目相看

設定匯出表格資料

假設我們需要匯出一個這樣的表格

前端匯出Excel,讓後端刮目相看

資料

// datadataList: [ { 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

ID 名稱 登陸次數 部門
{{ item。id }} {{ item。name }} {{ item。loginTime }} {{ item。department }}
複製程式碼

methods

// 匯出excelexportExcel() { // 引入檔案 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]));}複製程式碼

展示效果

前端匯出Excel,讓後端刮目相看

到此,前端匯出功能實現。這種方式的優勢在於:1。不用請求後端獲取檔案。2。所見即所得,前端可有靈活地把控輸出的資料,不用因為匯出的資料有差錯而拉著後端一起聯調。

多sheet匯出

我們可能有時候會遇到對Excel多sheet的操作,比如部門的多季度績效分多sheet匯出。

前端匯出Excel,讓後端刮目相看

前端匯出Excel,讓後端刮目相看

我們需要在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

複製程式碼

執行方法

// 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 ); // 對應下載檔案的名字 },複製程式碼

展示效果:

前端匯出Excel,讓後端刮目相看

至此,多Sheet匯出我們也完成了。

專案地址

專案地址:

https://github。com/FireSmallPanda/vuexDemo。git

存疑解析

大資料匯出和自定義表頭名稱

在文章發出後大家對大資料匯出和怎麼自定義表頭存在疑惑,我這邊設定了一個可以自定義匯出規模的例子。

前端匯出Excel,讓後端刮目相看

html部分

複製程式碼

js部分

// datarows: 100, // 行cols: 100 // 列// methods // 匯出自定義資料outPutBigData() { // 引入檔案 const { export_json_to_excel } = require(“vendor/Export2Excel。js”); // 表頭 let tHeader = []; // table表格中對應的屬性名 let filterVal = []; // 需要匯出的內容 let pushData = []; // 自定義生成列 for (let i = 0; i < this。cols * 1; i++) { tHeader。push(`第${i + 1}列資料`); filterVal。push(i); } // 生成自定義資料 for (let j = 0; j < this。rows * 1; j++) { let pushObj = {}; for (let i = 0; i < this。cols * 1; i++) { // 這邊為展示資料不一致性設定為隨機數 pushObj[i] = Math。random(); } // 插入一條資料 pushData。push(pushObj); } // ——-至此模擬後端請求資料結束——- // 表格繫結資料轉json const data = this。formatJson(filterVal, pushData); export_json_to_excel( tHeader, data, “自定義匯出資料” + new Date()。toLocaleDateString() ); // 對應下載檔案的名字},複製程式碼

演示效果:

為方便大家直觀的看到,我們這次匯出10000行10列的隨機資料。可以看到,其中的列名稱我也是自動生成的。

前端匯出Excel,讓後端刮目相看

作者:有趣的老凌

連結:https://juejin。cn/post/7030291455243452429

著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。