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

前言

需求场景

最近在开发项目时需要批量导入和导出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库将缓冲区数据保存到文件中。

<template><el-button type="primary" @click="exportExcel">导出excel</el-button>
</template><script setup>
import ExcelJS from "exceljs";
import FileSaver from "file-saver";
// 导出excel文件
const exportExcel = () => {// 创建工作簿const workbook = new ExcelJS.Workbook();// 添加工作表,名为sheet1const sheet1 = workbook.addWorksheet("sheet1");// 导出数据列表const data = [{"姓名": "张三", "年龄": 18, "身高": 175, "体重": 74},{"姓名": "李四", "年龄": 22, "身高": 177, "体重": 84},{"姓名": "王五", "年龄": 53, "身高": 155, "体重": 64}]// 获取表头所有键const headers = Object.keys(data[0])// 将标题写入第一行sheet1.addRow(headers);// 将数据写入工作表data.forEach((row) => {const values = Object.values(row)sheet1.addRow(values);});// 导出表格文件workbook.xlsx.writeBuffer().then((buffer) => {let file = new Blob([buffer], {type: "application/octet-stream"});FileSaver.saveAs(file, "ExcelJS.xlsx");}).catch(error => console.log('Error writing excel export', error))
}
</script><style scoped lang="scss"></style>

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

导入Excel

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

<template><inputtype="file"accept=".xls,.xlsx"class="upload-file"@change="importExcel($event)"/>
</template><script setup>
import ExcelJS from "exceljs";
// 导出excel文件
const importExcel = (event) => {//获取选择的文件const files = event.target.files//创建Workbook实例const workbook = new ExcelJS.Workbook();// 使用FileReader对象来读取文件内容const fileReader = new FileReader()// 二进制字符串的形式加载文件fileReader.readAsArrayBuffer(files[0])fileReader.onload = ev => {console.log(ev)// 从 buffer中加载数据解析workbook.xlsx.load(ev.target.result).then(workbook => {// 获取第一个worksheet内容const worksheet = workbook.getWorksheet(1);// 获取第一行的标题const headers = [];worksheet.getRow(1).eachCell((cell) => {headers.push(cell.value);});console.log("headers", headers)// 创建一个空的JavaScript对象数组,用于存储解析后的数据const data = [];// 遍历工作表的每一行(从第二行开始,因为第一行通常是标题行)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)})}
}
</script><style scoped lang="scss"></style>

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

进阶操作

添加数据

我们可以通过columns方法添加列标题并定义列键和宽度,设置好表头后,我们可以直接通过addRow方法,根据key值去添加每一行的数据。
参考文档:https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#%E5%88%97
完整代码如下:

<template><el-button type="primary" @click="exportExcel">导出excel</el-button>
</template><script setup>
import ExcelJS from "exceljs";
import FileSaver from "file-saver";
// 导出excel文件
const exportExcel = () => {// 创建工作簿const workbook = new ExcelJS.Workbook();// 添加工作表,名为sheet1const sheet1 = workbook.addWorksheet("sheet1");// 添加表头列数据sheet1.columns = [{header: "姓名", key: "name", width: 20},{header: "年龄", key: "age", width: 10},{header: "身高", key: "height", width: 10},{header: "体重", key: "weight", width: 10},];// 添加内容列数据sheet1.addRow({sort: 1, name: "张三", age: 18, height: 175, weight: 74});sheet1.addRow({sort: 2, name: "李四", age: 22, height: 177, weight: 88});sheet1.addRow({sort: 3, name: "王五", age: 53, height: 155, weight: 62});// 导出表格文件workbook.xlsx.writeBuffer().then((buffer) => {let file = new Blob([buffer], {type: "application/octet-stream"});FileSaver.saveAs(file, "ExcelJS.xlsx");}).catch(error => console.log('Error writing excel export', error))
}</script><style scoped lang="scss"></style>

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

读取数据

我们可以使用getRow方法,传入指定行参数读取行数据。
使用getColumn方法,传入键、字母、id参数读取列数据。
使用eachCell方法可以遍历每个单元格内容。
参考文档:https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#%E8%A1%8C
代码如下:

<template><el-button type="primary" @click="exportExcel">导出excel</el-button>
</template><script setup>
import ExcelJS from "exceljs";
import FileSaver from "file-saver";
// 导出excel文件
const exportExcel = () => {// 创建工作簿const workbook = new ExcelJS.Workbook();// 添加工作表,名为sheet1const sheet1 = workbook.addWorksheet("sheet1");// 添加表头列数据sheet1.columns = [{header: "姓名", key: "name", width: 20},{header: "年龄", key: "age", width: 10},{header: "身高", key: "height", width: 10},{header: "体重", key: "weight", width: 10},];// 添加内容列数据sheet1.addRow({sort: 1, name: "张三", age: 18, height: 175, weight: 74});sheet1.addRow({sort: 2, name: "李四", age: 22, height: 177, weight: 88});sheet1.addRow({sort: 3, name: "王五", age: 53, height: 155, weight: 62});// 读取行数据sheet1.getRow(1).eachCell((cell, rowIdx) => {console.log("行数据", cell.value, rowIdx);});// 读取列数据,可以通过键(name),字母(B)和基于id(1)的列号访问单个列sheet1.getColumn("name").eachCell((cell, rowIdx) => {console.log("列数据", cell.value, rowIdx);});
}</script><style scoped lang="scss"></style>

效果
image.png

样式

在导出excel文件时,默认没有任何样式的,为了美观我们需要添加样式,而exceljs支持修改表格样式,具体内容可参考文档https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#%E6%A0%B7%E5%BC%8F
例如,我们需要设置所有单元格居中对齐,并添加边框。并分别指定标题行和内容行字体大小、背景颜色、行高属性,代码如下:

<template><el-button type="primary" @click="exportExcel">导出excel</el-button>
</template><script setup>
import ExcelJS from "exceljs";
import FileSaver from "file-saver";
// 导出excel文件
const exportExcel = () => {// 创建工作簿const workbook = new ExcelJS.Workbook();// 添加工作表,名为sheet1const sheet1 = workbook.addWorksheet("sheet1");// 导出数据列表const data = [{"姓名": "张三", "年龄": 18, "身高": 175, "体重": 74},{"姓名": "李四", "年龄": 22, "身高": 177, "体重": 84},{"姓名": "王五", "年龄": 53, "身高": 155, "体重": 64}]// 获取表头所有键const headers = Object.keys(data[0])// 将标题写入第一行sheet1.addRow(headers);// 将数据写入工作表data.forEach((row) => {const values = Object.values(row)sheet1.addRow(values);});// 修改所有单元格样式// 遍历每一行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);// 设置行高为30titleCell.height = 30// 设置标题行单元格样式titleCell.eachCell((cell) => {// 设置标题行背景颜色为黄色cell.fill = {type: 'pattern',pattern: 'solid',fgColor: {argb: 'FFFF00'}};// 设置标题行字体cell.font = {color: {argb: 'FF0000'}, //颜色为红色bold: true,// 字体粗体size: 18 // 设置字体大小为18};})// 获取第二行到最后一行的内容数据const bodyRows = sheet1.getRows(2, sheet1.rowCount);// 处理内容行的数据bodyRows.forEach((bodyRow) => {// 设置行高为20bodyRow.height = 20bodyRow.eachCell((cell) => {cell.font = {size: 16 // 设置内容行字体大小为16};});});// 导出表格文件workbook.xlsx.writeBuffer().then((buffer) => {let file = new Blob([buffer], {type: "application/octet-stream"});FileSaver.saveAs(file, "ExcelJS.xlsx");}).catch(error => console.log('Error writing excel export', error))
}</script><style scoped lang="scss"></style>

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

筛选

在很多的时候我们需要对表格中每一列的数据进行筛选,比如直接筛选姓名等列信息,我们可以通过 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
代码如下:

<template><el-button type="primary" @click="exportExcel">导出excel</el-button>
</template><script setup>
import ExcelJS from "exceljs";
import FileSaver from "file-saver";
// 导出excel文件
const exportExcel = () => {// 创建工作簿const workbook = new ExcelJS.Workbook();// 添加工作表,名为sheet1const sheet1 = workbook.addWorksheet("sheet1");// 导出数据列表const data = [{"姓名": "张三", "年龄": 18, "身高": 175, "体重": 74},{"姓名": "李四", "年龄": 22, "身高": 177, "体重": 84},{"姓名": "王五", "年龄": 53, "身高": 155, "体重": 64}]// 获取表头所有键const headers = Object.keys(data[0])// 将标题写入第一行sheet1.addRow(headers);// 将数据写入工作表data.forEach((row) => {const values = Object.values(row)sheet1.addRow(values);});// 单列筛选// sheet1.autoFilter = "A1";// 多个列筛选sheet1.autoFilter = "A1:C1";// 导出表格文件workbook.xlsx.writeBuffer().then((buffer) => {let file = new Blob([buffer], {type: "application/octet-stream"});FileSaver.saveAs(file, "ExcelJS.xlsx");}).catch(error => console.log('Error writing excel export', error))
}</script><style scoped lang="scss"></style>

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

公式值

参考文档:https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#%E5%80%BC%E7%B1%BB%E5%9E%8B
我们可以直接对表格中的数据进行公式计算,比如 求和(SUM),平均数(AVERAGE) 等。
例如我们需要计算平均值、最大值、指定公式时,代码如下:

<template><el-button type="primary" @click="exportExcel">导出excel</el-button>
</template><script setup>
import ExcelJS from "exceljs";
import FileSaver from "file-saver";
// 导出excel文件
const exportExcel = () => {// 创建工作簿const workbook = new ExcelJS.Workbook();// 添加工作表,名为sheet1const sheet1 = workbook.addWorksheet("sheet1");// 导出数据列表const data = [{"姓名": "张三", "年龄": 18, "身高": 1.75, "体重": 74},{"姓名": "李四", "年龄": 22, "身高": 1.77, "体重": 84},{"姓名": "王五", "年龄": 53, "身高": 1.55, "体重": 64}]// 获取表头所有键const headers = Object.keys(data[0])// 将标题写入第一行sheet1.addRow(headers);// 将数据写入工作表data.forEach((row) => {const values = Object.values(row)sheet1.addRow(values);});// 添加单元格sheet1.getCell("E1").value = "BMI指数";sheet1.getCell("F1").value = "平均身高";sheet1.getCell("G1").value = "最大体重";// 计算平均身高sheet1.getCell("F2").value = {formula: "=AVERAGE(C2:C4)"};// 计算最大体重sheet1.getCell("G2").value = {formula: "=MAX(D2:D4)"};// 计算BMI指数// 获取第5列对象const BMIRange = sheet1.getColumn(5)BMIRange.eachCell((cell) => {console.log("cell", cell)console.log(cell.row)// 从第二列开始添加计算公式if (cell.row >= 2) {sheet1.getCell("E" + cell.row).value = {formula: "D" + cell.row + "/" + "(C" + cell.row + "*" + "C" + cell.row + ")"};}})// 导出表格文件workbook.xlsx.writeBuffer().then((buffer) => {let file = new Blob([buffer], {type: "application/octet-stream"});FileSaver.saveAs(file, "ExcelJS.xlsx");}).catch(error => console.log('Error writing excel export', error))
}</script><style scoped lang="scss"></style>

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

合并单元格

表格的合并应该是业务需求中最频繁的功能。当然这一功能使用 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
具体代码实现如下所示:

<template><el-button type="primary" @click="exportExcel">导出excel</el-button>
</template><script setup>
import ExcelJS from "exceljs";
import FileSaver from "file-saver";
// 导出excel文件
const exportExcel = () => {// 创建工作簿const workbook = new ExcelJS.Workbook();// 添加工作表,名为sheet1const sheet1 = workbook.addWorksheet("sheet1");// 导出数据列表const data = [{"姓名": "张三", "年龄": 18, "身高": 175, "体重": 74},{"姓名": "李四", "年龄": 18, "身高": '未知', "体重": '未知'},{"姓名": "王五", "年龄": 53, "身高": '未知', "体重": '未知'},{"姓名": "赵六", "年龄": 12, "身高": '未知', "体重": '未知'}]// 获取表头所有键const headers = Object.keys(data[0])// 将标题写入第一行sheet1.addRow(headers);// 将数据写入工作表data.forEach((row) => {const values = Object.values(row)sheet1.addRow(values);});// 上下合并单元格sheet1.mergeCells("B2:B3");// 左右合并单元格sheet1.mergeCells("C3:D3");// 范围合并单元格sheet1.mergeCells("C4:D5");// 导出表格文件workbook.xlsx.writeBuffer().then((buffer) => {let file = new Blob([buffer], {type: "application/octet-stream"});FileSaver.saveAs(file, "ExcelJS.xlsx");}).catch(error => console.log('Error writing excel export', error))
}</script><style scoped lang="scss"></style>

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

数据验证

有时候我们需要为某个单元格添加数据可以方便直接下拉选择指定的值,此时就需要使用数据验证功能,传入可填写的选项列表。
参考文档:https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#%E6%95%B0%E6%8D%AE%E9%AA%8C%E8%AF%81
例如我们对是否注册列添加数据验证,可填值为"是、否、未知",具体代码如下:

<template><el-button type="primary" @click="exportExcel">导出excel</el-button>
</template><script setup>
import ExcelJS from "exceljs";
import FileSaver from "file-saver";
// 导出excel文件
const exportExcel = () => {// 创建工作簿const workbook = new ExcelJS.Workbook();// 添加工作表,名为sheet1const sheet1 = workbook.addWorksheet("sheet1");// 导出数据列表const data = [{"姓名": "张三", "年龄": 18, "身高": 1.75, "体重": 74, "是否注册": ''},{"姓名": "李四", "年龄": 22, "身高": 1.77, "体重": 84, "是否注册": ''},{"姓名": "王五", "年龄": 53, "身高": 1.55, "体重": 64, "是否注册": ''}]// 获取表头所有键const headers = Object.keys(data[0])// 将标题写入第一行sheet1.addRow(headers);// 将数据写入工作表data.forEach((row) => {const values = Object.values(row)sheet1.addRow(values);});// 获取第5列对象const VerificationRange = sheet1.getColumn(5)VerificationRange.eachCell((cell) => {// 从第二列开始添加数据验证规则if (cell.row >= 2) {sheet1.getCell("E" + cell.row).dataValidation = {type: "list",allowBlank: true,formulae: ['"是,否,未知"']};}})// 导出表格文件workbook.xlsx.writeBuffer().then((buffer) => {let file = new Blob([buffer], {type: "application/octet-stream"});FileSaver.saveAs(file, "ExcelJS.xlsx");}).catch(error => console.log('Error writing excel export', error))
}</script><style scoped lang="scss"></style>

导出的excel文件效果如下:
image.png

条件格式化

我们可以为指定单元格添加条件格式,对满足条件的单元格设置指定的样式。
参考文档:https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#%E6%9D%A1%E4%BB%B6%E6%A0%BC%E5%BC%8F%E5%8C%96
例如为年龄大于18岁单元格进行红色标注,代码如下:

<template><el-button type="primary" @click="exportExcel">导出excel</el-button>
</template><script setup>
import ExcelJS from "exceljs";
import FileSaver from "file-saver";
// 导出excel文件
const exportExcel = () => {// 创建工作簿const workbook = new ExcelJS.Workbook();// 添加工作表,名为sheet1const sheet1 = workbook.addWorksheet("sheet1");// 导出数据列表const data = [{"姓名": "张三", "年龄": 18, "身高": 1.75, "体重": 74},{"姓名": "李四", "年龄": 22, "身高": 1.77, "体重": 84},{"姓名": "王五", "年龄": 53, "身高": 1.55, "体重": 64}]// 获取表头所有键const headers = Object.keys(data[0])// 将标题写入第一行sheet1.addRow(headers);// 将数据写入工作表data.forEach((row) => {const values = Object.values(row)sheet1.addRow(values);});// 年龄大于18岁红色标注sheet1.addConditionalFormatting({ref: "B2:B4",rules: [{type: "cellIs",operator: "greaterThan",priority: 1,formulae: [18],style: {fill: {type: "pattern",pattern: "solid",bgColor: { argb: "FFFFC0CB" },},},},],});// 导出表格文件workbook.xlsx.writeBuffer().then((buffer) => {let file = new Blob([buffer], {type: "application/octet-stream"});FileSaver.saveAs(file, "ExcelJS.xlsx");}).catch(error => console.log('Error writing excel export', error))
}</script><style scoped lang="scss"></style>

导出后的文件效果如下:
image.png

封装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();// 添加工作表,名为sheet1const 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 = 20sheet1.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文件导入与导出,代码如下:

<template><el-button type="primary" @click="exportExcel">导出excel</el-button><el-button type="success" @click="importExcel">导入excel</el-button><p>导入数据预览</p>{{ uploadData}}<el-dialogv-model="uploadDialogVisible"title="批量添加数据"width="40%"><el-form label-width="120px"><el-form-item label="模板下载:"><el-button type="info" @click="downloadTemplate"><el-icon><Download/></el-icon>点击下载</el-button></el-form-item><el-form-item label="文件上传:"><el-upload drag accept=".xls,.xlsx" :auto-upload="false" :on-change="handleChange"><el-icon class="el-icon--upload"><upload-filled/></el-icon><div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div><template #tip><div class="el-upload__tip">请上传.xls,.xlsx格式文件,文件最大为500kb</div></template></el-upload></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="uploadDialogVisible = false">取消</el-button><el-button type="primary" @click="submitUpload">导入</el-button></span></template></el-dialog>
</template><script setup>
import {ref} from "vue";
import {ElMessage} from "element-plus";
import {Download, UploadFilled} from "@element-plus/icons-vue"
import {getDemo} from "@/api/home";
import {timeFormatConversion} from "@/utils/timeFormat";
import {exportFile, importFile} from "@/utils/excel";
// 表格字段配置
const fieldConfig = ref([{'label': 'ID', // 标签'model': 'id',// 字段名'is_export': true,// 是否导出该字段},{'label': '用户名', // 标签'model': 'username',// 字段名'is_export': true, // 是否导出该字段},{'label': '省份', // 标签'model': 'province',// 字段名'is_export': true // 是否导出该字段},{'label': '性别', // 标签'model': 'sex_name',// 字段名'is_export': true // 是否导出该字段},{'label': '生日', // 标签'model': 'birthday',// 字段名'is_export': true, // 是否导出该字段},{'label': '身高(cm)', // 标签'model': 'height',// 字段名'is_export': true, // 是否导出该字段},{'label': '体重(kg)', // 标签'model': 'weight',// 字段名'is_export': true,// 是否导出该字段},{'label': '注册时间', // 标签'model': 'created_time',// 字段名'is_export': true, // 是否导出该字段},{'label': '个人介绍', // 标签'model': 'introduction',// 字段名'is_export': true,// 是否导出该字段}
])
// 导出Excel事件
const exportExcel = () => {ElMessage({message: '开始导出数据,请稍候!',type: 'success',})// 导出数据查询参数const printParams = {'size': 1000,'page': 1,}// 获取需要导出的字段配置const export_fields = fieldConfig.value.filter(obj => obj['is_export']).map(({label, model}) => ({[model]: label}))// 处理数据结构getDemo(printParams).then((response) => {// console.log(response.results)const export_data = response.results.map(obj => {const newObj = {};export_fields.forEach(field => {const [key, value] = Object.entries(field)[0];if (key === 'created_time') {newObj[value] = timeFormatConversion((obj[key]), 'YYYY-MM-DD HH:mm:ss');} else {newObj[value] = obj[key];}});return newObj;});let filename = '示例用户'exportFile(export_data, filename);}).catch(response => {//发生错误时执行的代码console.log(response)ElMessage.error('获取列表数据失败!')});
}
// 导入excel弹窗是否显示
const uploadDialogVisible = ref(false)
// 点击导入excel按钮事件
const importExcel = () => {uploadDialogVisible.value = true
}
// 下载模板文件
const downloadTemplate = () => {window.open('https://api.cuiliangblog.cn/static/demo-template.xlsx')
}
// 文件数据
const uploadData=ref([])
// 文件上传事件
const handleChange = (file) => {const reader = new FileReader();reader.onload = () => {const content = reader.result;importFile(content).then((data) => {console.log(data)uploadData.value = data}).catch(response => {//发生错误时执行的代码console.log(response)ElMessage.error('获取列表数据失败!')});};reader.readAsBinaryString(file.raw);
};
// 点击导入excel提交数据事件
const submitUpload = () => {uploadDialogVisible.value =false
}
</script><style scoped lang="scss"></style>

页面效果

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

完整代码

gitee:https://gitee.com/cuiliang0302/vue3_vite_element-plus
github:https://github.com/cuiliang0302/vue3-vite-template

查看更多

微信公众号

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

博客网站

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/702203.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中科大计网学习记录笔记(十五):可靠数据传输的原理

前前言&#xff1a;看过本节的朋友应该都知道本节长度长的吓人&#xff0c;但其实内容含量和之前的差不多&#xff0c;老师在本节课举的例子和解释比较多&#xff0c;所以大家坚持看完是一定可以理解透彻的。本节课大部分是在提出问题和解决问题&#xff0c;先明确出现的问题是…

python自动化管理和zabbix监控网络设备(有线网络配置部分)

目录 一、拓扑图 二、core-sw1 三、core-sw2 四、sum-sw1 五、sum-sw2 一、拓扑图 二、core-sw1 sys sysname core-sw1 vlan batch 10 20 30 40 50 60 100 vlan batch 200 210 220 230 240 250 stp region-configuration region-name huawei revision-level 1 instance…

学习python的第7天,她不再开放她的听歌榜单

我下午登录上小号&#xff0c;打开聊天消息看到了她的回复&#xff0c;我很开心兴奋&#xff0c;可是她不再开放她的听歌榜单了&#xff0c;我感觉得到&#xff0c;我要失恋了。 “因为当年电视上看没有王菲版本的” “行”。 “那你以后还会开放听歌榜单吗&#xff1f;”我…

Python入门必学:reverse()和reversed()的区别

Python入门必学&#xff1a;reverse()和reversed()的区别 &#x1f4c5;2024年02月25日 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程…

支付功能、支付平台、支持渠道如何测试?

作为一个支付平台&#xff0c;接入了快钱、易宝或直连银行等多家的渠道&#xff0c;内在的产品流程是自己的。业内有什么比较好的测试办法&#xff0c;来测试各渠道及其支持的银行通道呢&#xff1f; 作为产品&#xff0c;我自己办了十几张银行卡方便测试&#xff0c;但QA和开…

openGauss学习笔记-229 openGauss性能调优-系统调优-配置Ustore

文章目录 openGauss学习笔记-229 openGauss性能调优-系统调优-配置Ustore229.1 设计原理229.2 核心优势229.3 使用指导 openGauss学习笔记-229 openGauss性能调优-系统调优-配置Ustore Ustore存储引擎&#xff0c;又名In-place Update存储引擎&#xff08;原地更新&#xff09…

【前端素材】推荐优质医院后台管理系统I-Health平台模板(附源码)

一、需求分析 后台管理系统是一种用于管理和监控网站、应用程序或系统的在线工具。它通常是通过网页界面进行访问和操作&#xff0c;用于管理网站内容、用户权限、数据分析等。后台管理系统是网站或应用程序的控制中心&#xff0c;管理员可以通过后台系统进行各种管理和配置操…

数据结构(算法竞赛、蓝桥杯)--线段树+懒标记

1、B站视频链接&#xff1a;C02【模板】线段树懒标记 Luogu P3372 线段树 1_哔哩哔哩_bilibili 题目链接&#xff1a;P3372 【模板】线段树 1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) void build(int p,int l,int r){tr[p]{l,r,w[l],0};if(lr)return;//叶子节点返回int…

SpringBoot -【BeanFactory】基础使用及应用场景

1.介绍 在 Spring 框架中&#xff0c;BeanFactory 是 Spring IoC 容器的核心接口&#xff0c;负责管理 bean 的创建、配置和装配。它是 Spring IoC 容器的基础。BeanFactory 接口定义了一系列方法&#xff0c;用于管理和访问容器中的 bean 对象。 BeanFactoryAware 用于在 Sp…

matlab悬臂梁有限元分析

1、内容简介 略 47-可以交流、咨询、答疑 2、内容说明 略 建模说明 设计一个长方体的悬臂梁&#xff0c;长宽高分别为100m、10m和15m&#xff0c;材料特性为杨氏模量2e5&#xff0c;泊松比0.3&#xff0c; Matlab有限元分析&#xff08;截图&#xff09; 上图为悬臂梁的扰度…

Autosar 开篇

背景 AUTOSAR&#xff08;Automotive Open System Architecture&#xff09;是一个跨汽车行业的标准化软件架构&#xff0c;旨在促进汽车电子系统的开发和部署。下面是AUTOSAR发展的一些关键点&#xff1a; 起源和背景&#xff1a; AUTOSAR最初于2003年由汽车制造商宝马、戴姆…

从私人客户转变为教练会员网站

教练和顾问可以做出的最令人兴奋的转变之一就是通过教练会员网站扩大业务规模。 一对多优惠的类型有很多种&#xff0c;但与任何其他选择相比&#xff0c;教练和顾问的会员资格拥有最多的机会和灵活性&#xff0c;可以与你和你的客户一起发展。 世界正在转向更容易获得和更…

Peter算法小课堂—动态规划

Peter来啦&#xff0c;好久没有更新了呢 今天&#xff0c;我们来讨论讨论提高组的动态规划。 动态规划 动态规划有好多经典的题&#xff0c;有什么背包问题、正整数拆分、杨辉三角……但是&#xff0c;如果考到陌生的题&#xff0c;怎么办呢&#xff1f;比如说2000年提高组的…

AD24-Gerber生产文件输出及整理

一、Gerber生产文件输出 1、先进行规则检查 2、Gerber Files输出 3、钻孔文件 4、IPC网表 5、坐标文件 二、Gerber Flies文件整理 1、CAM 2、SMT 3、ASM 4、PRJ 5、DXF

ubuntu安装gptsovits

我看到社区有人需要&#xff0c;刚好我自己也要安装个ubuntu的用在自己的4090服务器上玩一玩。 于是就写一篇这样的教程。但是我只需要他的api推理&#xff0c;用于测试4090合成速度。所以这里只执行Python api.py 环境 1.首先下载整合包&#xff0c;里面有个nltk_data,拿出来…

Jmeter之单接口的性能测试

前言&#xff1a; 服务端的整体性能测试是一个非常复杂的概念&#xff0c;包含生成虚拟用户&#xff0c;模拟并发&#xff0c;分析性能结果等各种技术&#xff0c;期间可能还要解决设计场景、缓存影响、第三方接口mock、IP限制等问题。如何用有限的测试机器&#xff0c;在测试环…

Mysql 连接最近经常报超时

原因 怀疑是某个服务频繁调用mysql操作,导致linux buff/cache 过大 # 通过下列命令查看 free -h解决方案 临时解决方案: 可以通过写入 /proc/sys/vm/drop_caches 来释放缓存。这是一个临时的操作&#xff0c;内核会在需要时再次填充缓存 # 释放缓存 sync; echo 3 > /proc/…

【C语言】详解计算机二级c语言程序题

文章目录 前言资料相关程序题 一&#xff08;字符串&#xff09;程序题 二&#xff08;数组&#xff09;程序题 三&#xff08;基础&#xff09;程序题 四&#xff08;结构体&#xff09;程序题 五&#xff08;结构体&#xff09;程序题 六&#xff08;基础&#xff09; 前言 …

leetcode:491.递增子序列

1.误区&#xff1a;不能直接对数组排序再求解子集&#xff0c;因为那样就改变了原有数组的顺序 2.树形结构&#xff1a;一个一个取数&#xff0c;然后保证是递增序列&#xff0c;且不能重复。&#xff08;数层上不可以重复取&#xff0c;树枝上可以重复取&#xff09;收集的结…

[设计模式Java实现附plantuml源码~行为型]对象间的联动~观察者模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…