后台管理项目中经常使用文件导入导出,故封装了一个通用table的导出组件的实现
思路 使用 Dropdown 控件选择导出类型 触发导出
导出的table 数据类型
1 tableColumns: [ 2 { 3 title: '序号', 4 key: 'Ordinal', 5 align: 'center' 6 }, 7 { 8 title: '产品编号', 9 key: 'ProductNo', 10 align: 'left' 11 } 12 ] 13 tableData: [{Ordinal:1,ProductNo:'1234',ProductDesc:'1232222'}]
导出文件大部分情况下是后端处理,有时候我们只需要js处理 该怎么做呢?
1.导出CSV
首先实现导出CSV格式
拼接 csv格式其实就是个纯文本文件,中间使用逗号或者换行符进行拼接
这里使用 json2cvs这个包 需要npm 安装 npm install json2csv -s
下载方式
IE浏览器 不支持a标签进行下载,会打开url 故
对于微软系浏览器(IE和Edge)和非微软系列浏览器采用两种不同的方式进行下载
IE和Edge 采用了 navigator.msSaveBlob 方法 此方法为IE10及以上特有,IE10以下勿采用
非微软浏览器 使用a标签的click事件进行下载
关键代码
1 try { 2 const result = json2csv.parse(rows, { 3 fields: fields, 4 excelStrings: true 5 }); 6 if (this.MyBrowserIsIE()) { 7 // IE10以及Edge浏览器 8 var BOM = "\uFEFF"; 9 // 文件转Blob格式 10 var csvData = new Blob([BOM + result], { type: "text/csv" }); 11 navigator.msSaveBlob(csvData, `${fileName}.csv`); 12 } else { 13 let csvContent = "data:text/csv;charset=utf-8,\uFEFF" + result; 14 // 非ie 浏览器 15 this.createDownLoadClick(csvContent, `${fileName}.csv`); 16 } 17 } catch (err) { 18 alert(err); 19 }
1 //创建a标签下载 2 createDownLoadClick(content, fileName) { 3 const link = document.createElement("a"); 4 link.href = encodeURI(content); 5 link.download = fileName; 6 document.body.appendChild(link); 7 link.click(); 8 document.body.removeChild(link); 9 },
1 // 判断是否IE浏览器 2 MyBrowserIsIE() { 3 let isIE = false; 4 if ( 5 navigator.userAgent.indexOf("compatible") > -1 && 6 navigator.userAgent.indexOf("MSIE") > -1 7 ) { 8 // ie浏览器 9 isIE = true; 10 } 11 if (navigator.userAgent.indexOf("Trident") > -1) { 12 // edge 浏览器 13 isIE = true; 14 } 15 return isIE; 16 },
2.导出Excel类型文件
导出excel借鉴了iview-admin 自带的excel操作js(需要npm安装 xlsx)npm install xlsx -s
需要导出的地方调用excel.export_array_to_excel函数即可
1 const param = { 2 title: titles, 3 key: keys, 4 data: this.exportData, 5 autoWidth: true, 6 filename: this.exportFileName 7 }; 8 excel.export_array_to_excel(param);
完整excel操作js代码如下 excel.js
1 /* eslint-disable */ 2 import XLSX from 'xlsx'; 3 4 function auto_width(ws, data) { 5 /*set worksheet max width per col*/ 6 const colWidth = data.map(row => row.map(val => { 7 /*if null/undefined*/ 8 if (val == null) { 9 return { 10 'wch': 10 11 }; 12 } 13 /*if chinese*/ 14 else if (val.toString().charCodeAt(0) > 255) { 15 return { 16 'wch': val.toString().length * 2 17 }; 18 } else { 19 return { 20 'wch': val.toString().length 21 }; 22 } 23 })) 24 /*start in the first row*/ 25 let result = colWidth[0]; 26 for (let i = 1; i < colWidth.length; i++) { 27 for (let j = 0; j < colWidth[i].length; j++) { 28 if (result[j]['wch'] < colWidth[i][j]['wch']) { 29 result[j]['wch'] = colWidth[i][j]['wch']; 30 } 31 } 32 } 33 ws['!cols'] = result; 34 } 35 36 function json_to_array(key, jsonData) { 37 return jsonData.map(v => key.map(j => { 38 return v[j] 39 })); 40 } 41 42 // fix data,return string 43 function fixdata(data) { 44 let o = '' 45 let l = 0 46 const w = 10240 47 for (; l < data.byteLength / w; ++l) o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w))) 48 o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w))) 49 return o 50 } 51 52 // get head from excel file,return array 53 function get_header_row(sheet) { 54 const headers = [] 55 const range = XLSX.utils.decode_range(sheet['!ref']) 56 let C 57 const R = range.s.r /* start in the first row */ 58 for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */ 59 var cell = sheet[XLSX.utils.encode_cell({ 60 c: C, 61 r: R 62 })] /* find the cell in the first row */ 63 var hdr = 'UNKNOWN ' + C // <-- replace with your desired default 64 if (cell && cell.t) hdr = XLSX.utils.format_cell(cell) 65 headers.push(hdr) 66 } 67 return headers 68 } 69 70 export const export_table_to_excel = (id, filename) => { 71 const table = document.getElementById(id); 72 const wb = XLSX.utils.table_to_book(table); 73 XLSX.writeFile(wb, filename); 74 75 /* the second way */ 76 // const table = document.getElementById(id); 77 // const wb = XLSX.utils.book_new(); 78 // const ws = XLSX.utils.table_to_sheet(table); 79 // XLSX.utils.book_append_sheet(wb, ws, filename); 80 // XLSX.writeFile(wb, filename); 81 } 82 83 export const export_json_to_excel = ({ 84 data, 85 key, 86 title, 87 filename, 88 autoWidth 89 }) => { 90 const wb = XLSX.utils.book_new(); 91 data.unshift(title); 92 const ws = XLSX.utils.json_to_sheet(data, { 93 header: key, 94 skipHeader: true 95 }); 96 if (autoWidth) { 97 const arr = json_to_array(key, data); 98 auto_width(ws, arr); 99 } 100 XLSX.utils.book_append_sheet(wb, ws, filename); 101 XLSX.writeFile(wb, filename + '.xlsx'); 102 } 103 104 // 导出不带有汉字标题的execel内容 105 export const export_array_to_excel = ({ 106 key, 107 data, 108 title, 109 filename, 110 autoWidth 111 }) => { 112 const wb = XLSX.utils.book_new(); 113 const arr = json_to_array(key, data); 114 arr.unshift(title) 115 const ws = XLSX.utils.aoa_to_sheet(arr); 116 if (autoWidth) { 117 auto_width(ws, arr); 118 } 119 XLSX.utils.book_append_sheet(wb, ws, filename); 120 XLSX.writeFile(wb, filename + '.xlsx'); 121 } 122 123 // 导出带有汉字标题的execel内容 124 export const export_array_to_excel2 = ({ 125 key, 126 data, 127 title, 128 filename, 129 autoWidth 130 }) => { 131 const wb = XLSX.utils.book_new(); 132 const arr = json_to_array(key, data); 133 arr.unshift(key) 134 arr.unshift(title) 135 const ws = XLSX.utils.aoa_to_sheet(arr); 136 if (autoWidth) { 137 auto_width(ws, arr); 138 } 139 XLSX.utils.book_append_sheet(wb, ws, filename); 140 XLSX.writeFile(wb, filename + '.xlsx'); 141 } 142 143 export const read = (data, type) => { 144 /* if type == 'base64' must fix data first */ 145 // const fixedData = fixdata(data) 146 // const workbook = XLSX.read(btoa(fixedData), { type: 'base64' }) 147 const workbook = XLSX.read(data, { 148 type: type 149 }); 150 const firstSheetName = workbook.SheetNames[0]; 151 const worksheet = workbook.Sheets[firstSheetName]; 152 const header = get_header_row(worksheet); 153 const results = XLSX.utils.sheet_to_json(worksheet); 154 return { 155 header, 156 results 157 }; 158 } 159 160 export const readesxle = async (file, header, jsointitle) => { 161 return new Promise(function (resolve, reject) { 162 const resultdata = { 163 ErrCode: "9", 164 ErrText: '导入文件格式不正确。', 165 Rows: [] 166 } 167 const fileExt = file.name.split('.').pop().toLocaleLowerCase() 168 if (fileExt === 'xlsx' || fileExt === 'xls') { 169 const reader = new FileReader(); 170 171 const thisXLSX = XLSX; 172 const thisheader = header; 173 const thisjsointitle = jsointitle; 174 reader.readAsArrayBuffer(file) 175 reader.onloadstart = e => {} 176 // reader.onprogress = e => { 177 // this.progressPercent = Math.round(e.loaded / e.total * 100) 178 // } 179 reader.onerror = e => { 180 resultdata.ErrText = '文件读取出错'; 181 resultdata.ErrCode = "1"; 182 resolve(resultdata); 183 } 184 reader.onload = e => { 185 const data = e.target.result 186 const 187 workbook = thisXLSX.read(data, { 188 type: "array" 189 }); 190 let tempFlag = true; 191 192 const firstSheetName = workbook.SheetNames[0]; 193 const worksheet = workbook.Sheets[firstSheetName]; 194 const sheetsheader = get_header_row(worksheet); 195 const sheetarray = thisXLSX.utils.sheet_to_json(worksheet); 196 197 thisheader.forEach((item, index) => { 198 if (sheetsheader.findIndex(x => x == item) == -1) { 199 tempFlag = false 200 } 201 }); 202 if (tempFlag) { 203 let sheetresult = []; 204 for (let i = 0; i < sheetarray.length; i++) { 205 sheetresult.push({}); 206 for (let j = 0; j < thisheader.length; j++) { 207 if (sheetarray[i][thisheader[j]] == undefined || sheetarray[i][thisheader[j]] == null) 208 sheetresult[i][thisjsointitle[j]] = ""; 209 else 210 sheetresult[i][thisjsointitle[j]] = sheetarray[i][thisheader[j]]; 211 } 212 } 213 resultdata.ErrCode = "0"; 214 resultdata.EErrText = "文件导入成功"; 215 resultdata.Rows = sheetresult; 216 } else { 217 resultdata.ErrCode = "1"; 218 resultdata.EErrText = "导入文件格式不正确。"; 219 resultdata.Rows = []; 220 } 221 resolve(resultdata); 222 } 223 } else { 224 resultdata.ErrCode = "1"; 225 resultdata.ErrText = '文件:' + file.name + '不是EXCEL文件,请选择后缀为.xlsx或者.xls的EXCEL文件。'; 226 resolve(resultdata); 227 } 228 }) 229 } 230 231 export default { 232 export_table_to_excel, 233 export_array_to_excel, 234 export_json_to_excel, 235 export_array_to_excel2, 236 read, 237 readesxle 238 }
3.导出pdf
最开始使用jspdf 包
把 需要导出的table使用 canvas生成图片,然后把图片插入pdf内,但是这种方式不容易控制,并且生成的pdf清晰度不高,如果直接写pdf又会产生对中文支持的不友好,后采用前后端配合生成pdf文件并导出
使用blob的方式 后端返回文件流前端 接收并下载
主要代码如下
1 //思路 webapi返回二进制的文件流 js 通过Blob接收并转换成pdf文件下载 2 this.$axios({ 3 method: "post", 4 Prefix: "", 5 data: { 6 ExCode: "IRAP_RPT_DownLoadFile", 7 fileName: this.exportFileName, 8 access_token: this.$cookies.get("access_token"), 9 valueKeys: valueKeys, //"Product,Version,Description", 10 labeNames: labeNames, // "产品,版本,描述", 11 tableData: tableData 12 } 13 // responseType:'blob' 14 }) 15 .then(response => { 16 // base64字符串转 byte[] 17 var bstr = atob(response.data.FileInfo), 18 n = bstr.length, 19 u8arr = new Uint8Array(n); 20 while (n--) { 21 u8arr[n] = bstr.charCodeAt(n); 22 } 23 // 转blob 24 var blob = new Blob([u8arr], { 25 type: `application/pdf;charset-UTF-8` 26 }); 27 28 if (this.MyBrowserIsIE()) { 29 // IE10以及Edge浏览器 30 var BOM = "\uFEFF"; 31 // 传入 Blob 对象 32 navigator.msSaveBlob(blob, `${this.exportFileName}.pdf`); 33 } else { 34 // 非ie 浏览器 35 let content = window.URL.createObjectURL(blob); 36 this.createDownLoadClick(content, `${this.exportFileName}.pdf`); 37 } 38 }) 39 .catch(err => { 40 console.log(err); 41 });
因为公司接口通用规范我这里返回的是文件的base64字符串
如果后台直接返回了二进制的文件流 就不用再次进行转换 并且需要加上responseType:'blob'这句
后台接口采用C# webapi 的方式主要代码如下
vue通用导出组件
public string DownLoadFile(string clientID, string msgFormat, string inParam) { dynamic res = new System.Dynamic.ExpandoObject(); try { dynamic dn = inParam.GetSimpleObjectFromJson(); string token = dn.access_token.ToString(); // 解析Json 字符串中的数组然后 转实体对象 string fileName = dn.fileName; string lbelObj = dn.labeNames; string valueKeyObj = dn.valueKeys; object[] tableObj = dn.tableData; string tableJson = JsonConvert.SerializeObject(tableObj); string[] valueKeys = valueKeyObj.Split(','); string[] labeNames = lbelObj.Split(','); //string[] valueKeys = new string[] { "Product", "Version", "Description" }; ; //string[] labeNames = new string[] { "产品", "版本", "描述" }; ; DataTable tblDatas = new DataTable("Datas"); //string jsonStr = "[{\"Product\":\"1\",\"Version\":\"1\",\"Description\":\"1\"}]"; tblDatas = ToDataTableTwo(tableJson); PDFHelper pdfHelper = new PDFHelper(); byte[] array = pdfHelper.ExportPDF(fileName, labeNames, tblDatas, valueKeys); // 文件byte数组转base64字符串 string str = Convert.ToBase64String(array);; byte[] bt = array.ToArray(); res.ErrCode = 0; res.ErrText = "文件生成成功"; res.FileInfo = str; return JsonConvert.SerializeObject(res); } catch (Exception ex) { //throw new Exception(ex.Message); res.ErrCode = 9999; res.ErrText = "Excel导入异常:" + ex.Message; return JsonConvert.SerializeObject(res); } }
C# pdf生成 使用itextsharp
完整js前台代码
1 2314 154 7 138 for="(item,index) in exportList" > 9 12{{ item.ExportName }} 10 11
父组件调用代码
12 :isPagination="isPagination" :exportData="exportData" :exportColumns="exportColumns" ref="MyExportType">
如果父组件分页的需要导出所有未分页的数据 需要再次调用查询table数据的接口并且给exportData赋值
async myRepeatExprot(name) { // 查询所有 await this.geBTtResult(1) // 调用子组件的导出事件 await this.$refs.MyExportType.exportFile(name) },
否则 未分页或者导出当前页 直接导出即可 不需要通过父组件调用子组件事件
第一次写博文,有点紧张和言语不清忘见量,如果此文章对您有些许帮助,请给作者一些鼓励,或者留下您的意见
wexin alipay