公共Hooks封装报表导出useExportExcel实现详解
写在前面
对于经常需要开发企业管理后台的前端开发来说,必不可少的需要使用表格对于数据进行操作,在对于现有项目进行代码优化时,封装一些公共的Hooks.
本篇文章为useExportExcel.js
基于个人项目环境进行封装的Hooks,仅以本文介绍封装Hooks思想心得,故相关代码可能不适用他人
项目环境
Vue3.x + Ant Design Vue3.x + Vite3.x
对于企业管理后台最大的作用来说,用以管理企业内各种数据状况,同时,基于实际业务过程中,客户每逢年终(中)时都有大型汇报的需求,因此,数据报表形式的文档产出必不可少,本文则基于该常见需求场景进行封装的Hooks -- 导出数据报表
封装思考:报表数据来源
- 后端接口返回数据
后端返回二进制Blob文件,前端利用Blob进行下载,即参考系列文章公共Hooks封装之文件下载useDownloadFile的方式。
- 前端导出界面数据
前端导出界面数据的方式在企业管理后台中占比相对较少,一般用以数据量较少的特殊情况,以自己项目举例则是 用户在导入Excel时部分数据失败后,展示的失败数据报表及失败原因统计的表格,使用前端导出数据的方式进行导出。
封装分解:前端生成报表
接收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
=> 创建一个工作簿 utils.aoa_to_sheet
=> 源数据转成工作表 utils.book_append_sheet
=> 将工作表插入到工作簿中 writeFile
=> 调用下载
封装分解:后端接口返回数据导出优化
因为需要请求后端接口导出,即下载返回的二进制文件,依旧考虑用户体验设计,增加二次确认弹窗,并从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; // 限制同一文件同时触发多次下载 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: '导出发生异常,请重试', }); } }; // 通过数组数据前端导出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的资料请关注猪先飞其它相关文章!
原文出处:https://juejin.cn/post/7170948983219552270
相关文章
基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件功能
这篇文章主要介绍了基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-02-23- 在本篇内容里小编给大家分享的是关于c#使用封装方法以及相关知识点,对此有需要的朋友们可以学习下。...2020-06-25
- 这篇文章主要介绍了vue+element-ui表格封装tag标签使用插槽,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-19
- 这篇文章主要介绍了Vue封装全局过滤器Filters的步骤,通过封装vue全局过滤器实现filters的统一管理。感兴趣的朋友可以了解下...2020-09-16
- 这篇文章主要介绍了封装 axios+promise通用请求函数操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-12
- 这篇文章主要介绍了vue 将多个过滤器封装到一个文件中实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧...2020-09-05
- 这篇文章主要为大家详细介绍了原生JS封装Ajax插件,为大家介绍不同域之间互相请求资源的跨域...2016-05-05
- 这篇文章主要介绍了一百多行代码实现react拖拽hooks,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-21
- 这篇文章主要介绍了Mybatis返回结果封装map过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-09-19
- 这篇文章主要介绍了如何通过Proxy实现JSBridge模块化封装,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-10-23
- 这篇文章主要介绍了WebService 的简单封装接口调用方法,主要是通过简单的sql语句来查询数据库,从而返回dataset,十分简单实用,有需要的小伙伴可以参考下。...2020-06-25
- 这篇文章主要给大家介绍了关于利用unity代码C#封装为dll的相关资料,文中通过图文将实现的方法介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
- 组件(Component)是 Vue.js 最强大的功能之一,组件可以扩展 HTML 元素,封装可重用的代码,这篇文章主要给大家介绍了关于Vue封装全局toast组件,需要的朋友可以参考下...2021-07-13
- vue项目经常会用到axios来请求数据,那么首先肯定需要对这个请求方法进行一个二次封装,这篇文章主要给大家介绍了关于项目中Axios二次封装的相关资料,需要的朋友可以参考下...2021-06-08
- 这篇文章主要介绍了vue 封装面包屑组件教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-11-16
- 这篇文章主要介绍了JavaScript中的封装与继承特性,封装与继承是基于对象编程概念中的基本特性,需要的朋友可以参考下...2016-03-22
- 这篇文章主要给大家介绍了关于vue3封装放大镜组件的相关资料,封装之后,使用起来就更简单了,一个组件一行就可以,文中通过示例代码介绍的非常详细,需要的朋友可以参考下...2021-09-20
- 这篇文章主要为大家详细介绍了原生js实现对Ajax的封装,模仿jquery,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-01-26
- 这篇文章主要介绍了React Hooks常用场景的使用,根据使用场景分别进行举例说明,帮助你认识理解并可以熟练运用 React Hooks 大部分特性,感兴趣的可以了解一下...2021-04-13
- 这篇文章主要介绍了js实现对ajax请求面向对象的封装的相关资料,需要的朋友可以参考下...2016-01-12