当前位置:首页 > 技术分析 > 正文内容

Vue3 exceljs库实现前端导入导出Excel

ruisui883个月前 (01-24)技术分析18

前言

需求场景

最近在开发项目时需要批量导入和导出Excel数据,在实现这个需求时,我们既可以在前端完成数据解析和文件生成工作,也可以通过前端发起导入以及导出请求后,后端实现解析文件流解析文件内容以及生成文件并提供下载链接的功能。

相较于后端处理Excel数据而言,使用前端导入导出可以提供更高的实时性,用户可以直接在浏览器中触发导出操作,无需等待后端处理。且可以在前端完成数据生成以及数据校验处理工作,大大减轻后端服务器的压力,大幅增强用户体验。

具体的技术方案选型主要看业务场景,如果对于小型数据集、实时性需求较高的导入导出操作,优先考虑前端实现。而对于大型数据集、需要业务逻辑处理、以及安全性要求高的场景,则后端处理更为合适。

技术方案

xlsx与xlsx-style组合方案:xlsx 是目前前端最常用的Excel解决方案,又叫做SheetJS,但社区版不支持修改Excel的样式,需要购买Pro版才可以,如果需要修改导出的Excel文件样式,需要结合xlsx-style库一起使用。但遗憾的是xlsx库已经两年多不更新,而xlsx-style上一个版本更是8年前发布,目前已经不再推荐使用该方案。

exceljs与file-saver方案:exceljs是一款免费开源支持导入导出Excel 操作工具,并且可以实现样式的修改以及 Excel 的高级功能,是非常值得推荐的一个处理 Excel 的库,file-saver可以实现保存文件到本地。本文以exceljs与file-saver操作xlsx格式文件为例介绍如何具体上手使用。

exceljs介绍

ExcelJS是一个用于在Node.js和浏览器中创建、读取和修改Excel文件的强大JavaScript库。它提供了丰富的功能和灵活的API,使你能够在你的应用程序中处理和操作Excel文件。

下面是一些ExcelJS库的关键特性和功能:

  1. 创建和修改Excel文件:ExcelJS允许你创建新的Excel工作簿,并在其中添加工作表、行和单元格。你可以设置单元格的值、样式、数据类型以及其他属性。
  2. 读取和解析Excel文件:ExcelJS支持读取和解析现有的Excel文件。你可以将Excel文件加载到工作簿中,然后访问工作表、行和单元格的数据。
  3. 导出和保存Excel文件:ExcelJS可以将工作簿保存为Excel文件,支持多种格式,如XLSX、XLS和CSV。你可以将工作簿保存到本地文件系统或将其发送到客户端以供下载。
  4. 处理复杂的Excel功能:ExcelJS支持处理复杂的Excel功能,如公式、图表、数据验证、条件格式和保护工作表等。你可以根据需要设置和操作这些功能。
  5. 支持自定义样式和格式:ExcelJS允许你自定义单元格、行、列和工作表的样式和格式。你可以设置字体、颜色、填充、边框、对齐方式以及数字和日期格式等。

参考文档

npm仓库地址:
https://www.npmjs.com/package/exceljs

官方中文文档地址:
https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md

快速上手

安装依赖

exceljs用于Excel数据处理,file-sever用于保存到本地文件。

npm i exceljs
npm i file-saver

导出Excel

让我们先从简单的数据导出开始,快速体验如何使用exceljs导出Excel文件,需要注意的是在浏览器环境中运行 JavaScript,浏览器的安全策略通常不允许直接访问读写本地文件系统。在这种情况下,需要通过其他方式将文件转换为buffer数据,在导出Excel时使用FileSaver.js库将缓冲区数据保存到文件中。





当我们点击导出excel按钮时,调用exportFile函数,完成excel文件下载,下载后的文件内容如下:


导入Excel

导入excel文件时,同样使用FileReader的readAsArrayBuffer方法,将文件转换为二进制字符串,然后从buffer中加载数据并解析。





上传文件后,解析内容如下所示:

进阶操作

添加数据

我们可以通过columns方法添加列标题并定义列键和宽度,设置好表头后,我们可以直接通过addRow方法,根据key值去添加每一行的数据。

参考文档:
https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#%E5%88%97

完整代码如下:





添加数据后导出文件效果如下:

读取数据

我们可以使用getRow方法,传入指定行参数读取行数据。

使用getColumn方法,传入键、字母、id参数读取列数据。

使用eachCell方法可以遍历每个单元格内容。

参考文档:
https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#%E8%A1%8C

代码如下:





效果

样式

在导出excel文件时,默认没有任何样式的,为了美观我们需要添加样式,而exceljs支持修改表格样式,具体内容可参考文档
https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#%E6%A0%B7%E5%BC%8F

例如,我们需要设置所有单元格居中对齐,并添加边框。并分别指定标题行和内容行字体大小、背景颜色、行高属性,代码如下:





导出Excel样式效果如下所示,已经成功按我们指定的样式导出了文件:

筛选

在很多的时候我们需要对表格中每一列的数据进行筛选,比如直接筛选姓名等列信息,我们可以通过 autoFilter 来添加筛选。参考文档:
https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#
%E8%87%AA%E5%8A%A8%E7%AD%9B%E9%80%89%E5%99%A8

代码如下:





导入后的效果如下,在姓名、年龄、身高列添加了筛选按钮:

公式值

参考文档:exceljs/README_zh.md at
5bed18b45e824f409b08456b59b87430ded023ab · exceljs/exceljs · GitHub

我们可以直接对表格中的数据进行公式计算,比如 求和(SUM)平均数(AVERAGE) 等。

例如我们需要计算平均值、最大值、指定公式时,代码如下:





导出Excel文件效果如下,E列已经自动替换为公式计算。

合并单元格

表格的合并应该是业务需求中最频繁的功能。当然这一功能使用 xlsx 也可以实现,我们只需要使用mergeCells方法,传入合并单元格范围参数即可。

参考文档:
https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#
%E5%90%88%E5%B9%B6%E5%8D%95%E5%85%83%E6%A0%BC

具体代码实现如下所示:






单元格合并后导出文件效果如下:

数据验证

有时候我们需要为某个单元格添加数据可以方便直接下拉选择指定的值,此时就需要使用数据验证功能,传入可填写的选项列表。

参考文档:
https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#
%E6%95%B0%E6%8D%AE%E9%AA%8C%E8%AF%81

例如我们对是否注册列添加数据验证,可填值为"是、否、未知",具体代码如下:






导出的excel文件效果如下:

条件格式化

我们可以为指定单元格添加条件格式,对满足条件的单元格设置指定的样式。

参考文档:exceljs/README_zh.md at
5bed18b45e824f409b08456b59b87430ded023ab · exceljs/exceljs · GitHub

例如为年龄大于18岁单元格进行红色标注,代码如下:





导出后的文件效果如下:

封装exceljs

封装导入导出函数

为了提高项目代码的复用性,通常会将excel导入导出功能封装到单独的函数中方便调用,封装后的函数如下:

import ExcelJS from "exceljs";
import FileSaver from "file-saver";
import {timeFile} from "@/utils/timeFormat";
// 导出excel文件
export function exportFile(export_data, filename) {
    // 创建工作簿
    const workbook = new ExcelJS.Workbook();
    // 添加工作表,名为sheet1
    const sheet1 = workbook.addWorksheet("sheet1");
    // 获取表头所有键
    const headers = Object.keys(export_data[0])
    // 将标题写入第一行
    sheet1.addRow(headers);
    // 将数据写入工作表
    export_data.forEach((row) => {
        const values = Object.values(row)
        sheet1.addRow(values);
    });
    // 设置默认宽高属性
    sheet1.properties.defaultColWidth = 20
    sheet1.properties.defaultRowHeight = 20
    // 修改所有单元格样式
    // 遍历每一行
    sheet1.eachRow((row, rowNumber) => {
        // 遍历每个单元格
        row.eachCell((cell) => {
            // 设置边框样式
            cell.border = {
                top: {style: 'thin'},
                left: {style: 'thin'},
                bottom: {style: 'thin'},
                right: {style: 'thin'}
            };
            // 设置居中对齐
            cell.alignment = {
                vertical: 'middle',
                horizontal: 'center'
            };
        });
    });
    // 获取标题行数据
    const titleCell = sheet1.getRow(1);
    // 设置标题行单元格样式
    titleCell.eachCell((cell) => {
        // 设置标题行背景颜色
        cell.fill = {
            type: 'pattern',
            pattern: 'solid',
            fgColor: {argb: '3498db'}
        };
        // 设置标题行字体
        cell.font = {
            bold: true,// 字体粗体
        };
    })
    // 导出表格文件
    workbook.xlsx.writeBuffer().then((buffer) => {
        let file = new Blob([buffer], {type: "application/octet-stream"});
        FileSaver.saveAs(file, filename + timeFile() + ".xlsx");
    }).catch(error => console.log('Error writing excel export', error))
}

// 导入excel文件
export function importFile(content) {
    return new Promise((resolve, reject) => {
        // 创建一个空的JavaScript对象数组,用于存储解析后的数据
        const data = [];
        //创建Workbook实例
        const workbook = new ExcelJS.Workbook();
        workbook.xlsx.load(content).then(workbook => {
            // 获取第一个worksheet内容
            const worksheet = workbook.getWorksheet(1);
            // 获取第一行的标题
            const headers = [];
            worksheet.getRow(1).eachCell((cell) => {
                headers.push(cell.value);
            });
            // console.log("headers", headers)
            // 遍历工作表的每一行(从第二行开始,因为第一行通常是标题行)
            for (let rowNumber = 2; rowNumber <= worksheet.rowCount; rowNumber++) {
                const rowData = {};
                const row = worksheet.getRow(rowNumber);
                // 遍历当前行的每个单元格
                row.eachCell((cell, colNumber) => {
                    // 获取标题对应的键,并将当前单元格的值存储到相应的属性名中
                    rowData[headers[colNumber - 1]] = cell.value;
                });
                // 将当前行的数据对象添加到数组中
                data.push(rowData);
            }
            // console.log("data", data)
            resolve(data);
        }).catch(error => {
            reject(error);
        });
    })
}

vue组件调用

以element plus为例,调用函数完成Excel文件导入与导出,代码如下:





页面效果

封装后的页面效果如下,至此,一个简单的vue前端实现Excel文件导入导出功能便开发完成了。

完整代码

gitee:https://gitee.com/cuiliang0302/vue3_vite_element-plus

github:https://github.com/cuiliang0302/vue3-vite-template

查看更多

微信公众号

微信公众号同步更新,欢迎关注微信公众号《崔亮的博客》第一时间获取最近文章。

博客网站

崔亮的博客-专注devops自动化运维,传播优秀it运维技术文章。更多原创运维开发相关文章,欢迎访问
https://www.cuiliangblog.cn

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/533.html

分享给朋友:

“Vue3 exceljs库实现前端导入导出Excel” 的相关文章

vue组件间的九种通信方式

前言Vue组件实例间的作用域是相互独立的,而通常一个页面是由很多个组件构成,这些组件可能又嵌套了组件,形成了一个关系网图,它们的关系可能是像下图中一样,大致分为两种使用场景,父子组件间通信和非父子组件间通信,父子组件间通信又分为直接父子关系和间接父子关系。vue提供了多种通信方法,针对不同的通信需求...

继Yuzu后,任天堂要求移除多个Switch模拟器项目

IT之家 7 月 11 日消息,任天堂美国分公司 (Nintendo of America) 已要求移除多个用于模拟 Nintendo Switch 游戏的开源模拟器项目,其中包括 Suyu、Nzu、Uzuy、Torzu、Sudachi 和 Yuzu-vanced 等。这些模拟器均被指控包含绕过任天...

面试被逼疯:聊聊Python Import System?

面试官一个小时逼疯面试者:聊聊Python Import System?对于每一位Python开发者来说,import这个关键字是再熟悉不过了,无论是我们引用官方库还是三方库,都可以通过import xxx的形式来导入。可能很多人认为这只是Python的一个最基础的常识之一,似乎没有可以扩展的点了,...

「云原生」Containerd ctr,crictl 和 nerdctl 命令介绍与实战操作

一、概述作为接替Docker运行时的Containerd在早在Kubernetes1.7时就能直接与Kubelet集成使用,只是大部分时候我们因熟悉Docker,在部署集群时采用了默认的dockershim。在V1.24起的版本的kubelet就彻底移除了dockershim,改为默认使用Conta...

HTML5最新版本介绍

HTML5是HTML4.01和XHTML1.0之后超文本标记语言的最新版本,由一群自由思想者设计,最终实现了多媒体支持、交互性、更智能的表单和更好的语义标注。 HTML 5不只是 HTML规范的最新版本,它是用于生成现代 Web内容的一系列相关技术的总称,其中最重要的三个技术是:HTML5核心规范...

HTML5+眼球追踪?黑科技颠覆传统手机体验

今天,iH5工具推出一个新的神秘功能——眼动追踪,可以通过摄像头捕捉观众眼球活动!为了给大家具体演示该功能的使用,我做了一个案例,供大家参考。实际效果如下:案例比较简单,就是通过眼动功能获取视觉焦点位置,剔除用户看中的牌。现在,舞台的属性中多了一个“启用眼动”的选项,另外,还多了一个“启用摄像头”的...