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

前言

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

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

操作步骤

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

  1. 抽象数据结构

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

Java 复制代码
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;
    }
}
  1. 传数据

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

Java 复制代码
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;
}
  1. 生成报表逻辑

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

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

Java 复制代码
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);
  1. 通过模板简化生成报表逻辑代码

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

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

Java 复制代码
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);
}

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

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

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

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

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

Java 复制代码
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的优势与性能

相关推荐
前行的小黑炭18 分钟前
设计模式:为什么使用模板设计模式(不相同的步骤进行抽取,使用不同的子类实现)减少重复代码,让代码更好维护。
android·java·kotlin
Java技术小馆23 分钟前
如何设计一个本地缓存
java·面试·架构
XuanXu1 小时前
Java AQS原理以及应用
java
风象南4 小时前
SpringBoot中6种自定义starter开发方法
java·spring boot·后端
mghio13 小时前
Dubbo 中的集群容错
java·微服务·dubbo
咖啡教室18 小时前
java日常开发笔记和开发问题记录
java
咖啡教室18 小时前
java练习项目记录笔记
java
鱼樱前端18 小时前
maven的基础安装和使用--mac/window版本
java·后端
RainbowSea19 小时前
6. RabbitMQ 死信队列的详细操作编写
java·消息队列·rabbitmq