Vue+Element 實現excel的匯入匯出

落日長煙發表於2020-08-13

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時,目錄位置要對應

相關文章