vue:功能【xlsx】动态行内合并

场景:纯前端导出excel数据,涉及到列合并、行合并。

注)当前数据表头固定,行内数据不固定。以第一列WM为判断条件,相同名字的那几行数据合并单元格。合并的那几行数据,后面的列按需求进行合并。

注)本示例是用 vue3+element plus 实现的。

要求导出Excel效果图:

一、按需引入插件

npm i -S file-saver xlsx
npm i -D script-loader

二、导出实现

1、封装方法

excel导出的数据都是二维数组格式,如果是常规的list数据,需要转成二维数组。

注)生成的excel封装的方法如下(支持表头合并、导出的 excel 支持生成多个sheet工作表、表格可自适应宽度、自适应高度、合并表格)

【步骤】

1、导出操作涉及到使用 OutExcelSheet.exportSheetExcel 函数来导出一个名为 karlaExport导出.xlsx 的 Excel 文件。【三个参数:sheetData、mergesHeader 和文件名】

2、sheetData 是一个数组,用于存储要导出的表格数据。在代码中,使用了一个名为 sheet1 的对象来定义表格的名称、数据、合并单元格和行高等信息。

3、mergesHeader 是一个数组,用于指定要合并的行和列的范围。在给定的代码中,合并了一些特定的行和列,以创建标题行和表头的合并效果。

4、最后,通过调用 OutExcelSheet.exportSheetExcel 函数,并传递以上参数,将生成的 Excel 文件导出到本地。

import XLSX from 'xlsx-js-style'
import FileSaver from 'file-saver'export default {// 三个参数:sheetData、mergesHeader 和文件名。exportSheetExcel(sheetData, mergerArr, fileName = 'karlaExport.xlsx') {const wb = XLSX.utils.book_new() // 创建一个新工作簿for (let i = 0; i < sheetData.length; i++) {const sheet = sheetData[i]// 检查数据项是否存在if (!sheet.data) {continue // 如果数据项不存在,则跳过当前循环}const ws = XLSX.utils.aoa_to_sheet(sheet.data) // 将数据数组转换为工作表// 设置合并单元格ws['!merges'] = sheet.merges && sheet.merges.length > 0 ? [...sheet.merges, ...(mergerArr || [])] : mergerArr;// 设置列宽为自适应if (sheet.data.length > 0) {ws['!cols'] = sheet.data[0].map((_, index) => ({ wch: 15 }))}// 设置行高if (sheet.rowHeights && sheet.rowHeights.length > 0) {ws['!rows'] = sheet.rowHeights.map((height) => ({ hpt: height, hpx: height }))}const borderAll = {top: { style: 'thin' },bottom: { style: 'thin' },left: { style: 'thin' },right: { style: 'thin' }}// 设置单元格样式for (const key in ws) {if (ws.hasOwnProperty(key)) {const cell = ws[key]if (typeof cell === 'object') {cell.s = {border: borderAll,alignment: {horizontal: 'center',vertical: 'center',wrapText: true},font: {sz: 12,color: {rgb: '000000'}},numFmt: 'General',fill: {fgColor: { rgb: 'FFFFFF' }}}}}}XLSX.utils.book_append_sheet(wb, ws, sheet.name) // 将工作表添加到工作簿并指定名称}const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' }) // 将工作簿转换为数组const file = new Blob([wbout], { type: 'application/octet-stream' }) // 创建Blob对象FileSaver.saveAs(file, fileName) // 下载文件},// 二维数组中空的数据设置为 0emptyValues(array, defaultValue) {for (let i = 0; i < array.length; i++) {for (let j = 0; j < array[i].length; j++) {if (array[i][j] === null || array[i][j] === undefined || array[i][j] === '') {array[i][j] = defaultValue}}}return array},// 生成excel列表数据handleExcelTable(columnHeader, list) {if (list.length === 0) return []// 表头const tableColumn = Object.keys([columnHeader][0])// 表格生成的数据const sheet = [tableColumn]list.forEach((item) => {const row = tableColumn.map((column) => item[column])sheet.push(row)})// 表头匹配对应的中文const firstRow = sheet[0].map((column) => columnHeader[column])sheet[0] = firstRowreturn sheet || []}
}

2、前端代码,导出,用的mock数据,是转换后的二维数组,封装的方法中有写

<script lang="ts" setup>
// 引入导出excel 封装的方法
import OutExcelSheet from '@/hooks/web/outToExcelManySheet'defineOptions({ name: 'export' })/** 导出 */
const exportLoading = ref(false)
const handleExport = async () => {// 表格合并需要添加一行合并表头const header = ['WM','Total Leave days','Paid Leave Date','Actual working days of previous 3 months','','','Payout of commission 3 months','','','Average commission / day','Commission during leave','Leave Payout','Total Leave Payout']// 表头const columnsHeader = {name: 'WM',leaveDays: 'Total Leave days',paidLeaveDate: 'Paid Leave Date',actualDaysOneMonth: 'Actual working days of previous 3 months(近三月)',actualDaysTwoMonth: 'Actual working days of previous 3 months(近两月)',actualDaysThreeMonth: 'Actual working days of previous 3 months(近一月)',payoutCommissionOneMonthPrice: 'Payout of commission 3 months(近三月)',payoutCommissionTwoMonthPrice: 'Payout of commission 3 months(近二月)',payoutCommissionThreeMonthPrice: 'Payout of commission 3 months(近一月)',averageCommission: 'Average commission/day',commissionDuringLeave: 'Commission during leave',leavePayout: 'Leave Payout',totalLeavePayout: 'Total Leave Payout'}// mock 导出数据(带表头)const exportList = [['WM','Total Leave days','Paid Leave Date','Actual working days of previous 3 months\t(第一个月)','Actual working days of previous 3 months\t(第二个月)','Actual working days of previous 3 months\t(第三个月)','Payout of commission 3 months\t第一个月)','Payout of commission 3 months\t(第二个月)','Payout of commission 3 months\t(第三个月)','Average commission / day','Commission during leave','Leave Payout','Total Leave Payout'],['karla',5,'2023-01-01','10','18','18','10000','20000','30000','1400','640','760','0.00'],['karla',5,'2023-01-04','10','18','18','10000','20000','30000','1400','1600','0.00','0.00'],['karla',5,'2023-01-06','10','18','18','10000','20000','30000','1400','1800','0.00','0.00'],['karla',5,'2023-01-24','10','18','18','10000','20000','30000','1400','0.00','0.00','0.00'],['karla',5,'2023-01-18','10','18','18','10000','20000','30000','1400','1600','0.00','0.00'],['York',2,'2023-01-18','28','24','18','10000','20000','30000','1500','1800','0.00','666'],['York',2,'2023-01-24','28','24','18','10000','20000','30000','1500','700','800','666'],['Caleb',1,'2023-01-29','22','15','17','8899.12','7833','1455.63','1366.8','734.8','632','0.00']]// 生成表格const sheet1 = {name: 'LeavePay',// data: [header, ...OutExcelSheet.handleExcelTable(columnsHeader, list.value)],  // 常规list数据用封装的方法处理二维数据data: [header, ...exportList],  // 使用处理好的mock数据merges: [],rowHeights: [{ hpx: 20 }, { hpx: 20 }]}// 合并:第0列、第1列、第三列、第四列、第五列、第六列、第七列和第八列的相同值进行行合并const mergedRows = new Map()for (let i = 1; i < sheet1.data.length; i++) {const cellValue0 = sheet1.data[i][0]const cellValue1 = sheet1.data[i][1]const cellValue3 = sheet1.data[i][3]const cellValue4 = sheet1.data[i][4]const cellValue5 = sheet1.data[i][5]const cellValue6 = sheet1.data[i][6]const cellValue7 = sheet1.data[i][7]const cellValue8 = sheet1.data[i][8]const prevValue0 = sheet1.data[i - 1][0]const prevValue1 = sheet1.data[i - 1][1]const prevValue3 = sheet1.data[i - 1][3]const prevValue4 = sheet1.data[i - 1][4]const prevValue5 = sheet1.data[i - 1][5]const prevValue6 = sheet1.data[i - 1][6]const prevValue7 = sheet1.data[i - 1][7]const prevValue8 = sheet1.data[i - 1][8]if (cellValue0 === prevValue0 &&cellValue1 === prevValue1 &&cellValue3 === prevValue3 &&cellValue4 === prevValue4 &&cellValue5 === prevValue5 &&cellValue6 === prevValue6 &&cellValue7 === prevValue7 &&cellValue8 === prevValue8) {if (mergedRows.has(cellValue0)) {// 更新合并的结束行索引mergedRows.get(cellValue0).end = i} else {// 添加新的合并信息mergedRows.set(cellValue0, { start: i - 1, end: i })}}}// 添加行合并信息到 mergesHeaderfor (const [value, { start, end }] of mergedRows.entries()) {sheet1.merges.push({ s: { r: start, c: 0 }, e: { r: end, c: 0 } })sheet1.merges.push({ s: { r: start, c: 1 }, e: { r: end, c: 1 } })sheet1.merges.push({ s: { r: start, c: 3 }, e: { r: end, c: 3 } })sheet1.merges.push({ s: { r: start, c: 4 }, e: { r: end, c: 4 } })sheet1.merges.push({ s: { r: start, c: 5 }, e: { r: end, c: 5 } })sheet1.merges.push({ s: { r: start, c: 6 }, e: { r: end, c: 6 } })sheet1.merges.push({ s: { r: start, c: 7 }, e: { r: end, c: 7 } })sheet1.merges.push({ s: { r: start, c: 8 }, e: { r: end, c: 8 } })sheet1.merges.push({ s: { r: start, c: 12 }, e: { r: end, c: 12 } })}// 合并表头const mergesHeader = [// 行合并{ s: { r: 0, c: 3 }, e: { r: 0, c: 5 } },{ s: { r: 0, c: 6 }, e: { r: 0, c: 8 } },// 列合并(r 表示行索引,c 表示列索引){ s: { r: 0, c: 0 }, e: { r: 1, c: 0 } }, // 第0列的第0行和第1行合并{ s: { r: 0, c: 1 }, e: { r: 1, c: 1 } }, // 第1列的第0行和第1行合并{ s: { r: 0, c: 2 }, e: { r: 1, c: 2 } }, // 第2列的第1行和第1行合并{ s: { r: 0, c: 9 }, e: { r: 1, c: 9 } },{ s: { r: 0, c: 10 }, e: { r: 1, c: 10 } },{ s: { r: 0, c: 11 }, e: { r: 1, c: 11 } },{ s: { r: 0, c: 12 }, e: { r: 1, c: 12 } }]const sheetData = [sheet1]// 导出OutExcelSheet.exportSheetExcel(sheetData, mergesHeader, `karla导出.xlsx`)
}
</script>

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

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

相关文章

CV论文--2024.3.13

1、Sora Generates Videos with Stunning Geometrical Consistency 中文标题:Sora 生成具有惊人几何一致性的视频。 简介&#xff1a;最近发布的 Sora 模型展示了在视频生成领域的出色表现&#xff0c;引发了人们对其模拟真实世界现象能力的激烈讨论。尽管该模型越来越受欢迎&…

如何保证Redis和数据库数据一致性

缓存可以提升性能&#xff0c;减轻数据库压力&#xff0c;在获取这部分好处的同时&#xff0c;它却带来了一些新的问题&#xff0c;缓存和数据库之间的数据一致性问题。 想必大家在工作中只要用了咱们缓存势必就会遇到过此类问题 首先我们来看看一致性&#xff1a; 强一致性…

前端实现生成图片并批量下载,下载成果物是zip包

简介 项目上有个需求&#xff0c;需要根据表单填写一些信息&#xff0c;来生成定制的二维码图片&#xff0c;并且支持批量下载二维码图片。 之前的实现方式是直接后端生成二维码图片&#xff0c;点击下载时后端直接返回一个zip包即可。但是项目经理说后端实现方式每次改个东西…

python基础——列表【创建,下标索引,常见操作方法】

&#x1f4dd;前言&#xff1a; 这篇文章主要讲解一下python中常见的数据容器之一——列表 本文主要讲解列表的创建以及我们常用的列表操作方法 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;C语言入门基础以及python入门基础 &#x1f380…

泰迪智能科技3月线上培训计划

有学习意向可到 泰迪智能科技官网 咨询了解

Visual Basic6.0零基础教学(3)—焦点概念和深入学习属性

焦点概念和深入学习属性 文章目录 焦点概念和深入学习属性前言一、什么是焦点(Focus)?焦点的特点 二、窗体属性一、窗体的结构二、窗体的属性三、事件四、方法 一.控件属性一. 标签 Label二.文本框 TextBox2.常用事件 三.命令按钮事件 总结 前言 今天我们来继续学习VB中的属性…

Java全系工程源码加密,防止反编译

一、前言 在大约2015年左右&#xff0c;由于公司产品序列逐渐增加&#xff0c;涉及到N多项目部署交付&#xff0c;为了有效防止产品被滥用&#xff0c;私自部署&#xff0c;当时技术中心决定开发一套统一的授权认证管理系统&#xff0c;对公司所有产品序列进行 License 控制。…

Kotlin:为什么创建类不能被继承

一、为什么创建类不能被继承 class或data class 默认情况下&#xff0c;Kotlin 类是最终&#xff08;final&#xff09;的&#xff1a;它们不能被继承。 示例&#xff1a;data class PsersonBean 反编译data class PsersonBean 生成 public final class PsersonBean 示例&…

材料科学类3区SCI,仅13天超快上线见刊,国人友好!!

录用案例 JCR3区材料类SCI (3.31截稿) 【期刊简介】IF&#xff1a;3.0-4.0&#xff0c;JCR3区&#xff0c;中科院4区&#xff1b; 【检索情况】SCI在检&#xff1b; 【征稿领域】低温环境下新型生物降解材料的开发相关或结合研究均可&#xff1b; 【案例分享】重要时间节点…

伪分布式Spark集群搭建

一、软件环境 软 件 版 本 安 装 包 VMware虚拟机 16 VMware-workstation-full-16.2.2-19200509.exe SSH连接工具 FinalShell Linux OS CentOS7.5 CentOS-7.5-x86_64-DVD-1804.iso JDK 1.8 jdk-8u161-linux-x64.tar.gz Spark 3.2.1 spark-3.2.1-bin-…

PostgreSQL YUM安装

docker中的centos7中安装 选择对应的版本然后在容器中的centos7中执行下面命令 但是启动容器的时候需要注意 开启端口映射开启特权模式启动init进程 docker run -itd --name centos-postgresql -p 5433:5432 --privilegedtrue centos:centos7 /usr/sbin/init 启动然后进入后先…

java SSM在线学习网站系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM在线学习网站系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用…

【基于HTML5的网页设计及应用】——改变文字和背景颜色

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

有手就会Python自定义模块使用

1.自定义模块 自定义模块一般是在项目中根据自己的需求进行的封装 项目中自定义了额一个模块&#xff0c;module.py name "张三" age 23 weight 160 height 187 def test(): print("测试的方法") def demo(): print("天使的眼泪") …

2024年Twitter上最值得关注的26名顶级程序员

2023年7月23日&#xff0c;在Twitter发布17年后&#xff0c;马斯克在Twitter上表示&#xff0c;是时候将该平台更名为X了。 对很多人来说&#xff0c;Twitter是一个分享他们对社会新闻或人生重大事件的想法的地方&#xff0c;这里也是紧跟文化潮流、获取全球最新消息的最佳方式…

如何更简捷地在 Java 中进行函数式编程

public static void findNemo(List names) { boolean found false; for(String name : names) { if(name.equals(“Nemo”)) { found true; break; } } if(found) System.out.println(“Found Nemo”); else System.out.println(“Sorry, Nemo not found”); } …

392.判断子序列

题目&#xff1a;给定字符串s和t&#xff0c;判断s是否为t 的子序列。 字符串的一个子序列是原始字符串删除一些字符而不改变剩余字符相对位置形成的新字符串。 解题思路&#xff1a;s是否是 t 的子序列&#xff0c;因此只要能找到任意一种 s 在 t 中出现的方式&#xff0c;即…

代码随想录day19(1)二叉树:完全二叉树节点个数(leetcode222)

题目要求&#xff1a;求一个完全二叉树的节点个数 思路&#xff1a;首先完全二叉树可以用普通二叉树的方法来求&#xff0c;但是需要遍历所有的节点。 但是对于完全二叉树来说&#xff0c;只有最底层右侧的节点可能没满&#xff0c;其余每层节点都达到了最大值。所以我们可以…

Nwatch在stm32上的移植

目录 Nwatch在stm32上的移植前言实验目的移植game1_task任务相关代码片段结果本文中使用的工程 Nwatch在stm32上的移植 本文目标&#xff1a;Nwatch在stm32上的移植 按照本文的描述&#xff0c;应该可以跑通实验并举一反三。 先决条件&#xff1a;装有编译和集成的开发环境&…

Linux进程状态

目录 1.R运行状态&#xff08;running&#xff09; 2.S睡眠状态&#xff08;sleep&#xff09; 3.T或t状态&#xff08;stopped 或 tracing stop&#xff09; 4.Z状态&#xff08;zombie&#xff09;&#xff08;僵尸进程&#xff09; 1.R运行状态&#xff08;running&…