公共Hooks封裝報表導出useExportExcel實現詳解
寫在前面
對于經常需要開發(fā)企業(yè)管理后臺的前端開發(fā)來說,必不可少的需要使用表格對于數據進行操作,在對于現有項目進行代碼優(yōu)化時,封裝一些公共的Hooks.
本篇文章為useExportExcel.js
基于個人項目環(huán)境進行封裝的Hooks,僅以本文介紹封裝Hooks思想心得,故相關代碼可能不適用他人
項目環(huán)境
Vue3.x + Ant Design Vue3.x + Vite3.x
對于企業(yè)管理后臺最大的作用來說,用以管理企業(yè)內各種數據狀況,同時,基于實際業(yè)務過程中,客戶每逢年終(中)時都有大型匯報的需求,因此,數據報表形式的文檔產出必不可少,本文則基于該常見需求場景進行封裝的Hooks -- 導出數據報表
封裝思考:報表數據來源
- 后端接口返回數據
后端返回二進制Blob文件,前端利用Blob進行下載,即參考系列文章公共Hooks封裝之文件下載useDownloadFile的方式。
- 前端導出界面數據
前端導出界面數據的方式在企業(yè)管理后臺中占比相對較少,一般用以數據量較少的特殊情況,以自己項目舉例則是 用戶在導入Excel時部分數據失敗后,展示的失敗數據報表及失敗原因統(tǒng)計的表格,使用前端導出數據的方式進行導出。
封裝分解:前端生成報表
接收options
配置對象,包括data(源數據)、key(用來生成表格的行數據唯一標識)、title(表格標題)、fileName(導出文件名稱)
// 通過數組數據前端導出excel const exportByArray = options => { if (!options.data || !options.key || !options.title || !options.fileName) return new Error('缺少必需參數'); const arr = options.data.map(v => options.key.map(j => { return v[j]; }), ); arr.unshift(options.title); const ws = utils.aoa_to_sheet(arr); const colWidth = arr.map(row => row.map(val => { if (val == null) { return { wch: 10 }; } else if (val.toString().charCodeAt(0) > 255) { return { wch: val.toString().length * 2 }; } else { return { wch: val.toString().length }; } }), ); const result = colWidth[0]; for (let i = 1; i < colWidth.length; i++) { for (let j = 0; j < colWidth[i].length; j++) { if (result[j]['wch'] < colWidth[i][j]['wch']) { result[j]['wch'] = colWidth[i][j]['wch']; } } } ws['!cols'] = result; const wb = utils.book_new(); utils.book_append_sheet(wb, ws, options.fileName); writeFile(wb, options.fileName + '.xlsx'); };
前端生成報表方法Sheet.js
前端生成報表方法中用到的utils.aoa_to_sheet
,utils.book_new
,utils.book_append_sheet
,writeFile
,都來源于 SheetJS。
Step1: 項目安裝依賴yarn add xlsx
Step2: 在Hooks文件中引入 import { utils, writeFile } from 'xlsx'
Step3: 參考官方API,完善Hooks中前端導出方法 SheetJS - Utility Functions
- utils.aoa_to_sheet
將一個二維數組轉成sheet,會自動處理number、string、boolean、boolean、date 等類型數據
- utils.table_to_sheet
將一個table的dom直接轉成sheet,會自動識別colspan和rowspan并將其轉成對應的單元格合并
- utils.json_to_sheet
將一個由對象key-value組成的數組轉成sheet,可以設置header
這三種方法都是SheetJS的導出方法,存在差異,考慮實際數據,最后選擇的是utils.aoa_to_sheet
,其余方法可以在官方文檔中找到對應的示例
以上是一個完整的導出報表流程utils.book_new
=> 創(chuàng)建一個工作簿 utils.aoa_to_sheet
=> 源數據轉成工作表 utils.book_append_sheet
=> 將工作表插入到工作簿中 writeFile
=> 調用下載
封裝分解:后端接口返回數據導出優(yōu)化
因為需要請求后端接口導出,即下載返回的二進制文件,依舊考慮用戶體驗設計,增加二次確認彈窗,并從store里拿接口必須的token
// 打開導出文件確認彈窗 const exportByResBlob = options => { Modal.confirm({ title: options.title ? options.title : '導出確認', content: options.content ? options.content : '確認導出報表嗎?', onOk() { downloadFile(options); return Promise.resolve(); }, }); };
useExportExcel.js完整代碼
import { onBeforeUnmount } from 'vue'; import { utils, writeFile } from 'xlsx'; import { stringify } from 'qs'; import { Modal } from 'ant-design-vue'; import { useUserStore } from '@/store/userStore'; export function useExportExcel() { const userStore = useUserStore(); let xhr = null; let downloading = false; // 限制同一文件同時觸發(fā)多次下載 onBeforeUnmount(() => { xhr && xhr.abort(); }); // 打開導出文件確認彈窗 const exportByResBlob = options => { Modal.confirm({ title: options.title ? options.title : '導出確認', content: options.content ? options.content : '確認導出報表嗎?', onOk() { downloadFile(options); return Promise.resolve(); }, }); }; // 通過請求后端接口文件流導出excel const downloadFile = options => { try { if (downloading || !options.url || !options.fileName) return new Error('缺少必需參數'); downloading = true; const paramsStr = stringify(options.params || {}); xhr = new XMLHttpRequest(); xhr.responseType = 'blob'; if (paramsStr) { xhr.open('get', `${options.url}?${paramsStr}`, true); } else { xhr.open('get', options.url, true); } xhr.setRequestHeader('token', userStore.userToken); xhr.onloadend = function (e) { if (e.target.status === 200) { const aElement = document.createElement('a'); const blob = e.target.response; const url = window.URL.createObjectURL(blob); aElement.style.display = 'none'; aElement.href = url; aElement.download = `${options.fileName}.xlsx`; document.body.appendChild(aElement); aElement.click(); if (window.URL) { window.URL.revokeObjectURL(blob); } else { window.webkitURL.revokeObjectURL(blob); } document.body.removeChild(aElement); downloading = false; } }; xhr.send(); } catch (e) { console.error(e); downloading = false; Modal.error({ title: '提示', content: '導出發(fā)生異常,請重試', }); } }; // 通過數組數據前端導出excel const exportByArray = options => { if (!options.data || !options.key || !options.title || !options.fileName) return new Error('缺少必需參數'); const arr = options.data.map(v => options.key.map(j => { return v[j]; }), ); arr.unshift(options.title); const ws = utils.aoa_to_sheet(arr); const colWidth = arr.map(row => row.map(val => { if (val == null) { return { wch: 10 }; } else if (val.toString().charCodeAt(0) > 255) { return { wch: val.toString().length * 2 }; } else { return { wch: val.toString().length }; } }), ); const result = colWidth[0]; for (let i = 1; i < colWidth.length; i++) { for (let j = 0; j < colWidth[i].length; j++) { if (result[j]['wch'] < colWidth[i][j]['wch']) { result[j]['wch'] = colWidth[i][j]['wch']; } } } ws['!cols'] = result; const wb = utils.book_new(); utils.book_append_sheet(wb, ws, options.fileName); writeFile(wb, options.fileName + '.xlsx'); }; return { exportByResBlob, exportByArray, }; }
以上就是公共Hooks封裝報表導出useExportExcel實現詳解的詳細內容,更多關于Hooks封裝useExportExcel的資料請關注腳本之家其它相關文章!
相關文章
解決axios發(fā)送post請求返回400狀態(tài)碼的問題
今天小編就為大家分享一篇解決axios發(fā)送post請求返回400狀態(tài)碼的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08