vue实现通用导出(excel,csv, pdf文件)组件 前端导出

后台管理项目中常常使用文件导入导出,故封装了一个通用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.jsvue

 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 }
excel.js
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 的方式主要代码以下
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生成 使用itextsharpios

 

完整js前台代码web

 1 <template>
 2   <div style="float:right;padding-left:5px;">
 3     <Dropdown @on-click="btnExport" placement="bottom-start">
 4       <Button type="primary">导出  5         <Icon type="ios-arrow-down"></Icon>
 6       </Button>
 7       <DropdownMenu slot="list">
 8         <template v-for="(item,index) in exportList" >
 9           <DropdownItem :key="`${_uid}_${index}`" :name="item.ExportID">{{ item.ExportName }}</DropdownItem>
 10         </template>
 11       </DropdownMenu>
 12     </Dropdown>
 13   </div>
 14 </template>
 15 <script>
 16 import json2csv from "json2csv";  17 import excel from "../../Common/libs/excel";  18 export default {  19  data() {  20     return {  21  exportList: [  22  {  23           ExportName: "1-导出Excel",  24           ExportID: "Excel"
 25  },  26  {  27           ExportName: "2-导出Csv",  28           ExportID: "Csv"
 29  },  30  {  31           ExportName: "3-导出Pdf",  32           ExportID: "Pdf"
 33  }  34  ]  35  };  36  },  37  props: {  38  exportData: {  39  type: Array,  40       default: []  41  },  42  isPagination: {  43  type: Boolean,  44       default: false
 45  },  46  exportColumns: {  47  type: Array,  48       default: []  49  },  50  exportFileName: {  51  type: String,  52       default: ""
 53  }  54  },  55  watch: {},  56  computed: {},  57  mounted() {  58     // this.getRPTExportType() 初始化导出类型
 59  },  60  methods: {  61     // 下拉菜单改变时事件
 62  handleExportTypeChange(param) {  63       // change 事件改变的时候提交给父组建 参数 value
 64       this.$emit("myHandleExportTypeChange", param);  65  },  66     // 判断是否IE浏览器
 67  MyBrowserIsIE() {  68       let isIE = false;  69       if (  70         navigator.userAgent.indexOf("compatible") > -1 &&
 71         navigator.userAgent.indexOf("MSIE") > -1
 72  ) {  73         // ie浏览器
 74         isIE = true;  75  }  76       if (navigator.userAgent.indexOf("Trident") > -1) {  77         // edge 浏览器
 78         isIE = true;  79  }  80       return isIE;  81  },  82     //建立a标签下载
 83  createDownLoadClick(content, fileName) {  84       const link = document.createElement("a");  85       link.href = encodeURI(content);  86       link.download = fileName;  87  document.body.appendChild(link);  88  link.click();  89  document.body.removeChild(link);  90  },  91  btnExport(fileType) {  92       debugger;  93       // 若是是分页的
 94       if (this.isPagination == true) {  95         // 向父组件提交一个须要从新传数据的方法 类型name
 96         this.$emit("myHandleRepeatExprot", fileType);  97       } else {  98         // 不分页直接导出
 99         this.exportFile(fileType); 100  } 101  }, 102  exportFile(fileType) { 103       if (this.exportData.length == 0) { 104         this.$Message.error("不容许导出空表格"); 105         return false; 106  } 107       // 循环数组
108       this.exportData.forEach((item, index) => { 109         let temp = {}; 110         // 循环数组中的对象 当传入数据为undefined的时候 赋空
111         Object.keys(item).forEach(function(key) { 112           if (item[key] == undefined) { 113             item[key] == " "; 114             temp[key] = ""; 115           } else { 116             temp[key] = item[key]; 117  } 118  }); 119         this.exportData[index] = temp; 120  }); 121       let titles = []; // 导出内容的中文标题
122       let keys = []; // 导出内容的英文标题
123       this.exportColumns.forEach((item, index) => { 124         if (item.key != "handle") { 125           // 操做列定义为 handle
126  titles.push(item.title); 127  keys.push(item.key); 128  } 129  }); 130       if (fileType == "Excel") { 131         const param = { 132  title: titles, 133  key: keys, 134           data: this.exportData, 135           autoWidth: true, 136           filename: this.exportFileName 137  }; 138  excel.export_array_to_excel(param); 139       } else if (fileType == "Csv") { 140         this.exportCsv( 141           this.exportData, 142           this.exportColumns, 143  keys, 144           this.exportFileName 145  ); 146       } else { 147         // 导出pdf
148         let tempArray = []; 149         let labels = titles.join(","); // title拼接成一个字符串
150         let keys2 = keys.join(","); // 拼接字符串
151         let row0 = this.exportData[0]; //导出数组内容的第一行
152         var rowKeys = []; 153         for (var p1 in row0) { 154           // 数组
155           if (row0.hasOwnProperty(p1)) { 156             rowKeys.push(p1); // table内容的 key
157  } 158  } 159         // keys 和 table的第一列的key比较 取table中不存在的列 为了赋空值
160         var diffArray = keys.filter(key => !rowKeys.includes(key)); 161 
162         let tableData = []; 163         // 循环数组
164         this.exportData.forEach((rowItem, index) => { 165           let temp = rowItem; 166           diffArray.forEach((keyItem, index) => { 167             temp[keyItem] = " "; 168  }); 169  tableData.push(temp); 170  }); 171         this.exportPdfFile(keys2, labels, tableData); 172  } 173  }, 174     // row data里面的每个对象 keys 传入的须要导出列数组
175     // 筛选 须要导出的数据内容
176     /// 例如 keys ["Ordinal","Code"] row 有{Ordinal:1,Code:2,Name:3} 返回新对象{Ordinal:1,Code:2}
177  getRow(row, keys) { 178       let obj = {}; 179       keys.forEach(col => { 180         debugger; 181         let val = row[col]; 182         obj[col] = val; 183  }); 184       return obj; 185  }, 186  exportCsv(data, columns, keys, fileName) { 187       //导出的数据行集合
188       const rows = data.map(t => this.getRow(t, keys)); 189       //导出的数据列标题
190       var fields = []; 191       columns.forEach(t => { 192         if (t.key != "handle") { 193           // 操做列定义为handle
194           let temp = { 195  value: t.key, 196  label: t.title 197  }; 198  fields.push(temp); 199  } 200  }); 201       try { 202         const result = json2csv.parse(rows, { 203  fields: fields, 204           excelStrings: true
205  }); 206         if (this.MyBrowserIsIE()) { 207           // IE10以及Edge浏览器
208           var BOM = "\uFEFF"; 209           var csvData = new Blob([BOM + result], { type: "text/csv" }); 210  navigator.msSaveBlob(csvData, `${fileName}.csv`); 211         } else { 212           let csvContent = "data:text/csv;charset=utf-8,\uFEFF" + result; 213           // 非ie 浏览器
214           this.createDownLoadClick(csvContent, `${fileName}.csv`); 215  } 216       } catch (err) { 217  alert(err); 218  } 219  }, 220  exportPdfFile(valueKeys, labeNames, tableData) { 221       let rexportPdfFile = { 222         ExCode: "DownLoadFile", 223         fileName: this.exportFileName, 224         access_token: this.$cookies.get("access_token"), 225         valueKeys: valueKeys, // 例如 "Product,Version,Description",
226         labeNames: labeNames, // 例如 "产品,版本,描述",
227  tableData: tableData 228  }; 229       //思路 webapi返回二进制的文件流 js 经过Blob接收并转换成pdf文件下载
230       this.$axios({ 231         method: "post", 232         Prefix: "", 233  data: { 234           ExCode: "IRAP_RPT_DownLoadFile", 235           fileName: this.exportFileName, 236           access_token: this.$cookies.get("access_token"), 237           valueKeys: valueKeys, //"Product,Version,Description",
238           labeNames: labeNames, // "产品,版本,描述",
239  tableData: tableData 240  } 241         // responseType:'blob'
242  }) 243         .then(response => { 244           // base64字符串转 byte[]
245           var bstr = atob(response.data.FileInfo), 246             n = bstr.length, 247             u8arr = new Uint8Array(n); 248           while (n--) { 249             u8arr[n] = bstr.charCodeAt(n); 250  } 251           // 转blob
252           var blob = new Blob([u8arr], { 253             type: `application/pdf;charset-UTF-8`
254  }); 255 
256           if (this.MyBrowserIsIE()) { 257             // IE10以及Edge浏览器
258             var BOM = "\uFEFF"; 259             // 传入 Blob 对象
260             navigator.msSaveBlob(blob, `${this.exportFileName}.pdf`); 261           } else { 262             // 非ie 浏览器
263             let content = window.URL.createObjectURL(blob); 264             this.createDownLoadClick(content, `${this.exportFileName}.pdf`); 265  } 266  }) 267         .catch(err => { 268  console.log(err); 269  }); 270  }, 271  getRPTExportType() { 272       this.$axios({ 273         method: "post", 274         Prefix: "", 275  data: { 276           ExCode: "IRAP_RPT_ExportType", 277           access_token: this.$cookies.get("access_token") 278  } 279       }).then(response => { 280           if (response.data.ErrCode == 0) { 281             this.exportList = response.data.Rows 282           } else { 283             this.$Message.error(response.data.ErrText) 284  } 285  }) 286         .catch(err => { 287  console.log(err); 288  }); 289  } 290  } 291 }; 292 </script>
vue通用导出组件

父组件调用代码npm

1 <MyExportType :exportFileName='`test`' v-on:myHandleRepeatExprot="myRepeatExprot"
2             :isPagination="isPagination" :exportData="exportData" :exportColumns="exportColumns" ref="MyExportType"></MyExportType>

 

若是父组件分页的须要导出全部未分页的数据 须要再次调用查询table数据的接口而且给exportData赋值json

 async myRepeatExprot(name) { // 查询全部
        await this.geBTtResult(1) // 调用子组件的导出事件
        await this.$refs.MyExportType.exportFile(name) },

不然 未分页或者导出当前页 直接导出便可 不须要经过父组件调用子组件事件canvas

 第一次写博文,有点紧张和言语不清忘见量,若是此文章对您有些许帮助,请给做者一些鼓励,或者留下您的意见axios

                                                             wexin                                      alipay后端

                                                          

相关文章
相关标签/搜索