还在担心报表不好做?不用怕,试试这个方法

前言

在各种业务场景中,我们经常需要生成各种报表,例如学校中的学生成绩表、商业场景中的销售单和发票单、测量检测场景中的检测报告等等。这些报表对于组织和管理数据非常重要,因为它们提供了直观、清晰的方式来展示和分析数据。

一般情况下,使用Excel设计和生成这些报表就可以,但是随着业务规模的扩大,需求也会逐渐由人工转变为电脑自动批量生成,这个时候再用Excel设计就非常慢了,因此今天小编为大家介绍一下如何使用编程语言来设计一个报表。

操作步骤

下图是一个简单的学生成绩表,以下表为例给大家介绍一下操作的具体步骤:

1. 抽象数据结构

首先按照报表中的字段(学号、姓名等)抽象对应的数据结构:

class Data {public String School;public int Grade;public int Class;public String PrintDate;public ArrayList<Score> Scores = new ArrayList<>();
}class Score {public int SID;public String SName;public int Chinese;public int Math;public int English;public Score(int sid, String sName, int chinese, int math, int english) {this.SID = sid;this.SName = sName;this.Chinese = chinese;this.Math = math;this.English = english;}
}

2. 传数据

第二步小编将数据传入到第一步生成数据结构的Scores 队列中。

private static Data CreateData() {Data data = new Data();data.School = "高新第六小学";data.Grade = 1;data.Class = 2;data.PrintDate = "2023年1月5日";data.Scores.add(new Score(1, "李明", 98, 100, 96));data.Scores.add(new Score(2, "王芳", 96, 97, 98));data.Scores.add(new Score(3, "张锋", 99, 99, 95));data.Scores.add(new Score(4, "高明", 94, 96, 100));data.Scores.add(new Score(5, "沈梦", 90, 94, 93));data.Scores.add(new Score(6, "张菲", 93, 94, 95));data.Scores.add(new Score(7, "白洁", 95, 92, 94));data.Scores.add(new Score(8, "王鹏", 96, 97, 99));data.Scores.add(new Score(9, "刘玲", 96, 93, 94));data.Scores.add(new Score(10, "李丽", 94, 98, 99));return data;
}

3. 生成报表逻辑

生成数据之后,下面是编写生成一个报表的代码,该代码创建了一个工作簿并添加了一个工作表。除了添加数据外,还配置了报表所需的样式。

可以看到,该代码量不小且与业务高度相关。如果报表的样式或布局发生变化,就需要调整代码,这种情况下,维护成本会很高。

Data data = CreateData();Workbook workbook = new Workbook();
IWorksheet worksheet = workbook.getWorksheets().get(0);worksheet.getRange("A1").setValue(data.School);
worksheet.getRange("A3").setValue("年级");
worksheet.getRange("B3").setValue(data.Grade);
worksheet.getRange("D3").setValue("班级");
worksheet.getRange("E3").setValue(data.Class);Object[] array = new Object[]{"学号", "姓名", "语文", "数学", "英语"};
worksheet.getRange("A5:E5").setValue(array);worksheet.getRange("D7").setValue("打印日期");
worksheet.getRange("E7").setValue("2023年1月5日");//报表样式
worksheet.getRange("A1:E1").setColumnWidthInPixel(140);
worksheet.getRange("1:2").setRowHeightInPixel(50);worksheet.getRange("A1:E1").merge();
worksheet.getRange("A1").getFont().setSize(16);
worksheet.getRange("A1").setHorizontalAlignment(HorizontalAlignment.Center);worksheet.getRange("B3,E3").getBorders().get(BordersIndex.EdgeBottom).setColor(Color.GetBlack());
worksheet.getRange("A3:E3,D7").setHorizontalAlignment(HorizontalAlignment.Center);worksheet.getRange("E7").setHorizontalAlignment(HorizontalAlignment.Right);int baseRow = 5;
int dataRowCount = data.Scores.size();
worksheet.getRange(5, 0, dataRowCount, 1).getEntireRow().insert(InsertShiftDirection.Down);
for (int i = 0; i < data.Scores.size(); i++) {worksheet.getRange(baseRow + i, 0).setValue(data.Scores.get(i).SName);worksheet.getRange(baseRow + i, 1).setValue(data.Scores.get(i).SID);worksheet.getRange(baseRow + i, 2).setValue(data.Scores.get(i).Chinese);worksheet.getRange(baseRow + i, 3).setValue(data.Scores.get(i).Math);worksheet.getRange(baseRow + i, 4).setValue(data.Scores.get(i).English);
}worksheet.getRange("A5:E5").getInterior().setColor(Color.FromArgb(21, 96, 130));
worksheet.getRange("A5:E5").getFont().setColor(Color.GetWhite());
worksheet.getRange("A5:E5").setHorizontalAlignment(HorizontalAlignment.Center);worksheet.getRange(baseRow, 0, dataRowCount, 5).getInterior().setColor(Color.FromArgb(192, 230, 245));
worksheet.getRange(baseRow - 1, 0, dataRowCount + 1, 5).getBorders().setColor(Color.FromArgb(68, 179, 225));
worksheet.getRange(baseRow - 1, 0, dataRowCount + 1, 2).setHorizontalAlignment(HorizontalAlignment.Center);worksheet.getSheetView().setDisplayGridlines(false);

4. 通过模板简化生成报表逻辑代码

通过第三步的代码可以发现,许多样式,如字体、列宽、行高和颜色等都是相同的,但数据和布局却与业务密切相关,并随着报表而变化。为了解决上述问题,可以将报表抽象为一个Excel模板,并保留不变的内容,移除可变的内容,如下图所示:

这样小编就可以简化报表样式部分的代码,保留核心代码,如下所示:

Data data = CreateData();Workbook workbook = new Workbook();
workbook.open("template.xlsx");
IWorksheet worksheet = workbook.getWorksheets().get(0);worksheet.getRange("A1").setValue(data.School);
worksheet.getRange("B3").setValue(data.Grade);
worksheet.getRange("E3").setValue(data.Class);
worksheet.getRange("E8").setValue("2023年1月5日");int baseRow = 5;
int dataRowCount = data.Scores.size();
worksheet.getRange(baseRow + 1, 0, dataRowCount - 1, 1).getEntireRow().insert(InsertShiftDirection.Down);
worksheet.getRange(baseRow, 0, 1, 5).copy(worksheet.getRange(baseRow + 1, 0, dataRowCount - 1, 5));
//循环给报表中的字段传值
for (int i = 0; i < data.Scores.size(); i++) {worksheet.getRange(baseRow + i, 0).setValue(data.Scores.get(i).SName);worksheet.getRange(baseRow + i, 1).setValue(data.Scores.get(i).SID);worksheet.getRange(baseRow + i, 2).setValue(data.Scores.get(i).Chinese);worksheet.getRange(baseRow + i, 3).setValue(data.Scores.get(i).Math);worksheet.getRange(baseRow + i, 4).setValue(data.Scores.get(i).English);
}

可以看到,剥离掉样式的代码,简化了很多。但是,布局的调整,尤其是需要根据数据量调整行列,还是没有做到完全的业务解耦。

5. 使用模板语言二次简化代码

为了进一步解决第四步中的问题,我们可以通过模板语言,将报表改造成模板文件,来彻底做到业务解耦。将业务需求留在模板文件中,大大降低了代码维护的成本。下面小编对第四步的模板进行一些改造,如下图所示:

可以看到,和第四步的模板相比,新的模板将字段(年龄、班级等)对应的值以参数值表示,以{{ds.School}}为例,模板语言由两个大括号组成,中间的字符串表示从名为ds的数据源中,将School字段填充至 C1 单元格中。

下面是使用报表语言后简化后的代码:

Workbook workbook = new Workbook();
workbook.open("template.xlsx");
workbook.addDataSource("ds",CreateData());
workbook.processTemplate();
workbook.save("reprot.xlsx");

展示效果:

总结

本文所用到的所有代码均来自葡萄城公司的服务端表格控件产品GcExcel。如果您想了解更多信息,可以参考这篇产品文档及Demo 网站。

扩展链接:

Redis从入门到实践

一节课带你搞懂数据库事务!

Chrome开发者工具使用教程

如何在Web应用中添加一个JavaScript Excel查看器

高性能渲染——详解HTML Canvas的优势与性能

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

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

相关文章

上海共享办公室的现状与未来

共享办公室&#xff0c;又称联合办公、众创空间、孵化器等&#xff0c;是一种将空闲的办公空间出租给不同的企业或个人&#xff0c;提供灵活的租期、价格和服务的新型办公模式。共享办公室的出现&#xff0c;满足了新兴企业和自由职业者对于低成本、高效率、多元化的办公需求&a…

工程监测仪器振弦采集仪详细的稳定性测试

工程监测仪器振弦采集仪详细的稳定性测试 振弦采集仪是一种常用于工程监测的仪器&#xff0c;用于测量结构的振动和变形。稳定性测试是评估采集仪的测量稳定性和精度的一种方法&#xff0c;可以确保采集仪在长时间使用中的准确性和可靠性。 稳定性测试的步骤如下&#xff1a; …

开源大规模分布式MQTT消息服务器EMQX部署教程

1.EMQX是什么&#xff1f; EMQX 是一款开源的大规模分布式 MQTT 消息服务器&#xff0c;功能丰富&#xff0c;专为物联网和实时通信应用而设计。EMQX 5.0 单集群支持 MQTT 并发连接数高达 1 亿条&#xff0c;单服务器的传输与处理吞吐量可达每秒百万级 MQTT 消息&#xff0c;并…

Hadoop增加新节点环境配置(自用)

完成Hadoop集群增添一个新的节点配置&#xff08;文中命名为&#xff09;Hadoop106&#xff0c;没有进行继续为该节点分配身份职能的步骤 1.在VMware中安装CentOS 7 新建虚拟机 1.⾸先我们创建⼀个新的虚拟机&#xff0c;也可以点⽂件-新建虚拟机。 2.选择⾃定义&#xff0c…

三维模型设计新纪元:3D开发工具HOOPS在机械加工行业的应用与优势

在当今快速发展的科技时代&#xff0c;机械加工行业正经历着巨大的变革&#xff0c;而HOOPS技术正是其中一项重要的创新。HOOPS技术不仅仅是一种用于处理和可视化计算机辅助设计&#xff08;CAD&#xff09;数据的工具&#xff0c;更是机械加工领域中提升效率、优化设计的利器。…

Mac安装及配置MySql及图形化工具MySQLworkbench安装

Mac下载配置MySql mysql下载及安装 下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/ 根据自己电脑确定下载x86还是ARM版本的 如果不确定&#xff0c;可以查看自己电脑版本&#xff0c;终端输入命令 uname -a 点击Download下载&#xff0c;可跳过登录注册&…

js 项目中我们会先创建一个空对象 然后到了后面可能需要再对空对象进行判断 略显复杂

//判断是不是空对象isEmptyObject(obj) {return Object.keys(obj).length 0;}, 假如 我们把 let observeParams 设置为null呢 就可以直接通过判断 if(observeParams){...} 来做判断 而不是判断空对象

[GXYCTF2019]禁止套娃(特详解)

刚打开页面什么都没有&#xff0c;抓包也什么都没有 那就dirsaerch扫一下&#xff0c;发现状态码都是429&#xff0c;访问太快了&#xff08;这里很多师傅都没有说明或者说清楚&#xff09; 这里改了一下线程&#xff08;kali自带的&#xff0c;如果用的脚本要加前面要加python…

关于如何将Win幻兽帕鲁服务端存档转化为单人本地存档的一种方法(无损转移)

本文转自博主的个人博客&#xff1a;https://blog.zhumengmeng.work,欢迎大家前往查看。 原文链接&#xff1a;点我访问 **起因&#xff1a;**最近大火的开放世界缝合体游戏幻兽帕鲁的大火也是引起了博主的注意&#xff0c;然后博主和周边小伙伴纷纷入手&#xff0c;博主也是利…

java中Filter的使用

1.1 Filter概述 Filter 表示过滤器&#xff0c;是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。Servlet 我们之前都已经学习过了&#xff0c;Filter和Listener 我们今天都会进行学习。 过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能。 如下图…

Python入门到精通(六)——Python函数进阶

Python函数进阶 一、函数的多返回值 二、函数多种传参方式 1、位置参数 2、关键字参数 3、缺省参数 4、不定长参数 &#xff08;1&#xff09;位置传递 &#xff08;2&#xff09;关键字传递 三、匿名函数 &#xff08;1&#xff09;函数作为参数传递 &#xff08;2&…

通过Demo学WPF—数据绑定(一)✨

前言✨ 想学习WPF&#xff0c;但是看视频教程觉得太耗时间&#xff0c;直接看文档又觉得似懂非懂&#xff0c;因此想通过看Demo代码文档的方式进行学习。 准备✨ 微软官方其实提供了WPF的一些Demo&#xff0c;地址为&#xff1a;microsoft/WPF-Samples: Repository for WPF …

day25 日期对象

日期对象 日期对象&#xff1a;用来表示时间的对象作用&#xff1a;可以得到当前系统时间 实例化 在代码中发现了new关键字时&#xff0c;一般将这个操作称为实例化创建一个时间对象并获取时间&#xff08;可以用于计算倒计时&#xff09; 获得当前时间const date new Date…

QT 使用XML保存操作记录

文章目录 1 实现程序保存操作记录的思路2 XML文档基本结构3 QDomDocument实现XML读写3.1 QDomDocument实现生成XML文件3.2 QDomDocument实现读取XML文件 4 QXmlStreamWriter实现读写4.1 QXmlStreamWriter实现生成XML4.2 QXmlStreamWriter实现读取XML 1 实现程序保存操作记录的思…

【linux】-centos7版本前后-变化篇

1.centos7版本前后区别 首先文件系统变化&#xff0c;由EXT4&#xff0c;变为XFS格式。可支持容量500TB的文件&#xff0c;而6代仅能支持16TB。首个进程变为systemd, 替换了熟悉的init进程。它的特点是功能强大&#xff0c;体积也很强大。 systemd给我们带来了一个全家桶命令&…

【linux】磁盘空间不足-常用排查和处理命令

【linux】磁盘空间不足-常用排查和处理命令 1.通查一下 df -h #查看服务器磁盘空间情况 du -hs * 2>/dev/null #列出各目录所占空间大小 或 du -h -d 1 2>/dev/null #列出各目录所占空间大小 1.1情况一 df 磁盘空间和du 目录空间占用相等&#xff0c…

C# 二分搜索(Binary Search)

二分搜索概念 二分查找也称折半查找&#xff08;Binary Search&#xff09;它是一种效率较高的查找方法。但是&#xff0c;折半查找要求线性表必须采用顺序存储结构&#xff0c;而且表中元素按关键字有序排列。 二分搜索的背景 二分搜索法的概念和思想可以追溯到古代的中国和…

【python】求矩阵的特征值和特征向量

使用np.linalg.eig同时求特征值和特征向量 import numpy as np#输入矩阵 A np.array([[1, 1/2, 1/6, 1/9],[2, 1, 1/3, 1/5],[6, 3,1,1/2],[9, 5,2,1]])#求解特征值和其对应的特征向量 eigval,eigvec np.linalg.eig(A) for i in range(len(eigval)):print(f特征值&#xff1a…

STM32学习笔记一——初识STM32

目录 一、什么是ARM 二. Cortex 内核 三.什么是STM32 四.STM32核心板原理图&#xff1a; 五.STM32的内部结构&#xff1a; 六.stm32系统结构简化图 STM32基本原理分析&#xff1a; 七.典型型号——STM32F103ZET6 stm32——32位单片机&#xff08;数据总线是32位的&am…

文献速递:人工智能医学影像分割--- 深度学习分割骨盆骨骼:大规模CT数据集和基线模型

文献速递&#xff1a;人工智能医学影像分割— 深度学习分割骨盆骨骼&#xff1a;大规模CT数据集和基线模型 我们为大家带来人工智能技术在医学影像分割上的应用文献。 人工智能在医学影像分析中发挥着至关重要的作用&#xff0c;尤其体现在图像分割技术上。这项技术的目的是准…