Vue+Element 實現excel的導入導出
在最近項目中,用到了模板下載與批量上傳的功能,下面是實現步驟(本地環境是@vue/cli 4.3.1+element,vue2.0可能會有報錯,自行調整即可)
一、環境安裝
1、先安裝相關依賴
1 npm install -S xlsx file-saver 2 npm install -D script-loader
2、依賴裝完之后,還需要准備兩個js文件,分別是Blob.js和Export2Excel.js(這兩個文件在文章末尾有源碼),將兩個文件放在項目指定目錄中。我是放在里src目錄下的utils中,放在其他目錄也可以
二、excel文件導出
1、在相應vue文件中,定義觸發導出excel文件的方法
1 <div class="insured-btn-div">
2 <el-button type="primary" size="small" @click="handleExport">模板下載</el-button>
3 </div>
2、再引入Export2Excel.js即可,源碼如下
1 //模板下載
2 handleExport() { 3 require.ensure([], () => { 4 const { export_json_to_excel } = require('@/utils/Export2Excel'); 5 console.log(this.insured); 6 let tHeader = []; 7 if (this.insured.length > 0) { 8 //從insured中提取標題
9 this.insured.forEach(item=>{ 10 tHeader.push(item.name); 11 }) 12 } 13 export_json_to_excel(tHeader, [], '被保險人信息模板'); 14 return false; 15 }) 16 },
注意:我的模板下載是一個只有標題的空excel,而且標題是直接從其他地方獲取的,為了更好理解,再給出一個簡單示例
1 // 下載
2 handleExport() { 3 require.ensure([], () => { 4 const { 5 export_json_to_excel 6 } = require('vendor/Export2Excel'); 7 const tHeader = ['序號', '文章標題', '作者', '閱讀數', '發布時間']; 8 const filterVal = ['id', 'title', 'author', 'views', 'display_time']; 9 const list = [ 10 {id: 1, title: 2, author: 3, pageviews: 4, display_time: 5}, 11 {id: 6, title: 7, author: 8, pageviews: 9, display_time: 10}, 12 {id: 11, title: 12, author: 13, pageviews: 14, display_time: 15}, 13 ]; 14 const data = this.formatJson(filterVal, list); 15 export_json_to_excel(tHeader, data, '列表excel'); 16 }) 17 },
我的模板下載后是這樣的:
簡單示例得到的excel文件是這樣的:由於filterVal中是views,而list中是pageviews,所以得到的閱讀數那一欄都是空的
三、excel文件導出
模板下載之后,按照格式填寫對應的數據,再上傳解析
1、這是觸發文件導出的方法,action="",不上傳服務器,只是解析excel中的數據而已
1 <div class="insured-btn-div">
2 <el-upload 3 class="upload-demo"
4 action=""
5 :on-change="handleChange"
6 :show-file-list="false"
7 accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
8 :auto-upload="false">
9 <el-button size="small" type="primary">批量上傳</el-button>
10 </el-upload>
11 </div>
2、解析excel文件的方法
1 //上傳文件
2 handleChange(file, fileList){ 3 var f = file.raw;// 獲取文件內容
4 // 通過DOM取文件數據
5 var rABS = false; //是否將文件讀取為二進制字符串
6 var reader = new FileReader(); 7 var that = this; 8 //if (!FileReader.prototype.readAsBinaryString) {
9 FileReader.prototype.readAsBinaryString = function(f) { 10 var binary = ""; 11 var rABS = false; //是否將文件讀取為二進制字符串
12 var wb; //讀取完成的數據
13 var outdata; 14 var reader = new FileReader(); 15 reader.onload = function(e) { 16 var bytes = new Uint8Array(reader.result); 17 var length = bytes.byteLength; 18 for(var i = 0; i < length; i++) { 19 binary += String.fromCharCode(bytes[i]); 20 } 21 var XLSX = require('xlsx'); 22 if(rABS) { 23 wb = XLSX.read(btoa(fixdata(binary)), { //手動轉化
24 type: 'base64'
25 }); 26 } else { 27 wb = XLSX.read(binary, { 28 type: 'binary'
29 }); 30 } 31 outdata = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);//outdata就是你想要的東西
32 this.da = [...outdata] //這就是excel文件中的數據
33 // let arr = [];
34 // 下面是數據解析提取邏輯
35 if (that.insured.length > 0) { 36 //首先清空之前的被保人數據
37 that.insureTemplate.id = 0;//將模板id重置為0;
38 that.insureLength = 0; // 將投保人數據長度變為0
39 that.form.insuredTableData = [];//將投保人數據清空
40 for (let v of this.da) { 41 // 判斷是否超出批量投保最大份數
42 if (that.insureTemplate.id >= that.product.batch_bill_max) { 43 break; 44 } 45 let obj = {}; 46 that.insureTemplate.id += 1; 47 obj.id = that.insureTemplate.id; 48 that.insured.forEach(j=>{ 49 if (v.hasOwnProperty(j.name)) { 50 obj[j.field] = v[j.name]; 51 } else { 52 //模板里沒有填的字段,直接賦空值
53 obj[j.field] = ''; 54 } 55 }); 56 // 提取被保人的生日及性別
57 if (obj.hasOwnProperty('insureder_cert_num')) { 58 let res = that.getBirthdayAndGenderByNum(obj.insureder_cert_num); 59 if (res[0] != 0) { 60 if (obj.hasOwnProperty('insureder_birthdate')) { 61 obj.insureder_birthdate = res[0]; 62 } 63 if (obj.hasOwnProperty('insureder_sex')) { 64 if (res[1] == 0) { 65 //注意這個男女ID和后台對應
66 obj.insureder_sex = 49; 67 } else { 68 obj.insureder_sex = 48; 69 } 70 } 71 } 72 } 73 obj.price = '0.00'; 74 that.form.insuredTableData.push(obj); 75 // obj.code = v['被保人姓名']
76 // obj.type = v['被保人手機號']
77 // arr.push(obj)
78 } 79 that.insureLength = that.insureTemplate.id; 80 } 81 // return arr
82 } 83 reader.readAsArrayBuffer(f); 84 } 85
86 if(rABS) { 87 reader.readAsArrayBuffer(f); 88 } else { 89 reader.readAsBinaryString(f); 90 } 91
92 }
這是模板文件中填寫的數據
最后再貼上Blob.js和Export2Excel.js的源碼
Blob.js
1 /* eslint-disable */
2 /* Blob.js 3 * A Blob implementation. 4 * 2014-05-27 5 * 6 * By Eli Grey, http://eligrey.com 7 * By Devin Samarin, https://github.com/eboyjr 8 * License: X11/MIT 9 * See LICENSE.md 10 */
11
12 /*global self, unescape */
13 /*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, 14 plusplus: true */
15
16 /*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
17
18 (function (view) { 19 "use strict"; 20
21 view.URL = view.URL || view.webkitURL; 22
23 if (view.Blob && view.URL) { 24 try { 25 new Blob; 26 return; 27 } catch (e) {} 28 } 29
30 // Internally we use a BlobBuilder implementation to base Blob off of
31 // in order to support older browsers that only have BlobBuilder
32 var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) { 33 var
34 get_class = function(object) { 35 return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; 36 } 37 , FakeBlobBuilder = function BlobBuilder() { 38 this.data = []; 39 } 40 , FakeBlob = function Blob(data, type, encoding) { 41 this.data = data; 42 this.size = data.length; 43 this.type = type; 44 this.encoding = encoding; 45 } 46 , FBB_proto = FakeBlobBuilder.prototype 47 , FB_proto = FakeBlob.prototype 48 , FileReaderSync = view.FileReaderSync 49 , FileException = function(type) { 50 this.code = this[this.name = type]; 51 } 52 , file_ex_codes = ( 53 "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
54 + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
55 ).split(" ") 56 , file_ex_code = file_ex_codes.length 57 , real_URL = view.URL || view.webkitURL || view 58 , real_create_object_URL = real_URL.createObjectURL 59 , real_revoke_object_URL = real_URL.revokeObjectURL 60 , URL = real_URL 61 , btoa = view.btoa 62 , atob = view.atob 63
64 , ArrayBuffer = view.ArrayBuffer 65 , Uint8Array = view.Uint8Array 66 ; 67 FakeBlob.fake = FB_proto.fake = true; 68 while (file_ex_code--) { 69 FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; 70 } 71 if (!real_URL.createObjectURL) { 72 URL = view.URL = {}; 73 } 74 URL.createObjectURL = function(blob) { 75 var
76 type = blob.type 77 , data_URI_header 78 ; 79 if (type === null) { 80 type = "application/octet-stream"; 81 } 82 if (blob instanceof FakeBlob) { 83 data_URI_header = "data:" + type; 84 if (blob.encoding === "base64") { 85 return data_URI_header + ";base64," + blob.data; 86 } else if (blob.encoding === "URI") { 87 return data_URI_header + "," + decodeURIComponent(blob.data); 88 } if (btoa) { 89 return data_URI_header + ";base64," + btoa(blob.data); 90 } else { 91 return data_URI_header + "," + encodeURIComponent(blob.data); 92 } 93 } else if (real_create_object_URL) { 94 return real_create_object_URL.call(real_URL, blob); 95 } 96 }; 97 URL.revokeObjectURL = function(object_URL) { 98 if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { 99 real_revoke_object_URL.call(real_URL, object_URL); 100 } 101 }; 102 FBB_proto.append = function(data/*, endings*/) { 103 var bb = this.data; 104 // decode data to a binary string
105 if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { 106 var
107 str = ""
108 , buf = new Uint8Array(data) 109 , i = 0
110 , buf_len = buf.length 111 ; 112 for (; i < buf_len; i++) { 113 str += String.fromCharCode(buf[i]); 114 } 115 bb.push(str); 116 } else if (get_class(data) === "Blob" || get_class(data) === "File") { 117 if (FileReaderSync) { 118 var fr = new FileReaderSync; 119 bb.push(fr.readAsBinaryString(data)); 120 } else { 121 // async FileReader won't work as BlobBuilder is sync
122 throw new FileException("NOT_READABLE_ERR"); 123 } 124 } else if (data instanceof FakeBlob) { 125 if (data.encoding === "base64" && atob) { 126 bb.push(atob(data.data)); 127 } else if (data.encoding === "URI") { 128 bb.push(decodeURIComponent(data.data)); 129 } else if (data.encoding === "raw") { 130 bb.push(data.data); 131 } 132 } else { 133 if (typeof data !== "string") { 134 data += ""; // convert unsupported types to strings
135 } 136 // decode UTF-16 to binary string
137 bb.push(unescape(encodeURIComponent(data))); 138 } 139 }; 140 FBB_proto.getBlob = function(type) { 141 if (!arguments.length) { 142 type = null; 143 } 144 return new FakeBlob(this.data.join(""), type, "raw"); 145 }; 146 FBB_proto.toString = function() { 147 return "[object BlobBuilder]"; 148 }; 149 FB_proto.slice = function(start, end, type) { 150 var args = arguments.length; 151 if (args < 3) { 152 type = null; 153 } 154 return new FakeBlob( 155 this.data.slice(start, args > 1 ? end : this.data.length) 156 , type 157 , this.encoding 158 ); 159 }; 160 FB_proto.toString = function() { 161 return "[object Blob]"; 162 }; 163 FB_proto.close = function() { 164 this.size = this.data.length = 0; 165 }; 166 return FakeBlobBuilder; 167 }(view)); 168
169 view.Blob = function Blob(blobParts, options) { 170 var type = options ? (options.type || "") : ""; 171 var builder = new BlobBuilder(); 172 if (blobParts) { 173 for (var i = 0, len = blobParts.length; i < len; i++) { 174 builder.append(blobParts[i]); 175 } 176 } 177 return builder.getBlob(type); 178 }; 179 }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
Export2Excel.js
1 /* eslint-disable */
2 require('script-loader!file-saver'); 3 require('@/utils/Blob'); 4 require('script-loader!xlsx/dist/xlsx.core.min'); 5 function generateArray(table) { 6 var out = []; 7 var rows = table.querySelectorAll('tr'); 8 var ranges = []; 9 for (var R = 0; R < rows.length; ++R) { 10 var outRow = []; 11 var row = rows[R]; 12 var columns = row.querySelectorAll('td'); 13 for (var C = 0; C < columns.length; ++C) { 14 var cell = columns[C]; 15 var colspan = cell.getAttribute('colspan'); 16 var rowspan = cell.getAttribute('rowspan'); 17 var cellValue = cell.innerText; 18 if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue; 19
20 //Skip ranges
21 ranges.forEach(function (range) { 22 if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) { 23 for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null); 24 } 25 }); 26
27 //Handle Row Span
28 if (rowspan || colspan) { 29 rowspan = rowspan || 1; 30 colspan = colspan || 1; 31 ranges.push({s: {r: R, c: outRow.length}, e: {r: R + rowspan - 1, c: outRow.length + colspan - 1}}); 32 } 33 ; 34
35 //Handle Value
36 outRow.push(cellValue !== "" ? cellValue : null); 37
38 //Handle Colspan
39 if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null); 40 } 41 out.push(outRow); 42 } 43 return [out, ranges]; 44 }; 45
46 function datenum(v, date1904) { 47 if (date1904) v += 1462; 48 var epoch = Date.parse(v); 49 return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); 50 } 51
52 function sheet_from_array_of_arrays(data, opts) { 53 var ws = {}; 54 var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}}; 55 for (var R = 0; R != data.length; ++R) { 56 for (var C = 0; C != data[R].length; ++C) { 57 if (range.s.r > R) range.s.r = R; 58 if (range.s.c > C) range.s.c = C; 59 if (range.e.r < R) range.e.r = R; 60 if (range.e.c < C) range.e.c = C; 61 var cell = {v: data[R][C]}; 62 if (cell.v == null) continue; 63 var cell_ref = XLSX.utils.encode_cell({c: C, r: R}); 64
65 if (typeof cell.v === 'number') cell.t = 'n'; 66 else if (typeof cell.v === 'boolean') cell.t = 'b'; 67 else if (cell.v instanceof Date) { 68 cell.t = 'n'; 69 cell.z = XLSX.SSF._table[14]; 70 cell.v = datenum(cell.v); 71 } 72 else cell.t = 's'; 73
74 ws[cell_ref] = cell; 75 } 76 } 77 if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range); 78 return ws; 79 } 80
81 function Workbook() { 82 if (!(this instanceof Workbook)) return new Workbook(); 83 this.SheetNames = []; 84 this.Sheets = {}; 85 } 86
87 function s2ab(s) { 88 var buf = new ArrayBuffer(s.length); 89 var view = new Uint8Array(buf); 90 for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; 91 return buf; 92 } 93
94 export function export_table_to_excel(id) { 95 var theTable = document.getElementById(id); 96 console.log('a') 97 var oo = generateArray(theTable); 98 var ranges = oo[1]; 99
100 /* original data */
101 var data = oo[0]; 102 var ws_name = "SheetJS"; 103 console.log(data); 104
105 var wb = new Workbook(), ws = sheet_from_array_of_arrays(data); 106
107 /* add ranges to worksheet */
108 // ws['!cols'] = ['apple', 'banan'];
109 ws['!merges'] = ranges; 110
111 /* add worksheet to workbook */
112 wb.SheetNames.push(ws_name); 113 wb.Sheets[ws_name] = ws; 114
115 var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'}); 116
117 saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx") 118 } 119
120 function formatJson(jsonData) { 121 console.log(jsonData) 122 } 123 export function export_json_to_excel(th, jsonData, defaultTitle) { 124
125 /* original data */
126
127 var data = jsonData; 128 data.unshift(th); 129 var ws_name = "SheetJS"; 130
131 var wb = new Workbook(), ws = sheet_from_array_of_arrays(data); 132
133
134 /* add worksheet to workbook */
135 wb.SheetNames.push(ws_name); 136 wb.Sheets[ws_name] = ws; 137
138 var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'}); 139 var title = defaultTitle || '列表'
140 saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx") 141 }
注意:在Export2Excel.js引入Blob.js時,目錄位置要對應