公共Hooks封装报表导出useExportExcel实现详解
作者:JasonSubmara 发布时间:2024-04-28 09:21:36
写在前面
对于经常需要开发企业管理后台的前端开发来说,必不可少的需要使用表格对于数据进行操作,在对于现有项目进行代码优化时,封装一些公共的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,
};
}
来源:https://juejin.cn/post/7170948983219552270


猜你喜欢
- 在使用pymongo时遇到了一个小坑:在Flask框架中,将字典插入mongodb后再返回就报错@app.route('xxxx
- 问题: jsp中想要输出的中文被显示成“?” 解决方法 : 在eclipse-windows- preferences中 搜索jsp , E
- 本文介绍一种将一个大的文本文件分割成多个小文件的方法方法一:1.读取文章所有的行,并存入列表中2.定义分割成的小文本的行数3.将原文本内容按
- 著名的老掉牙的IE6.0在我这里已经有六年工龄了,前几天朋友拿到个IE8.0新的Beta版本,我的Sever2003装不上,大为扫兴。Chr
- 相信很多朋友在用Python写完代码之后都迫不及待的想发给对象交流(装X),但是发源码又要求对方有对应的解释器,一般是行不通的,所以我们要把
- os.stat(path) :用于在给定的路径上执行一个系统 stat 的调用。path:指定路径返回值:st_mode: inode 保护
- 1.需求说明记录一下项目对用户 UGC 文本进行字数限制的具体实现。不同的产品,出于种种原因,一般都会对用户输入的文本内容做字数限制。出于产
- 一、运算符算术运算符:+ - * / 可以在select 语句中使用连接运算符:|| select deptno|| dname from
- 前言:我们在打印一些 PDF 文件的时候可能会遇见加密不能打印的情况,需要提供密码才能打印。如果直接在浏览器中浏览 PDF 文件,它不能调取
- 高斯模糊的介绍与原理通常,图像处理软件会提供"模糊"(blur)滤镜,使图片产生模糊的效果。"模糊"
- 关联2张表时出现了无法创建外键的情况,从这个博客看到,问题出在第六点的Charset和Collate选项在表级和字段级上的一致性上。我的2张
- 本文实例讲述了Python使用matplotlib实现交换式图形显示功能。分享给大家供大家参考,具体如下:一 代码from random i
- 方法一:<code class="language-python">""" 根
- 一、基于socket实现的TCP客户端import socket # 建立socket对象# 参数一表示IP地址类型(AF_INE
- Python有许多吸引力,如效率,代码可读性和速度,使其成为数据科学爱好者的首选编程语言。Python通常是希望升级其应用程序功能的数据科学
- MSDN上看了一下说是sql server 2005不支持在分布式事务处理中存在指向本地的链接服务器(环回链接服务器)个人尝试了下是由于在双
- 本文实例讲述了python实现的简单文本类游戏实现方法。分享给大家供大家参考。具体实现方法如下:######################
- 原来看过MYSQL同步数据的实现,可是自己还没有动过手,今天没什么事就玩一玩,正好在旁边有另一台空电脑,都在同一个路由器下。哈哈,正好。 不
- 最近发现一个问题,是关于IDEA的一些骚操作的事儿~具体怎么回事,一起来看看。我们都知道使用git分布式版本控制工具,提、拉 代码都会有一个
- 求和try: while True: n=input() s=1 for x in raw_input(