vue3 导出数据为 excel 文件

文章目录

  • 安装插件
  • 封装组件 -- Export2Excel.js
  • 多表封装
  • 界面使用 -- 数据处理成二维数组
  • 更多

菜鸟最近做了一个需求,就是需要上传表单并识别,然后识别出来的内容要可以修改,然后想的就是识别内容变成 form 表单,所以并没有使用 SpreadJS ,这个 SpreadJS 有很多 excel 的功能,强大是强大,就是我这个需求没有那么复杂!

然后上传识别交给后端处理了,菜鸟做的就是查看详情的是时候可把表单展示的数据导出成 excel 文件就行!

安装插件

npm i xlsx
npm install -S file-saver

封装组件 – Export2Excel.js

import { saveAs } from 'file-saver'
import * as XLSX from 'xlsx'/**** @param {Object} workbook 工作薄* @param {Object} worksheet 工作表* @param {Object} cell 单元格* 标记,引用单元格时所使用的地址格式(如:A1、C7)*/
function datenum (v, date1904) {if (date1904) v += 1462var epoch = Date.parse(v)return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000)
}function sheetFromArrayOfArrays (data, opts) {var ws = {}var range = {s: {c: 10000000,r: 10000000},e: {c: 0,r: 0}}for (var R = 0; R !== data.length; ++R) {for (var C = 0; C !== data[R].length; ++C) {if (range.s.r > R) range.s.r = Rif (range.s.c > C) range.s.c = Cif (range.e.r < R) range.e.r = Rif (range.e.c < C) range.e.c = Cvar cell = {v: data[R][C] // v表示单元格原始值, t表示内容类型,s-string类型,n-number类型,b-boolean类型,d-date类型,等等}if (cell.v == null) continue/*** 通过地址对象 { r: R, c: C } 来获取单元格,R 和 C 分别代表从 0 开始的行和列的索引。* XLSX.utils 中的 encode_cell/decode_cell 方法可以转换单元格地址*    XLSX.utils.encode_cell({ r: 7, c: 2 })  ===》 C7*/var cellRef = XLSX.utils.encode_cell({ c: C, r: R })if (typeof cell.v === 'number') cell.t = 'n'else if (typeof cell.v === 'boolean') cell.t = 'b'else if (cell.v instanceof Date) {cell.t = 'n'cell.z = XLSX.SSF._table[14]cell.v = datenum(cell.v)} else cell.t = 's'ws[cellRef] = cell}}// ws['!ref']:表示所有单元格的范围,例如从A1到F8则记录为A1:F8if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range)return ws
}function Workbook () {if (!(this instanceof Workbook)) return new Workbook()this.SheetNames = []this.Sheets = {}
}// 字符串转为ArrayBuffer
function s2ab (s) {var buf = new ArrayBuffer(s.length)var view = new Uint8Array(buf)for (var i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFFreturn buf
}/**** @param {Array} multiHeader  多行表头* @param {Array} header  表头* @param {Array} data  数据* @param {String} filename  文件名* @param {Array} merges  合并单元格* @param {Boolean} autoWidth  是否设置单元格宽度* @param {String} bookType  要生成的文件类型*/
export function exportJsonToExcel ({multiHeader = [],header,data,filename,merges = [],autoWidth = true,bookType = 'xlsx'
} = {}) {filename = filename || 'excel-list'data = [...data]data.unshift(header)for (let i = multiHeader.length - 1; i > -1; i--) {data.unshift(multiHeader[i])}var wsName = 'SheetJS'var wb = new Workbook()var ws = sheetFromArrayOfArrays(data)if (merges.length > 0) {// ws[!merges]:存放一些单元格合并信息,是一个数组,每个数组由包含s和e构成的对象组成,s表示开始,e表示结束,r表示行,c表示列if (!ws['!merges']) ws['!merges'] = []merges.forEach(item => {ws['!merges'].push(XLSX.utils.decode_range(item))})}if (autoWidth) {/* 设置worksheet每列的最大宽度 */const colWidth = data.map(row => row.map(val => {/* 先判断是否为null/undefined */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}}}))/* 以第一行为初始值 */let result = colWidth[0]for (let i = 1; i < colWidth.length; i++) {for (let j = 0; j < colWidth[i].length; j++) {if (result[j] && result[j]['wch'] < colWidth[i][j]['wch']) {result[j]['wch'] = colWidth[i][j]['wch']}}}// ws['!cols']设置单元格宽度, [{'wch': 10},{'wch': 10}] ===> 第一列和第二列设置了宽度ws['!cols'] = result}/* add worksheet to workbook */wb.SheetNames.push(wsName)wb.Sheets[wsName] = wsvar wbout = XLSX.write(wb, {bookType: bookType,bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性type: 'binary'})saveAs(new Blob([s2ab(wbout)], { type: 'application/octet-stream' }),`${filename}.${bookType}`)
}

此处参考:vue3中将数据导出为excel表格

多表封装

但是菜鸟的需求是要一个 excel 包含两个表,所以菜鸟对其进行了优化:

import { saveAs } from "file-saver";
import * as XLSX from "xlsx";/**** @param {Object} workbook 工作薄* @param {Object} worksheet 工作表* @param {Object} cell 单元格* 标记,引用单元格时所使用的地址格式(如:A1、C7)*/
function datenum(v, date1904) {if (date1904) v += 1462;var epoch = Date.parse(v);return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}// eslint-disable-next-line
function sheetFromArrayOfArrays(data, opts) {var ws = {};var range = {s: {c: 10000000,r: 10000000,},e: {c: 0,r: 0,},};for (var R = 0; R !== data.length; ++R) {for (var C = 0; C !== data[R].length; ++C) {if (range.s.r > R) range.s.r = R;if (range.s.c > C) range.s.c = C;if (range.e.r < R) range.e.r = R;if (range.e.c < C) range.e.c = C;var cell = {v: data[R][C], // v表示单元格原始值, t表示内容类型,s-string类型,n-number类型,b-boolean类型,d-date类型,等等};if (cell.v == null) continue;/*** 通过地址对象 { r: R, c: C } 来获取单元格,R 和 C 分别代表从 0 开始的行和列的索引。* XLSX.utils 中的 encode_cell/decode_cell 方法可以转换单元格地址*    XLSX.utils.encode_cell({ r: 7, c: 2 })  ===》 C7*/var cellRef = XLSX.utils.encode_cell({ c: C, r: R });if (typeof cell.v === "number") cell.t = "n";else if (typeof cell.v === "boolean") cell.t = "b";else if (cell.v instanceof Date) {cell.t = "n";cell.z = XLSX.SSF._table[14];cell.v = datenum(cell.v);} else cell.t = "s";ws[cellRef] = cell;}}// ws['!ref']:表示所有单元格的范围,例如从A1到F8则记录为A1:F8if (range.s.c < 10000000) ws["!ref"] = XLSX.utils.encode_range(range);return ws;
}function Workbook() {if (!(this instanceof Workbook)) return new Workbook();this.SheetNames = [];this.Sheets = {};
}// 字符串转为ArrayBuffer
function s2ab(s) {var buf = new ArrayBuffer(s.length);var view = new Uint8Array(buf);for (var i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;return buf;
}/**** @param {Array} multiHeader  多行表头* @param {Array} header  表头* @param {Array} data  数据* @param {String} filename  文件名* @param {Array} merges  合并单元格* @param {Boolean} autoWidth  是否设置单元格宽度* @param {String} bookType  要生成的文件类型*/
export function exportJsonToExcel({multiHeader = [],header,data,filename,sheetname,merges = [],autoWidth = true,bookType = "xlsx",
} = {}) {filename = filename || "excel-list";for (let i in header) {data[i] = [...data[i]];data[i].unshift(header[i]);}for (let j in data) {for (let i = multiHeader.length - 1; i > -1; i--) {data[j].unshift(multiHeader[i]);}}let wsName = [];for (let i in sheetname) {wsName.push(sheetname[i]);}var wb = new Workbook();let ws = [];for (let i in data) {ws.push(sheetFromArrayOfArrays(data[i]));}if (merges.length > 0) {// ws[!merges]:存放一些单元格合并信息,是一个数组,每个数组由包含s和e构成的对象组成,s表示开始,e表示结束,r表示行,c表示列for (let i in ws) {if (!ws[i]["!merges"]) ws["!merges"] = [];merges.forEach((item) => {ws[i]["!merges"].push(XLSX.utils.decode_range(item));});}}if (autoWidth) {/* 设置worksheet每列的最大宽度 */const colWidth = data.map((row) =>row.map((val) => {/* 先判断是否为null/undefined */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,};}}));/* 以第一行为初始值 */let result = colWidth[0];for (let i = 1; i < colWidth.length; i++) {for (let j = 0; j < colWidth[i].length; j++) {if (result[j] && result[j]["wch"] < colWidth[i][j]["wch"]) {result[j]["wch"] = colWidth[i][j]["wch"];}}}// ws['!cols']设置单元格宽度, [{'wch': 10},{'wch': 10}] ===> 第一列和第二列设置了宽度for (let i in ws) {ws[i]["!cols"] = result;}}/* add worksheet to workbook */for (let i in wsName) {wb.SheetNames.push(wsName[i]);wb.Sheets[wsName[i]] = ws[i];}var wbout = XLSX.write(wb, {bookType: bookType,bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性type: "binary",});saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), `${filename}.${bookType}`);
}

界面使用 – 数据处理成二维数组

第一个的使用直接参考别人的,我这里使用的是菜鸟封装的,如果有误,可以留言,菜鸟进行更改,因为菜鸟这里只测试了一个和两个表格的情况

// 数据处理  --》 读者按照自己的逻辑来
for (const key in envFactors.value) {let data1 = []; //设置一个二级列表,导出的表格每一行为一个二级列表for (let i in objkeyArr.value) {let keyname = objkeyArr.value[i];data1.push(envFactors.value[key][keyname]);}data2.value.push(data1); //把二级列表塞入一级列表
}
console.log(data2); // data2 必须是二维数组for (const key in sampleGroups.value) {let data1 = [];data1.push(sampleGroups.value[key].sampleTestName,sampleGroups.value[key].sampleAnalysisName,sampleGroups.value[key].groupFirstWay,sampleGroups.value[key].groupSecWay,sampleGroups.value[key].groupThirdWay,sampleGroups.value[key].groupFourthWay,sampleGroups.value[key].groupFifthWay);data3.value.push(data1);
}
console.log(data3);
// 数据处理结束// 导出成表格
function exportExcel() {exportJsonToExcel({header: [["检测报告样品名称","分析样品名称","分组名称","分组名称","分组名称","分组名称","分组名称",],objkeyArr.value,], // 表名个数可以不等于数据每一行的列数filename: "详请表",data: [data3.value, data2.value],sheetname: ["样品分组表", "环境因子表"],});
}

更多

菜鸟这里只使用了header、data、filename、sheetname 等属性,其他属性可以看该篇文章,挺详细的:纯前端js(或者vue)导出excel实现:合并单元格、设置单元格样式、单元格内换行

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

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

相关文章

反爬虫机制与反爬虫技术(二)

反爬虫机制与反爬虫技术二 1、动态页面处理与验证码识别概述2、反爬虫案例:页面登录与滑块验证码处理2.1、用例简介2.2、库(模块)简介2.3、网页分析2.4、Selenium准备操作2.5、页面登录2.6、模糊移动滑块测试3、滑块验证码处理:精确移动滑块3.1、精确移动滑块的原理3.2、滑…

【模块补充】importlib

importlib 【一】介绍 importlib 模块是 Python 中用于动态加载和导入模块的内置模块。它提供了一组函数和类&#xff0c;使得我们可以在运行时根据需要加载模块&#xff0c;并且可以对已导入的模块进行操作和管理。 【二】详解及示例&#xff1a; 【1】动态加载模块&#…

PyQt6简介

锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计12条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话版…

企业远程访问业务系统:对比MPLS专线,贝锐蒲公英为何更优优势?

如今&#xff0c;企业大多都会采用OA、ERP、CRM等各种数字化业务系统。 私有云、公有云混合架构也变得越来越常见。 比如&#xff1a;研发系统部署在公司本地私有云、确保数据安全&#xff0c;OA采用公有云方案、满足随时随地访问需求。 如此一来&#xff0c;也产生了远程访问…

js前端跨屏效果

效果: 三个球 源码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>三个球</title> </h…

js实现图片懒加载

方式一&#xff1a;html实现 在img标签加上 loading"lazy" 方式二&#xff1a;js实现 通过js监听页面的滚动&#xff0c;实现的原理主要是判断当前图片是否到了可视区域&#xff1a; 拿到所有的图片 dom 。遍历每个图片判断当前图片是否到了可视区范围内。如果到了…

Maven项目下详细的SSM整合流程

文章目录 &#x1f389;SSM整合流程一、两个容器整合✨ 1、先准备好数据库config.properties连接、mybatis-config.xml&#x1f38a; 2、容器一&#xff1a;优先配置spring.xml文件&#x1f38a; 3、容器二&#xff1a;配置springMVC.xml文件&#x1f38a; 4、Tomcat整合spring…

解释PCIe MSI 中断要求中断向量连续?PCIe 规范里并没有明确指出

MSI 向量必须连续&#xff1f; 前言 MSI 物理条件&#xff0c;MSI 中断产生的逻辑是RC初始化的时候&#xff0c;由软件将配置写入到 EP 的 2 个寄存器中&#xff0c;这两个寄存器一个指示的是地址 Message Address&#xff0c;一个指示的是数据 Message Data。当 EP 试图触发…

你再不学Git就来不及了!!!

其他系列文章导航 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 版本控制 什么是版本控制 为什么要版本控制 一、认识 Git 1.1Git 简史 1.2Git 与其他版本管理系统的主要区别 1.3Git 的三种状态 二、Git 使用快速入门 2.1获…

springboot使用redis缓存乱码(key或者 value 乱码)一招解决

如果查看redis中的值是这样 创建一个配置类就可以解决 package com.deka.config;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; i…

CPU+GPU多样化算力,ZStack Cloud助力游戏精酿核心业务上云

游戏精酿通过ZStack Cloud云平台提供高性能、高可用的云主机、云存储和云网络&#xff1b;前期通过超融合架构快速构建云基础设施&#xff0c;来支持Jira、Redis等关键业务&#xff1b;并实现对原有私有云平台业务的替代&#xff0c;按需将原有私有云业务滚动迁移到ZStack Clou…

移动端浏览器 jquery 获取 pdf blob文件流 预览pdf

最近遇到一个需求&#xff0c;一个古早的移动端 juery 项目要求做一个页面&#xff0c;从接口获取 pdf 文件流&#xff0c;然后预览出来 这里使用第三方工具&#xff1a;pdf.js 代码如下&#xff1a; // 引入相关文件<script src"../js/pdf.js" type"text…

N_1 验证密码

N_1 验证密码 题目 设计一个用户密码验证程序&#xff0c;要求密码输入只有3次机会&#xff0c;且密码中不能包含”*”字符。 分析 需要考虑3个问题&#xff1a;验证次数、特殊字符和正误密码判断&#xff1b;验证次数需要使用循环&#xff0c;3个问题需要用到分支结构&…

java 系统属性和环境属性

Java系统属性和环境属性都是与Java应用程序相关的参数&#xff0c;但它们有以下区别&#xff1a; 系统属性是由Java虚拟机&#xff08;JVM&#xff09;设置的&#xff0c;而环境属性是由操作系统设置的。 系统属性是以“-D”开头的命令行参数传递给JVM的&#xff0c;而环境属性…

深入理解Spring AOP的工作流程

文章目录 引言什么是AOP&#xff1f;Spring AOP的工作原理1. JDK动态代理2. CGLIB代理 Spring AOP的注解方式Aspect注解EnableAspectJAutoProxy注解 Spring AOP的工作流程拓展应用1. 自定义注解2. 异常处理3. 切面优先级 结论 &#x1f389;深入理解Spring AOP的工作流程 ☆* o…

关于运行软件程序出现vcruntime140.dll丢失的修复教程-解决方案

vcruntime140.dll是Microsoft Visual C库文件的一部分&#xff0c;用于支持Windows操作系统上的应用程序。如果找不到或丢失了这个文件&#xff0c;可能会导致某些应用程序无法正常运行。下面是关于vcruntime140.dll丢失的5个修复方法&#xff0c;以及vcruntime140.dll文件属性…

Python基础教程之分支结构详解

文章目录 一、分支结构二、单分支结构三、双分支结构四、多分支结构五、嵌套分支结构六、三元表达式七、条件测试关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③P…

Elasticsearch基础优化

分片策略 分片和副本得设计为ES提供支付分布式和故障转移得特性&#xff0c;但不意味着分片和副本是可以无限分配&#xff0c; 而且索引得分片完成分配后由于索引得路由机制&#xff0c;不能重新修改分片数&#xff08;副本数可以动态修改&#xff09; 一个分片得底层为一个l…

python之pyqt专栏2-项目文件解析

项目结构 在上一篇文章python之pyqt专栏1-环境搭建&#xff0c;创建新的pyqt项目&#xff0c;下面我们来看一下这个项目下的文件。 从下面的文件结构图可以看到&#xff0c;该项目下有3个文件&#xff0c;untitled.ui,untitled.py 以及main.py。 QtDesigner可以UI界面的方式&am…

Feign接口请求返回异常 no suitable HttpMessageConvert found for response type

问题场景&#xff1a; 后端调用feign接口请求, 接口返回异常, no suitable HttpMessageConvert found for response type 问题描述 报错异常如下&#xff1a; //根据图片特征 去查询人员信息ResultVo<List> personVos ipbdFaceLibPersonApi.queryFacePersonByFeatur…