EasyExcel模板导出与公式计算(下)

目录

环境要求

功能预览

需求分析

导入依赖

制作模板

编写代码

格式优化

最终效果

总结


在上一篇 EasyExcel模板导出与公式计算(上)-CSDN博客 文章中我们知道了在若依中使用自带的@Excel注解来实现表格数据的导出,并且通过重写相关接口来支持导出公式的自动计算功能,而在本文中,我们将探究如何在带有自定义表头的定制化场景下,如何实现表格数据的导出和公式的自动计算。

环境要求

若依Cloud 3.6.3

EasyExcel  3.3.2

功能预览

与上一篇文章相同,这里继续以若依用户管理的导出功能为示例,做一个简单的公式计算的验证,大家可根据自己需求修改相应代码。

首先本地启动若依微服务版本,登录后台,依次选择系统管理 —> 用户管理菜单,进入如下页面

 点击该页面的导出按钮

获取如下导出文件

现在如果我们想要在第四行计算第二和第三行用户编号的和,可以直接在B4单元格输入"=A2+A3"这样的公式来实现,可以注意到此时B4单元格中的公式颜色已经发生变化,并且自动选中了A2和A3单元格,但实际上我们的需求为从数据库表中导出的单元格实现自动计算结果,接下来将通过若依前端页面来实现该功能验证。

现在需求变为除了能够正确计算单元格数据外,还要变成像如下图的导出样式:

不难看出,我们需要导出的结果有自定义的表头,以及时间,并且需要特别注意的是,原本的计算公式由=A2+A3,变为了=A5+A6,这是因为新增了表头导致行号发生了变化,接下来将针对这种需求进行实现。

需求分析

由于有了自定义标头的要求,显然我们不能按照上一篇文章中直进行数据的导出,因此我们将使用EasyExcel实现该功能

导入依赖

首先在ruoyi-common-core的pom.xml添加EaysExcel的依赖,之所以添加到这里是因为该模块为通用核心模块,会在项目中被其他的模块引用,可以实现“一处添加,多处使用”的效果。

<!-- easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.2</version></dependency>

制作模板

为了实现上述中的导出效果,我们需要使用Excel制作一个用于导出的模板,最终效果如下所示:

可以看到图中红色框所标识的是新增的表头内容,其中时间后面的{dataDate} 则为填充对象,而第5行对应的内容中除了花括号外,还有一个点,这其实是代表的是将使用对象集合填充,与时间的dataDate区别为, dataDate仅为一个对象。

编写代码

由于我们使用了EasyExcel之后,原有的导出功能需要做调整, @Excel注解将被替换为@ExcelProperty,如下图所示,其中value属性对应了我们的模板中的表头

对应Controller层的代码也需要做如下调整:

@Log(title = "用户管理", businessType = BusinessType.EXPORT)@RequiresPermissions("system:user:export")@PostMapping("/export")public void export(HttpServletResponse response, SysUser user){List<SysUser> list = userService.selectUserList(user);//ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);//util.exportExcel(response, list, "用户数据");try(InputStream resourceAsStream =  SysUserController.class.getClassLoader().getResourceAsStream("UserExportTemplate.xlsx");ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(resourceAsStream).inMemory(true).build()) {response.setContentType("application/vnd.ms-excel");String filename=  URLEncoder.encode("用户管理数据导出","UTF-8");response.setHeader("Content-disposition","attachment;filename="+filename+".xlsx");WriteSheet writeSheet = EasyExcel.writerSheet().build();FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();excelWriter.fill(list, fillConfig, writeSheet);Map<String, Object> map = MapUtils.newHashMap();// 自定义日期map.put("dataDate", new Date());excelWriter.fill(map, writeSheet);// 拿到原始的Workbook对象Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();// 获取已经填充的sheet对象Sheet userSheet = workbook.getSheetAt(0);for(Row row : userSheet){for (Cell cell : row){if(cell.getCellType() == CellType.STRING &&StringUtils.isNotBlank(cell.getStringCellValue())&& cell.getStringCellValue().contains("=")){cell.setCellFormula(cell.getStringCellValue().substring(1));}}}// 关闭excelWriter.finish();} catch (IOException e) {e.printStackTrace();}}

 我们首先获取到了用户信息的list对象, 随后加载自定义的模板 UserExportTemplate.xlsx ,使用EasyExcel 构建一个 WriteSheet 对象,其作用为填充自定义的日期对象以及我们的查询的list对象,待填充完成后。通过excelWriter再次获取到初始的WorkBook对象,在这里就是如何解决公式自动计算的关键,通过workBookt获取到Sheet对象后对其进行遍历,依次获取到Row和Cell对象, 这里的Cell对象就是我们的单元格,而我们填值的单元格为String类型,因此需要进行过滤,并且此处需要特别注意,去掉公式中原有的 = 符号,否则会引发异常。最后需要使用finish方法关闭流。该方案来源于官方文档easyexcel模板填充后,有公式的单元格不会自动计算,而需要我手动算,如何处理? | Easy Excel 官网

以及github上的 issue https://github.com/alibaba/easyexcel/issues/3242 

格式优化

虽然上述内容已经解决了公式导出后无法自动计算的问题,但是如果仔细观察就会发现有个问题可以进一步优化,由于最开始填入的单元格公式为A2+A3,而当增加了自定义表头后,此处内容应变为A5+A6,在Excel中这种情况会自动处理,但在当前场景下,只能通过手动方式去修正最终的单元格,而如果带有公式的单元格非常多时就会变得非常麻烦,因此可以通过如下代码进行调整:

 @Log(title = "用户管理", businessType = BusinessType.EXPORT)@RequiresPermissions("system:user:export")@PostMapping("/export")public void export(HttpServletResponse response, SysUser user){List<SysUser> list = userService.selectUserList(user);//ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);//util.exportExcel(response, list, "用户数据");try(InputStream resourceAsStream =  SysUserController.class.getClassLoader().getResourceAsStream("UserExportTemplate.xlsx");ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(resourceAsStream).inMemory(true).build()) {response.setContentType("application/vnd.ms-excel");String filename=  URLEncoder.encode("用户管理数据导出","UTF-8");response.setHeader("Content-disposition","attachment;filename="+filename+".xlsx");WriteSheet writeSheet = EasyExcel.writerSheet().build();FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();excelWriter.fill(list, fillConfig, writeSheet);Map<String, Object> map = MapUtils.newHashMap();// 自定义日期map.put("dataDate", new Date());excelWriter.fill(map, writeSheet);// 根据自定义表头调整公式int rowOffset = 3;// 拿到原始的Workbook对象Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();// 获取已经填充的sheet对象Sheet userSheet = workbook.getSheetAt(0);for(Row row : userSheet){for (Cell cell : row){if(cell.getCellType() == CellType.STRING &&StringUtils.isNotBlank(cell.getStringCellValue())&& cell.getStringCellValue().contains("=")){// 获取当前单元格内容String formula = cell.getStringCellValue();// 正则表达式匹配单元格引用 (例如 A1, B2 等)Pattern pattern = Pattern.compile("([A-Z]+)(\\d+)");Matcher matcher = pattern.matcher(formula);StringBuffer adjustedFormula = new StringBuffer();while(matcher.find()) {// 列名 (A, B, ...)String column = matcher.group(1);// 行号 (1, 2, ...)int rowNum = Integer.parseInt(matcher.group(2));// 调整行号int adjustedRow = rowNum + rowOffset;// 替换原始引用为调整后的引用matcher.appendReplacement(adjustedFormula, column + adjustedRow);}matcher.appendTail(adjustedFormula);// 重新设置公式cell.setCellFormula(adjustedFormula.substring(1));}}}// 关闭excelWriter.finish();} catch (IOException e) {e.printStackTrace();}}

此处通过在验证公式时使用正则匹配原有的公式,并根据设置的 rowOffset 动态进行调整(rowOffset为3是因为当前填入的公式从A2为基准开始,因此偏移量为3而并非4),使其能够正确计算结果。(此处代码也可抽取为方法在查询时list对象时直接使用)

最终效果

可以看到前端页面中填入的内容为=A2+A3,而导出文件内容为=A5+A6,并且打开导出的文件后自动计算了结果。

总结

对于具有自定义表头内容的导出需求,需要使用EasyExcel工具实现功能增强,并且需要先将内容写入到内容中之后,再读取将公式进行进一步过滤转换(去除等号,调整行号),进而实现导出文文件的计算功能,根据官方文档所述,该方案适用于文件小的场景,当文件过大时将可能造成内存溢出的问题,因此官方建议在Java层实现对公式的计算后再进行导出,如果各位读者有更好的想法欢迎评论区留言探讨。

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

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

相关文章

【Python 千题 —— 算法篇】无重复字符最长子段

Python 千题持续更新中 …… 脑图地址 &#x1f449;&#xff1a;⭐https://twilight-fanyi.gitee.io/mind-map/Python千题.html⭐ 题目背景 在编程过程中&#xff0c;处理字符串的任务时常遇到&#xff0c;其中一个经典问题是查找无重复字符的最长子串。这在很多应用场景中…

redisson中的分布式锁

我的博客大纲 我的后端学习大纲 a.redisson概述&#xff1a; 1.Redisson是一个在Redis的基础上实现的Java驻内存数据网格&#xff08;In-Memory Data Grid&#xff09;2.redisson介绍官方文档地址&#xff1a;3.Redisson它不仅提供了一系列的分布式的Java常用对象&#xff0c;还…

使用vscode编辑matlab完美解决方法

vscode里面的matlab插件都不好用&#xff0c;需要搭配互补一下 1先安装MATLAB 这个插件可以进行代码高亮、格式化、跳转&#xff0c;F5运行所有代码&#xff0c;或者选中要运行的代码&#xff0c;右键单独运行&#xff0c; 优点&#xff1a;运行速度很快&#xff0c;和matlab里…

vcruntime140.dll丢失报错处理及dll下载修复方法

概述 vcruntime140.dll是Visual C Redistributable for Visual Studio的一个动态链接库文件。 如果你在运行某个程序时遇到了vcruntime140.dll丢失的错误&#xff0c;你可以尝试以下解决方法&#xff1a; 重新安装程序&#xff1a; 如果你只在运行某个特定程序时出现了该错误…

云手机怎样简化海外社媒平台运营

随着越来越多的卖家希望拓展海外市场&#xff0c;运营TikTok、Facebook等社交媒体平台已经成为吸引流量和促进销售的重要手段。然而&#xff0c;在管理海外社媒账号的过程中&#xff0c;许多人会面临网络连接的问题。这时&#xff0c;使用一款高效便捷的云手机工具就显得尤为便…

心理辅导新篇章:Spring Boot学生评估系统

1 绪论 1.1 研究背景 现在大家正处于互联网加的时代&#xff0c;这个时代它就是一个信息内容无比丰富&#xff0c;信息处理与管理变得越加高效的网络化的时代&#xff0c;这个时代让大家的生活不仅变得更加地便利化&#xff0c;也让时间变得更加地宝贵化&#xff0c;因为每天的…

JavaScript 循环控制语句-break和continue

break循环 首先i0&#xff0c;判断i是否<5,满足条件&#xff0c;判断i是否等于3&#xff0c;i不等于3&#xff0c;输出i0&#xff0c;i的值加1&#xff0c;判断i是否<5&#xff0c;判断i是否等于3&#xff0c;i不等于3&#xff0c;输出i1&#xff0c;i的值加1&#xff0c…

高精度加法,减法,乘法,除法

加法&#xff1a; 大整数该如何储存&#xff1f; 用数组储存&#xff1a; 把个位放在数下标为0的位置&#xff0c;十位放在数组下标为1的位置&#xff08;也就是高位放在数组的后面&#xff09; 因为这样&#xff0c;如果需要增加一位最高位&#xff0c;那我们就可以直接在…

前端基础 | HTML基础:HTML结构,HTML常见标签

文章目录 HTML1、HTML结构1.1HTML标签1.1.1标签1.1.2标签含义 1.2HTML文件基本结构1.3标签层次结构1.4 快速生成代码框架 2、HTML常见标签2.1注释标签2.2标题标签&#xff1a;h1–h62.3段落标签&#xff1a;p2.4 换行标签&#xff1a;br2.5格式化标签2.6 图片标签&#xff1a;i…

git submodule子模块的使用

子模块的使用 添加子模块 添加子模块 git submodule add <子仓库URL> <子仓库路径> 例子&#xff1a; git submodule add http://192.168.100.181/guideir/poco.git 3rdparty/poco 若子模块存在好几个分支&#xff0c;可以在添加子模块时&#xff0c;指定分支 g…

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位&#xff1a;为1时表示在内存期间被访问过&#xff0c;为0时表示未被访问&#xff1b;修改位&#xff1a;为1时表示该页面自从被装入内存后被修改过&#xff0c;为0时表示未修改过。 置换页面时&#xff0c;最先置换访问位和修改位为…

mac安装spark

参考&#xff1a;在Mac上安装Spark apache-spark-3.5.1_mac安装spark-CSDN博客 几个需要用到的路径&#xff1a; hadoop的bin目录&#xff1a;/opt/homebrew/Cellar/hadoop/3.4.0/bin spark的conf目录/opt/homebrew/Cellar/apache-spark/3.5.2/libexec/conf spark的bin目录&am…

iOS——retain和release底层原理

retain实现原理 retain的源码&#xff1a; //使用此方法等价于使用[this retain] inline id objc_object::retain() {//确保对象不是tagged pointerASSERT(!isTaggedPointer());return rootRetain(false, RRVariant::FastOrMsgSend); }ALWAYS_INLINE id objc_object::rootR…

VR虚拟展厅的应用场景有哪些?

虚拟展厅作为一种利用虚拟现实技术构建的三维展示空间&#xff0c;其应用场景广泛且多样。视创云展为企业虚拟展厅搭建提供技术支持。以下是一些主要的应用场景&#xff1a; 1. 博物馆和艺术展览 文物保护与展示&#xff1a; 在博物馆中&#xff0c;为了保护珍贵的文物和艺术…

数据结构与算法学习day20-二叉树的最大深度、最小深度、完全二叉树的节点个数、平衡二叉树、二叉树所有路径

一、二叉树的最大深度 1.题目 104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#xff09; 2.思路 2.1递归法 二叉树节点的深度&#xff1a;指从根节点到该节点的最长简单路径边的条数或者节点数&#xff08;取决于深度从0开始还是从1开始&#xff09;二叉树节点的高度…

【Python 学习】Pandas基础与应用(1)

题目 1 Pandas 简介1.1 主要特征1.2 Pandas 安装 2 Pandas中的数据结构2.1 Series 数据结构和操作2.1.1 Series的数据结构2.1.2 Seres的操作 2.2 DataFrame 数据结构和操作2.2.1 DataFrame 数据结构2.2.2 Dataframe 操作2.2.3 DateFrame 的特殊操作 2.3 Series 和 DataFrame 的…

Linux——网络基础Socket编程

目录 一计算机网络背景 二协议 1初始协议 1.1协议分层 1.2OSI七层模型 1.3TCP/IP五层模型 2再始协议 2.1为什么要有TCP/IP协议 2.2TCP/IP与OS的关系 2.3所以什么是协议 三网络传输基本流程 1局域网&#xff08;以太网&#xff09;通信原理 1.1认识mac地址 2同…

【牛站 / USACO2007】

题目 思路 离散化&#xff08;降低空间复杂度&#xff09; 点的编号 ∈ [ 1 , 1000 ] &#xff0c;但是点的个数最多为 2 ⋅ T ∈ [ 4 , 200 ] 点的编号 \in [1, 1000]&#xff0c;但是点的个数最多为 2 \cdot T \in[4, 200] 点的编号∈[1,1000]&#xff0c;但是点的个数最多为…

python文件自动化(4)

接上节课内容&#xff0c;在开始正式移动文件到目标文件夹之前&#xff0c;我们需要再思考一个问题。在代码运行之前&#xff0c;阿文的下载文件夹里已经存在一些分类文件夹了&#xff0c;比如图例中“PDF文件”这个文件夹就是已经存在的。这样的话&#xff0c;在程序运行时&am…

电脑硬盘数据丢失了怎么恢复?简单实用的硬盘数据找回的方法

我们的电脑使用硬盘作为存储设备来保存数据&#xff0c;硬盘里的数据是存储在扇区上&#xff0c;这些存储数据的单元则位于表面有磁性材料的旋转的盘片上。硬盘内部的磁头悬浮于高速旋转的盘片上&#xff0c;用于读写和检索数据。 假如我们使用电脑时不小心删除了某个文件&…