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後端

                                                          

相關文章
相關標籤/搜索