部门费用统计表
merge · 场景 0003各部门费用明细,每个部门后跟一行小计,末尾一行合计。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
纯 Java · Apache-2.0 · 基于 Apache POI
纯 Java 生成带图表、公式与样式的 Excel 报表不手写 Workbook,映射、公式、图表全在一份 XML 里
一段 EXPORT WITH DSL 声明列映射、金额格式、滚动期末库存公式、合计行样式与三维面积图;Java 侧构造业务数据、拿到强类型服务代理、调用一次方法,导出即完成 —— 底层由框架驱动 Apache POI,xls / xlsx 双格式、流式导出与 42 套主题开箱即用。

非设计稿:由示例代码运行导出,可下载 xlsx 与 XML 配置逐项对照
List<JQuickRow> rows = inventoryRows(); // 业务数据:月份、期初、入库、出库JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory("forestGreen", rows, out);JQuickFactory factory = new JQuickXmlFactory(parser, "jquick/biz/graph/0107_graph_inventory-trend.xml");Graph0107InventoryTrendService service = factory.createApi(Graph0107InventoryTrendService.class);service.exportInventoryTrend("field", "value"); // 图表、公式、样式全在 XML 里省略 import 与数据构造辅助方法;service 接口由 XML 的 namespace 生成代理,方法名即 XML 里的 excel name。
EXPORT WITH SHEET="库存量趋势", HEADER=true, MAPPING={"a":"月份","b":"期初","c":"入库","d":"出库","e":"期末"}, FORMAT={"b":"#,##0","c":"#,##0","d":"#,##0","e":"#,##0"}, FORMULAS={E2:'B2+C2-D2', E3:'E2+C3-D3', E4:'E3+C4-D4', E5:'E4+C5-D5', C6:'SUM(C2:C5)', D6:'SUM(D2:D5)', E6:'E5'}, STYLE={ROW 6:{bold:true, fillPattern:solid_foreground, fillForegroundColor:lightYellow, alignment:right}}, GRAPH={TYPE=AREA3D, TITLE="库存量趋势图", CATEGORY_AXIS=月份, VALUE_AXIS=库存, CATEGORIES=["1月","2月","3月","4月"], SERIES=[{NAME=期末, DATA=[4800,5200,4600,5100]}]}映射、格式、逐行滚动余额公式、合计行高亮与折线图全部声明式表达;这段 DSL 就是上面截图的完整规则,可下载原始文件直接使用。
Apache POI 是优秀的底层库,JQuick-Excel 正是基于它构建——下表比较的是「写法」,不是「能力」。
| 对比维度 | JQuick-Excel | 手写 Apache POI |
|---|---|---|
| 上手方式 | EXPORT WITH DSL 一段声明完成整表 | 手写 Workbook / Sheet / Row / Cell 逐层构建 |
| 字段映射与格式 | MAPPING + FORMAT 各一行 | 逐列 setCellValue 并手动创建 DataFormat |
| 公式与汇总 | FORMULAS 声明逐行公式与 SUM 合计 | 逐单元格 setCellFormula 并自行维护行号 |
| 图表 | GRAPH 一段 DSL,10 种图表类型 | XDDF 手工构建坐标轴、数据引用与系列 |
| 数据校验 | 20 条内置规则,作用域到行 / 列 / 单元格 | 自行实现校验逻辑与错误提示 |
| 主题样式 | 42 套内置导出主题,按名引用 | 手写 CellStyle 或自建模板文件 |
| 大文件 | 流式导出与批量消费内置 | 自行切换 SXSSF 并管理滑动窗口与样式缓存 |
底层就是 Apache POI:JQuick-Excel 把重复样板收进一份 XML,POI 的能力一个不少,两者同为 Apache-2.0,可放心共存。
JQuick-Excel 是一个轻量级 Java 框架,用于导入与导出 xls 与 xlsx 工作簿。它将 XML 服务定义与声明式 DSL 结合,统一表达字段映射、类型转换、数据校验、公式、样式、合并汇总、图表与页脚。
XML 定义使 Excel 规则与服务契约保持一致:Java 调用方根据该定义创建服务代理,再向导入或导出操作传入业务数据、流与上下文值。框架不强制依赖 Spring,普通 Java 项目与 Spring Boot 项目使用同一套 API;底层基于 Apache POI,运行环境为纯 Java,可在 Windows、Linux、macOS 及信创操作系统上使用。
xls 与 xlsx 工作簿。IMPORT WITH 与 EXPORT WITH 声明导入导出规则。GRAPH DSL 配置。环境要求:
| 项目 | 要求 |
|---|---|
| JDK | Java 8 或更高版本 |
| 工作簿格式 | xls、xlsx |
| 底层依赖 | Apache POI、内置词法与语法分析运行时、Javelin Core、JQuick XML 代理,均为纯 Java |
<dependency> <groupId>io.github.paohaijiao</groupId> <artifactId>jquick-excel</artifactId> <version>3.6.0</version></dependency>版本号请以 Maven Central 上的最新发布版为准。
implementation 'io.github.paohaijiao:jquick-excel:3.6.0'使用 JQuick-Excel 完成一次导入需要三个部分:类路径中的 XML 定义、服务接口、Java 调用代码。
第一步,在 resources 目录创建 jquick-excel.xml:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.StudentService"> <excel name="importExcel" returnClass="java.util.List"> <![CDATA[ IMPORT WITH SHEET="Students", HEADER=true, MAPPING={"ID":"id","Name":"name","Age":"age"} ]]> </excel></excels>第二步,定义与 XML namespace 同名的服务接口:
package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.param.Param;
import java.util.List;
public interface StudentService {
List<JQuickRow> importExcel(@Param("field") String field, @Param("value") String value);}第三步,创建代理并调用:
package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.im.JQuickExcelImportXmlParseFactory;
import java.io.InputStream;import java.util.List;
public class SimpleImportDemo {
public static void main(String[] args) throws Exception { try (InputStream input = SimpleImportDemo.class.getClassLoader() .getResourceAsStream("students.xlsx")) { JQuickParseHandler parser = new JQuickExcelImportXmlParseFactory(input); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); StudentService service = factory.createApi(StudentService.class);
List<JQuickRow> rows = service.importExcel("field", "value"); System.out.println("导入行数:" + rows.size()); } }}在 jquick-excel.xml 中增加导出定义:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.StudentService"> <excel name="exportExcel" returnClass="void"> <![CDATA[ EXPORT WITH SHEET="Students", HEADER=true, MAPPING={"id":"ID","name":"Name","age":"Age"} ]]> </excel></excels>服务接口与 Java 调用代码:
package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.param.Param;
import java.util.List;
public interface StudentService {
void exportExcel(@Param("field") String field, @Param("value") String value);}package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.util.JObjectConverter;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;
import java.io.FileOutputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;
public class SimpleExportDemo {
public static void main(String[] args) throws Exception { List<Map<String, Object>> data = new ArrayList<>(); Map<String, Object> student = new LinkedHashMap<>(); student.put("id", "1001"); student.put("name", "Alice"); student.put("age", 20); data.add(student);
// 将 Map 数据转换为框架行对象 List<JQuickRow> rows = JQuickRow.toRows(JObjectConverter.convert(data));
try (OutputStream output = new FileOutputStream("students.xlsx")) { JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, output); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); StudentService service = factory.createApi(StudentService.class);
service.exportExcel("field", "value"); System.out.println("导出完成"); } }}1=男、2=女)通过 JContext 字典转换为 Excel 中可读的「男 / 女」,出生日期统一格式化为 yyyy-MM-dd,表头加粗着色,并在年龄列末尾追加 SUM 汇总公式。JQuickRow 字段,性别文字反向翻译为内部码值,出生日期同样格式化为 yyyy-MM-dd。jquick-excel.xml,但两者 MAPPING 方向相反:导出是「字段 → 表头」,导入是「表头 → 字段」。Demo 的读写路径与截图位置固定如下,代码中严格使用这些路径:
| 用途 | Windows 本地路径 | 说明 |
|---|---|---|
| 导入测试文件目录 | D:\idea\jquick-docs\public\jquick-excel\import |
测试文件手工放入此目录,程序只读取 |
| 导出文件输出目录 | D:\idea\jquick-docs\public\jquick-excel\export |
程序运行后自动生成导出文件,无需手工创建 |
| 运行截图目录 | D:\idea\jquick-docs\public\jquick-excel\image |
Demo 截图统一存放于此,文档以 /jquick-excel/image/文件名 引用 |
<dependency> <groupId>io.github.paohaijiao</groupId> <artifactId>jquick-excel</artifactId> <version>3.6.0</version></dependency>将测试文件 students.xlsx 放到 D:\idea\jquick-docs\public\jquick-excel\import 目录。文件内只有一张工作表 Sheet1,第一行为表头,内容如下(也可直接下载站点已放置好的文件:students.xlsx):
| 学号 | 姓名 | 性别 | 年龄 | 出生日期 | 班级 |
|---|---|---|---|---|---|
| 2024001 | 张三 | 男 | 20 | 2024-09-01 | 计算机1班 |
| 2024002 | 李四 | 女 | 21 | 2024-09-01 | 计算机2班 |
| 2024003 | 王五 | 男 | 22 | 2024-09-02 | 计算机3班 |
在类路径(src/main/resources)下创建 jquick-excel.xml。导出规则负责表头命名、字典转换、日期格式、表头样式与年龄汇总公式;导入规则负责反向映射:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.StudentExcelService">
<!-- 导出:Java 字段 -> Excel 表头,性别码值经字典转换,日期统一为 yyyy-MM-dd --> <excel name="exportStudents" returnClass="void"> <![CDATA[ EXPORT WITH SHEET="学生信息", HEADER=true, MAPPING={ "id":"学号", "name":"姓名", "gender":"性别", "age":"年龄", "enrollmentDate":"出生日期", "className":"班级" }, TRANSFORM={ "gender":trans(${dict},${gender}), "enrollmentDate":dateFormat(${enrollmentDate},'yyyy-MM-dd') }, FORMULAS={ D5:'SUM(D2:D4)' }, STYLE={ ROW 1: { fontName: Arial, fontHeightInPoints: 12, bold: true, color: white, fillPattern: solid_foreground, fillForegroundColor: darkBlue, alignment: center } } ]]> </excel>
<!-- 导入:Excel 表头 -> JQuickRow 字段,性别文字经字典还原为码值 --> <excel name="importStudents" returnClass="java.util.List"> <![CDATA[ IMPORT WITH HEADER=true, SHEET='Sheet1', MAPPING={ "学号": "id", "姓名": "name", "性别": "gender", "年龄": "age", "出生日期": "birthday", "班级": "className" }, TRANSFORM={ "gender":trans(${dict},${gender}), "birthday":dateFormat(${birthday},'yyyy-MM-dd') } ]]> </excel>
</excels>接口全限定名与 XML 的 namespace 完全一致,方法名与 excel 节点的 name 完全一致:
package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.param.Param;
import java.util.List;
/** * 学生信息导入导出服务契约。 */public interface StudentExcelService {
/** 导出学生信息到 Excel,导出方法返回 void。 */ void exportStudents(@Param("field") String field, @Param("value") String value);
/** 从 Excel 导入学生信息,返回转换后的结构化行数据。 */ List<JQuickRow> importStudents(@Param("field") String field, @Param("value") String value);}导出列顺序由首行数据的字段遍历顺序决定。先定义学生业务对象:
package com.example;
import java.util.Date;
/** * 学生领域对象。 */public class Student {
private String id; private String name; /** 性别码值:1=男,2=女,导出时经字典转换为文字。 */ private Integer gender; private Integer age; private Date enrollmentDate; private String className;
public Student() { }
public Student(String id, String name, Integer gender, Integer age, Date enrollmentDate, String className) { this.id = id; this.name = name; this.gender = gender; this.age = age; this.enrollmentDate = enrollmentDate; this.className = className; }
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getGender() { return gender; }
public void setGender(Integer gender) { this.gender = gender; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public Date getEnrollmentDate() { return enrollmentDate; }
public void setEnrollmentDate(Date enrollmentDate) { this.enrollmentDate = enrollmentDate; }
public String getClassName() { return className; }
public void setClassName(String className) { this.className = className; }}JQuickRow 默认基于 HashMap 存储字段,列顺序不确定。为了让导出列固定为「学号 → 姓名 → 性别 → 年龄 → 出生日期 → 班级」(年龄落在 D 列,D5 公式才能正确求和),Demo 使用一个仅覆盖遍历顺序的保序子类,取值与转换逻辑仍由框架处理:
package com.example;
import com.github.paohaijiao.statement.JQuickRow;
import java.util.ArrayList;import java.util.LinkedHashSet;import java.util.List;import java.util.Map;import java.util.Set;
/** * 保序行对象:按字段 put 的顺序暴露 keySet / entrySet。 * JQuick-Excel 导出时以首行数据的 keySet 顺序决定 Excel 列顺序。 */public class OrderedJQuickRow extends JQuickRow {
private static final long serialVersionUID = 1L;
/** 按首次 put 的顺序记录字段名。 */ private final List<String> keys = new ArrayList<>();
@Override public Object put(String key, Object value) { if (!containsKey(key)) { keys.add(key); } return super.put(key, value); }
@Override public Set<String> keySet() { return new LinkedHashSet<>(keys); }
@Override public Set<Map.Entry<String, Object>> entrySet() { Set<Map.Entry<String, Object>> ordered = new LinkedHashSet<>(); for (String key : keys) { ordered.add(new EntryView(key)); } return ordered; }
/** 与保序键集合配套的只读条目视图。 */ private final class EntryView implements Map.Entry<String, Object> {
private final String key;
private EntryView(String key) { this.key = key; }
@Override public String getKey() { return key; }
@Override public Object getValue() { return get(key); }
@Override public Object setValue(Object value) { Object old = get(key); put(key, value); return old; } }}主程序严格按照目录约定读写:从 import 读取测试文件,导出结果自动输出到 export。
package com.example;
import com.github.paohaijiao.param.JContext;import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.im.JQuickExcelImportXmlParseFactory;
import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.InputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.Calendar;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;
/** * JQuick-Excel 导入导出完整 Demo。 * * 目录约定: * 1. 导入测试文件:D:/idea/jquick-docs/public/jquick-excel/import/students.xlsx * 2. 导出文件输出:D:/idea/jquick-docs/public/jquick-excel/export/students-export.xlsx * 3. 运行截图存放:D:/idea/jquick-docs/public/jquick-excel/image */public class StudentImportExportDemo {
/** 导入测试文件读取目录(测试文件手工放入此目录)。 */ private static final String IMPORT_DIR = "D:/idea/jquick-docs/public/jquick-excel/import";
/** 导出文件输出目录(程序自动生成,无需手工创建文件)。 */ private static final String EXPORT_DIR = "D:/idea/jquick-docs/public/jquick-excel/export";
public static void main(String[] args) throws Exception { // 第一步:把业务数据导出为 Excel,文件自动输出到 export 目录 exportStudents();
// 第二步:读取 import 目录中的测试 Excel,完成表头映射与字典转换 importStudents(); }
/** * 导出示例:业务数据 -> JQuickRow -> XML 代理 -> export 目录下的 xlsx 文件。 */ private static void exportStudents() throws Exception { // 1. 准备导出用的性别字典:库中码值 -> Excel 中展示的文字 Map<String, Object> genderDict = new HashMap<>(); genderDict.put("1", "男"); genderDict.put("2", "女"); JContext context = new JContext(); context.put("dict", genderDict);
// 2. 准备业务数据(实际项目中通常来自数据库查询) List<Student> data = new ArrayList<>(); data.add(new Student("2024001", "张三", 1, 20, date(2024, 9, 1), "计算机1班")); data.add(new Student("2024002", "李四", 2, 21, date(2024, 9, 1), "计算机2班")); data.add(new Student("2024003", "王五", 1, 22, date(2024, 9, 2), "计算机3班"));
// 3. 按固定列顺序组装行对象:A 学号 | B 姓名 | C 性别 | D 年龄 | E 出生日期 | F 班级 List<JQuickRow> rows = new ArrayList<>(); for (Student student : data) { JQuickRow row = new OrderedJQuickRow(); row.put("id", student.getId()); row.put("name", student.getName()); row.put("gender", student.getGender()); row.put("age", student.getAge()); row.put("enrollmentDate", student.getEnrollmentDate()); row.put("className", student.getClassName()); rows.add(row); }
// 4. 导出文件固定输出到 export 目录 String exportFile = EXPORT_DIR + "/students-export.xlsx"; try (OutputStream output = new FileOutputStream(exportFile)) { // 解析器携带上下文(字典)、数据行与输出流 JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(context, rows, output); // 按类路径资源名加载 XML 规则定义 JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); StudentExcelService service = factory.createApi(StudentExcelService.class);
// 方法名与 XML 中的 excel 节点 name="exportStudents" 对应 service.exportStudents("field", "value"); } System.out.println("导出完成,文件位置:" + exportFile); }
/** * 导入示例:读取 import 目录中的测试文件,映射为 JQuickRow 列表并打印。 */ private static void importStudents() throws Exception { // 1. 准备导入用的性别字典:Excel 中展示的文字 -> 系统内部码值(方向与导出相反) Map<String, Object> genderDict = new HashMap<>(); genderDict.put("男", "1"); genderDict.put("女", "2"); JContext context = new JContext(); context.put("dict", genderDict);
// 2. 测试文件固定从 import 目录读取 String importFile = IMPORT_DIR + "/students.xlsx"; try (InputStream input = new FileInputStream(importFile)) { JQuickParseHandler parser = new JQuickExcelImportXmlParseFactory(context, input); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); StudentExcelService service = factory.createApi(StudentExcelService.class);
// 3. 执行导入,返回按 MAPPING 右侧字段命名的结构化行 List<JQuickRow> rows = service.importStudents("field", "value");
System.out.println("导入完成,读取行数:" + rows.size()); for (JQuickRow row : rows) { // 性别文字已被字典转换为码值;数字单元格读入为 Double, // 整数场景使用 getIntConverted 做安全转换。 System.out.println("学号=" + row.getString("id") + ",姓名=" + row.getString("name") + ",性别码值=" + row.getString("gender") + ",年龄=" + row.getIntConverted("age") + ",出生日期=" + row.getString("birthday") + ",班级=" + row.getString("className")); } } }
private static Date date(int year, int month, int day) { Calendar calendar = Calendar.getInstance(); calendar.set(year, month - 1, day, 0, 0, 0); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTime(); }}在 IDE 中运行 StudentImportExportDemo,控制台输出如下(已省略框架启动 Banner 与日志):
导出完成,文件位置:D:/idea/jquick-docs/public/jquick-excel/export/students-export.xlsx导入完成,读取行数:3学号=2024001,姓名=张三,性别码值=1,年龄=20,出生日期=2024-09-01,班级=计算机1班学号=2024002,姓名=李四,性别码值=2,年龄=21,出生日期=2024-09-01,班级=计算机2班学号=2024003,姓名=王五,性别码值=1,年龄=22,出生日期=2024-09-02,班级=计算机3班导出文件 students-export.xlsx 自动出现在 export 目录,工作表「学生信息」内容如下:
| 行 | A 学号 | B 姓名 | C 性别 | D 年龄 | E 出生日期 | F 班级 |
|---|---|---|---|---|---|---|
| 1(表头) | 学号 | 姓名 | 性别 | 年龄 | 出生日期 | 班级 |
| 2 | 2024001 | 张三 | 男 | 20 | 2024-09-01 | 计算机1班 |
| 3 | 2024002 | 李四 | 女 | 21 | 2024-09-01 | 计算机2班 |
| 4 | 2024003 | 王五 | 男 | 22 | 2024-09-02 | 计算机3班 |
| 5 | SUM(D2:D4) |
截图统一保存到 D:\idea\jquick-docs\public\jquick-excel\image,在文档中通过站点路径 /jquick-excel/image/... 引用。需要补充截图时,按下表文件名放入对应图片即可,无需修改文档链接。
| 截图内容 | 本地存放位置 | 文档引用路径 |
|---|---|---|
import 目录中的测试文件在 Excel/WPS 中的打开效果 |
public/jquick-excel/image/demo-import-file.png |
/jquick-excel/image/demo-import-file.png |
程序运行后 export 目录生成的导出文件效果(含表头样式与 D5 汇总结果) |
public/jquick-excel/image/demo-export-file.png |
/jquick-excel/image/demo-export-file.png |
| IDE 控制台导入导出运行结果 | public/jquick-excel/image/demo-console.png |
/jquick-excel/image/demo-console.png |
1)导入测试文件效果(import/students.xlsx,截图位置:image/demo-import-file.png)

2)导出文件效果(export/students-export.xlsx,截图位置:image/demo-export-file.png)

3)控制台运行结果(截图位置:image/demo-console.png)

D:/idea/...)即可;请先确认 import、export、image 三个目录已存在。1→男)与导入字典(男→1)方向相反,两个 JContext 不能混用。JQuickRow.toRows(JObjectConverter.convert(data));需要列顺序稳定时再使用本示例的保序行对象。Double 类型,使用 getIntConverted("age") 等转换方法取值,避免直接 getString 产生类型转换异常。导入 DSL 的固定入口为 IMPORT WITH,各参数之间以英文逗号分隔:
IMPORT WITHSHEET="Students",HEADER=true,MAPPING={"ID":"id"},TRANSFORM={"name":toUpper(${name})},VALIDATION={C2:C4:{integer{required:true}}}SHEET="Students"。SHEET=1。IMPORT WITH SHEET="Students", HEADER=trueHEADER=true 表示首行为表头,数据从表头下一行开始读取。IMPORT WITH SHEET="Students", HEADER=true{"表头":"字段"},例如 {"ID":"id"} 表示表头为 ID 的列,读取后以 id 作为字段名。IMPORT WITHSHEET="Students",HEADER=true,MAPPING={"ID":"id","Name":"name","Age":"age"}${field}:读取当前行中指定字段的值,例如 ${name}。${key}:读取 JContext 中以该键保存的外部值,例如 ${dict}。toUpper(${name}):将文本转换为大写。dateFormat(${birthday},'yyyy-MM-dd'):按指定模式格式化日期值。trans(${dict},${sex}):使用 JContext 中的字典对值进行映射。IMPORT WITHSHEET="Sheet1",HEADER=true,MAPPING={"学号":"no","姓名":"name","性别":"sex","出生日期":"birthday"},TRANSFORM={ "sex":trans(${dict},${sex}), "birthday":dateFormat(${birthday},'yyyy-MM-dd')}JContext 字典的构造方式:
import com.github.paohaijiao.param.JContext;
import java.util.HashMap;import java.util.Map;
public class ContextDemo {
public static JContext buildContext() { Map<String, Object> gender = new HashMap<>(); gender.put("男", "1"); gender.put("女", "2");
JContext context = new JContext(); context.put("dict", gender); return context; }}外部依赖 jquick-transform-function 还通过 Java SPI 提供 formatDate、translate 等更多函数;只有显式引入该依赖后,这些函数才可以在 TRANSFORM 中使用。
required、msg 与 map:
required:是否要求必填,布尔值。msg:校验不通过时使用的自定义提示信息;未提供时使用规则默认信息。map:规则自身所需的参数集合,如上下限、格式模式等。| 范围 | 含义 | 示例 |
|---|---|---|
| 行 | 单行或行区间 | ROW 5、ROW 1..10 |
| 列 | 单列或列区间 | COL A、COL A..D |
| 单元格 | 单个单元格 | C1 |
| 矩形区域 | 矩形单元格区域 | A1:B5 |
| 规则 | 作用 | map 常用参数 |
|---|---|---|
boolean |
校验布尔值 | —— |
date_format |
校验日期字符串格式 | format |
max_date |
校验日期上限 | format、maxDate |
min_date |
校验日期下限 | format、minDate |
integer |
校验整数 | —— |
decimal |
校验小数 | —— |
max_value |
校验数值上限 | maxValue |
min_value |
校验数值下限 | minValue |
dict |
校验字典成员关系 | 字典键值对 |
email |
校验邮箱地址 | —— |
mobile |
校验手机号码 | —— |
max_length |
校验字符串最大长度 | maxLength |
min_length |
校验字符串最小长度 | minLength |
regex |
校验正则表达式 | pattern |
start_with |
要求指定前缀 | startWith |
not_start_with |
禁止指定前缀 | notStartWith |
end_with |
要求指定后缀 | endWith |
not_end_with |
禁止指定后缀 | notEndWith |
contain |
要求包含子串 | contains |
not_contain |
禁止包含子串 | notContain |
IMPORT WITH VALIDATION={ ROW 2..100:{ integer{required:true,msg:'年龄必须是整数'} }, C2:C100:{ email{required:true,msg:'邮箱格式无效'} }}msg,未配置 msg 时使用对应规则的默认提示信息。IMPORT WITHHEADER=true,SHEET='Sheet1',MAPPING={ "学号": "no", "姓名": "name", "性别": "sex", "年龄": "age", "出生日期": "birthday"},TRANSFORM={ "sex":trans(${dict},${sex}), "birthday":dateFormat(${birthday},'yyyy-MM-dd')},VALIDATION={ ROW 2..100:{ integer{required:true,msg:'年龄必须是整数'} }}导出 DSL 的固定入口为 EXPORT WITH,各参数之间以英文逗号分隔:
EXPORT WITHSHEET="Report",HEADER=true,MAPPING={"id":"ID","name":"Name","amount":"Amount"},FORMAT={"amount":"currency"},TRANSFORM={"name":toUpper(${name})},FORMULAS={D2:'SUM(B2:C2)'},STYLE={ROW 1:{bold:true}},MERGE:{ROWS 1..1,COLS A..D WITH FIRST},GRAPH={TYPE=PIE,TITLE="Revenue",CATEGORIES=["Q1"],SERIES=[{NAME="2023",DATA=[1]}]},FOOTER="Generated by JQuickExcel"EXPORT WITH SHEET="Report", HEADER=trueHEADER=true 表示生成表头。EXPORT WITH SHEET="Report", HEADER=trueFORMAT={"字段名":"格式码"},可同时声明多个字段,逗号分隔。| 位置 | 取值说明 | 必填 |
|---|---|---|
| 键(字段名) | 源数据字段名,区分大小写,与 MAPPING 键及 JQuickRow 中的键保持一致 |
是 |
| 值(格式码) | 标准 Excel 数字格式码,原样传入 POI DataFormat.getFormat();空字符串等于不设置格式 |
是 |
常用格式码如下:
| 配置项名称 | 描述 | 用法 |
|---|---|---|
yyyy-MM-dd |
日期,年-月-日 | FORMAT={"enrollmentDate":"yyyy-MM-dd"} |
yyyy"年"m"月"d"日" |
中文日期 | FORMAT={"enrollmentDate":"yyyy\"年\"m\"月\"d\"日\""} |
yyyy-MM-dd HH:mm |
日期 + 时分(24 小时制) | FORMAT={"createdAt":"yyyy-MM-dd HH:mm"} |
#,##0 |
整数,带千分位分隔符 | FORMAT={"quantity":"#,##0"} |
#,##0.00 |
数值,千分位且固定两位小数 | FORMAT={"amount":"#,##0.00"} |
0.00 |
数值,固定两位小数,不带千分位 | FORMAT={"score":"0.00"} |
0.00% |
百分比(底层数值乘 100 显示) | FORMAT={"rate":"0.00%"} |
¥#,##0.00 |
人民币货币格式 | FORMAT={"amount":"¥#,##0.00"} |
"$"#,##0.00 |
美元货币格式 | FORMAT={"amount":"\"$\"#,##0.00"} |
000000 |
数字补零(如 6 位工号、邮编) | FORMAT={"empNo":"000000"} |
EXPORT WITHFORMAT={"amount":"¥#,##0.00","enrollmentDate":"yyyy-MM-dd"}MAPPING={"源字段":"表头文本"},方向为「字段 → 表头」,可一次声明多个。| 配置项名称 | 描述 | 用法 |
|---|---|---|
"源字段":"表头文本" |
将该列的表头重命名为指定文本 | MAPPING={"id":"主键","name":"姓名"} |
未在 MAPPING 中声明的字段 |
该列仍会导出,表头默认直接使用字段名本身 | MAPPING={"id":"主键"}(其余列沿用字段名) |
EXPORT WITHSHEET="Students",HEADER=true,MAPPING={"id":"ID","name":"Name","age":"Age"}TRANSFORM={"字段名":函数调用},可一次声明多个字段。${field} 读取当前行字段值,${key} 读取 JContext 中预置的值。JQuickMethodInvocationManager)中查找提供者;未命中时回退到内置求值器。内置求值器函数:
| 配置项名称 | 描述 | 用法 |
|---|---|---|
toUpper |
将文本转为大写 | TRANSFORM={"name":toUpper(${name})} |
dateFormat |
按模式格式化日期值 | TRANSFORM={"enrollmentDate":dateFormat(${enrollmentDate},'yyyy-MM-dd')} |
trans |
通过 JContext 中的字典映射值 |
TRANSFORM={"gender":trans(${dict},${gender})} |
除内置函数外,独立模块 jquick-transform-function 通过 Java SPI 提供 248 个函数(如 formatDate、translate、maskEmail、idCardValidate 等)。各函数的参数、返回值与完整用法请以该仓库文档为准,函数扩展统一遵从 SPI 契约:
<dependency> <groupId>io.github.paohaijiao</groupId> <artifactId>jquick-transform-function</artifactId> <version>1.4.0</version></dependency>SPI 契约(com.github.paohaijiao.function.core.JQuickMethodFunctionProvider):
| 配置项名称 | 描述 | 用法 |
|---|---|---|
getMethodName() |
声明函数名,TRANSFORM 按此名称查找 | 返回 "formatDate" |
invoke(List<Object> args) |
接收求值后的参数并返回结果 | return args.get(0); |
getPriority() |
加载优先级,默认 5000,数字小者优先 | default int getPriority() |
META-INF/services/...JQuickMethodFunctionProvider |
SPI 服务描述文件,逐行列出实现类全限定名 | com.example.FullNameFunction |
函数分组目录(完整 248 个函数的清单与用法以 jquick-transform-function 仓库为准):
| 配置项名称 | 描述 | 代表用法 |
|---|---|---|
array |
数组与列表判断 | isArray(value) |
bit |
位运算 | bitAnd(a,b)、bitOr(a,b)、bitXor(a,b) |
bool |
布尔判断 | isBoolean(value) |
business |
银行卡、邮箱、性别、身份证、手机号处理与校验 | bankCardMask(cardNo)、idCardValidate(idCard)、phoneMask(phone)、isEmail(value) |
collection |
空值判断、集合拼接与长度 | isEmpty(value)、join(list,delimiter)、size(value) |
condition |
比较、范围、分支与空值默认 | between(value,min,max)、caseWhen(...)、coalesce(...)、nvl(value,defaultValue) |
convert |
布尔、日期、short 类型转换 | toBoolean(value)、toDate(value,pattern) |
date |
日期运算、提取、比较、格式化 | addDays(date,days)、daysBetween(d1,d2)、formatDate(date,pattern)、now()、today() |
geometry |
几何、向量、矩阵、复数运算 | distance(x1,y1,x2,y2)、gcd(a,b)、factorial(n)、isPrime(n) |
extra |
类型转换、数字格式化、类型检查 | cast(value,targetClass)、formatNumber(number,pattern)、typeOf(value) |
json |
JSON 序列化 | toJson(value) |
math |
四则运算、三角函数、统计、进制转换 | abs(value)、roundTo(value,places)、sum(...)、toHex(number) |
random |
随机值、随机选择、UUID | randomInt(min,max)、randomChoice(list)、randomUUID()、shuffle(list) |
string |
字符串查找、替换、大小写、脱敏、编码 | substring(str,begin,end)、replaceAll(str,regex,rep)、maskEmail(email)、base64Encode(str) |
translate |
上下文字典翻译 | translate(context,code,dictType,defaultValue) |
EXPORT WITHMAPPING={"name":"Name","gender":"Gender"},TRANSFORM={ "name":toUpper(${name}), "gender":trans(${dict},${gender})}FORMULAS={目标:'公式文本'},可同时声明多个目标,逗号分隔;公式文本必须加引号(单双引号均可)。Cell.setCellFormula(...) 写入公式;未能识别的函数按自定义公式原样写入。公式目标写法:
| 配置项名称 | 描述 | 用法 |
|---|---|---|
单元格 C1 |
向单个单元格写入公式 | FORMULAS={D5:'SUM(D2:D4)'} |
行目标 ROW 5 |
向指定行的所有已用列写入同一公式(相对引用按列平移) | FORMULAS={ROW 5:'SUM(B2:C2)'} |
行区间 ROW 1..10 |
向行区间内每一行的所有已用列写入公式 | FORMULAS={ROW 2..10:'SUM(B2:C2)'} |
列目标 COL A |
向指定列写入公式 | FORMULAS={COL E:'D2*0.1'} |
列区间 COL A..D |
向列区间写入公式 | FORMULAS={COL E..F:'D2*0.1'} |
内置公式函数(按分类完整列出):
数学函数
| 配置项名称 | 描述 | 用法 |
|---|---|---|
ABS |
绝对值 | ABS(D2) |
AVERAGE |
算术平均值 | AVERAGE(D2:D4) |
COUNT |
统计数值单元格数量 | COUNT(D2:D4) |
MAX |
最大值 | MAX(D2:D4) |
MIN |
最小值 | MIN(D2:D4) |
POWER |
幂运算 | POWER(2,3) |
RAND |
返回 [0,1) 随机值 | RAND() |
RANK |
数值在区域内的排名 | RANK(D2,D2:D4) |
ROUND |
按位数四舍五入 | ROUND(3.14159,2) |
SQRT |
平方根 | SQRT(4) |
STDEV |
样本标准差 | STDEV(D2:D4) |
SUM |
数值求和 | SUM(D2:D4) |
日期时间函数
| 配置项名称 | 描述 | 用法 |
|---|---|---|
DATE |
组合年月日生成日期(与 TIME 组合时按日期时间处理) |
DATE(2023,5,15) |
DATETIME |
创建日期时间值 | DATETIME(2023,5,15,14,30,0) |
DAY |
提取日期中的日 | DAY(A1) |
DAYS |
计算两个日期相差天数 | DAYS(A1,A2) |
EDATE |
日期按月偏移 | EDATE(A1,3) |
EOMONTH |
返回偏移若干月后的月末日期 | EOMONTH(A1,3) |
HOUR |
提取小时 | HOUR(A1) |
MINUTE |
提取分钟 | MINUTE(A1) |
MONTH |
提取月份 | MONTH(A1) |
NETWORKDAYS |
统计两个日期间的工作日数 | NETWORKDAYS(A1,A2) |
NOW |
当前日期时间 | NOW() |
SECOND |
提取秒 | SECOND(A1) |
TIME |
组合时分秒生成时间 | TIME(14,30,0) |
TODAY |
当前日期 | TODAY() |
WEEKDAY |
返回星期编号 | WEEKDAY(A1,2) |
WEEKNUM |
返回一年中的周数 | WEEKNUM(A1,1) |
WORKDAY |
按工作日偏移日期 | WORKDAY(A1,3,A2) |
YEAR |
提取年份 | YEAR(A1) |
文本函数
| 配置项名称 | 描述 | 用法 |
|---|---|---|
CONCAT |
拼接多个文本 | CONCAT(A1,B1) |
CONCATENATE |
拼接多个值(文本/数字/布尔) | CONCATENATE("A",1,TRUE) |
EXACT |
区分大小写比较两个文本 | EXACT("a","A") |
FIND |
区分大小写查找文本位置 | FIND("n",A1) |
LEFT |
从左侧取指定长度文本 | LEFT(A1,2) |
LEN |
文本字符数 | LEN(A1) |
LOWER |
转为小写 | LOWER(A1) |
MID |
按起始位置和长度取文本 | MID(A1,2,3) |
PROPER |
每个单词首字母大写 | PROPER(A1) |
REPLACE |
按位置替换文本 | REPLACE(A1,2,2,"XY") |
RIGHT |
从右侧取指定长度文本 | RIGHT(A1,2) |
SEARCH |
不区分大小写查找文本位置 | SEARCH("n",A1,3) |
SUBSTITUTE |
按内容替换文本 | SUBSTITUTE(A1,"a","b") |
TEXT |
按格式码将值转为文本 | TEXT(C2,"0.0%") |
TRIM |
去除两端空格 | TRIM(A1) |
UPPER |
转为大写 | UPPER(A1) |
VALUE |
文本转数字 | VALUE(A1) |
逻辑与查询函数
| 配置项名称 | 描述 | 用法 |
|---|---|---|
IF |
条件成立返回一个值,否则返回另一个值 | IF(D2>0,"Yes","No") |
AND |
所有条件均成立 | AND(A1>0,B1>0) |
OR |
任一条件成立 | OR(A1>0,B1>0) |
LOOKUP |
向量查询(在单行/列中查找并返回另一单行/列对应值) | LOOKUP(22,D2:D4,C2:C4) |
EXPORT WITH FORMULAS={ D5:'SUM(D2:D4)', E5:'ROUND(AVERAGE(D2:D4),2)', F5:'IF(D2>0,RANK(D2,D2:D4),0)'}ROW 1、COL A、单元格 A2、区域 A2:C100。| 属性 | 作用 | 示例值 |
|---|---|---|
fontName |
字体族 | Arial |
fontHeightInPoints |
字号(磅) | 12 |
bold |
粗体 | true |
italic |
斜体 | true |
color |
字体颜色 | yellow |
alignment |
水平对齐 | center |
verticalAlignment |
垂直对齐 | center |
wrapText |
自动换行 | true |
borderLeft / borderRight / borderTop / borderBottom |
四周边框 | thin |
fillPattern |
填充模式 | solid_foreground |
fillForegroundColor |
前景填充色 | yellow |
fillBackgroundColor |
背景填充色 | white |
dataFormatString |
单元格显示格式 | "yyyy-MM-dd" |
边框取值:none、thin、medium、dashed、dotted、thick、double。
填充模式取值:no_fill、solid_foreground、fine_dots、sparse_dots、thin_horz_bands、thin_vert_bands。
EXPORT WITH STYLE={ ROW 1:{ fontName:Arial, fontHeightInPoints:12, italic:true, color:yellow, bold:true }, A2:C100:{ alignment:center, wrapText:true, borderBottom:thin, fillForegroundColor:lightYellow }}WITH 后指定合并后保留值的聚合策略。| 策略 | 含义 |
|---|---|
MAX |
使用最大值 |
MIN |
使用最小值 |
AVG |
使用平均值 |
SUM |
对值求和 |
FIRST |
使用第一个值 |
LAST |
使用最后一个值 |
CONCAT |
拼接值 |
COUNT |
统计值数量 |
VALUE |
使用固定值 |
EXPORT WITH MERGE:{ ROWS 1..1, COLS A..F WITH FIRST}GRAPH = { 配置项, 配置项, ... },各配置项逗号分隔。完整配置项如下:
| 配置项名称 | 描述 | 用法 |
|---|---|---|
TYPE |
图表类型,取图表类型枚举值之一 | TYPE = LINE |
TITLE |
图表标题,字符串必须加引号 | TITLE = "月度销售统计" |
CATEGORY_AXIS |
分类轴(横轴)标题,取不加引号的标识符,支持中文 | CATEGORY_AXIS = 月份 |
VALUE_AXIS |
数值轴(纵轴)标题,取不加引号的标识符,支持中文 | VALUE_AXIS = 销售额 |
CATEGORIES |
分类值数组,逐项对应数据点;文本需加引号 | CATEGORIES = ["1月", "2月", "3月"] |
SERIES |
数据系列数组;每个系列对象固定包含 NAME 与 DATA,顺序为先 NAME 后 DATA |
SERIES = [{NAME = 一季度, DATA = [10, 20, 30]}] |
NAME |
系列名称,取不加引号的标识符,支持中文 | NAME = 产品A |
DATA |
系列数据点数组,仅接受数字(整数或小数) | DATA = [120, 200.5, 150] |
支持的图表类型(10 种):
| 配置项名称 | 描述 | 用法 |
|---|---|---|
LINE |
趋势分析折线图 | TYPE = LINE |
COLUMN |
垂直对比柱状图 | TYPE = COLUMN |
BAR |
水平对比条形图 | TYPE = BAR |
BAR3D |
三维条形图 | TYPE = BAR3D |
PIE |
比例展示饼图 | TYPE = PIE |
AREA |
累积趋势面积图 | TYPE = AREA |
AREA3D |
三维面积图 | TYPE = AREA3D |
SCATTER |
相关性散点图 | TYPE = SCATTER |
RADAR |
多维比较雷达图 | TYPE = RADAR |
SURFACE |
曲面数据图 | TYPE = SURFACE |
EXPORT WITH GRAPH = { TYPE = COLUMN, TITLE = "季度销售统计", CATEGORY_AXIS = 产品, VALUE_AXIS = 销量, CATEGORIES = ["产品A", "产品B", "产品C"], SERIES = [ {NAME = 第一季度, DATA = [120, 200, 150]}, {NAME = 第二季度, DATA = [180, 210, 190]} ]}EXPORT WITHFOOTER="Generated by JQuickExcel",MAPPING={"name":"Name"}带表达式的示例:
EXPORT WITHMAPPING={"name":"Name","gender":"Gender"},TRANSFORM={"gender":trans(${dict},${gender})},FOOTER="Generated by JQuickExcel on ${current_date()}"EXPORT WITHSHEET="学生表",HEADER=true,MAPPING={ "id":"主键", "name":"姓名", "gender":"性别", "age":"年龄", "enrollmentDate":"入学时间", "className":"班级"},FORMAT={ "enrollmentDate":"yyyy-MM-dd"},TRANSFORM={ "gender":trans(${dict},${gender}), "age":add(${age},1), "enrollmentDate":dateFormat(${enrollmentDate},'yyyy-MM-dd')},FORMULAS={ D5:'ABS(D2)'},STYLE={ ROW 1:{ fontName:Arial, fontHeightInPoints:12, italic:true, color:yellow, bold:true }},MERGE:{ ROWS 1..1, COLS A..D WITH FIRST},GRAPH={ TYPE=PIE, TITLE="Revenue", CATEGORIES=["Q1"], SERIES=[{NAME="2023",DATA=[1]}]},FOOTER="Generated by JQuickExcel"除声明式参数外,JQuick-Excel 内置 42 套导出模板(主题)。每套主题统一控制表头行、奇偶斑马数据行、大标题行、公式单元格与页脚的配色、字体与边框风格;主题编码由枚举 com.github.paohaijiao.theme.enums.JExcelThemeType 定义。主题为导出能力,导入不受影响。
主题不通过 DSL 关键字设置,而是在规则解析之后、创建导出处理器之前,通过以下两种方式指定。
方式一:在 JExcelExportModel 上调用 setTheme。
package org.example;
import com.github.paohaijiao.executor.JQuickExcelCommonExportExecutor;import com.github.paohaijiao.handler.JExcelExportHandler;import com.github.paohaijiao.model.JExcelExportModel;import com.github.paohaijiao.statement.JQuickRow;import org.apache.poi.ss.usermodel.Workbook;
import java.io.FileOutputStream;import java.util.List;
/** * 导出主题使用示例 / export theme example. */public class ThemeExportDemo {
public static void main(String[] args) throws Exception { // 1. 解析声明式导出规则 String rule = "EXPORT WITH HEADER=true"; JQuickExcelCommonExportExecutor executor = new JQuickExcelCommonExportExecutor(); JExcelExportModel model = (JExcelExportModel) executor.execute(rule);
// 2. 创建导出处理器之前设置主题编码(不区分大小写) model.setTheme("jade");
// 3. 准备业务数据行 List<JQuickRow> rows = prepareRows();
// 4. 创建处理器并导出 JExcelExportHandler handler = new JExcelExportHandler(model, rows); Workbook workbook = handler.getWorkBook(); try (FileOutputStream os = new FileOutputStream("d:/test/theme.xlsx")) { workbook.write(os); } }
private static List<JQuickRow> prepareRows() { // 省略:按业务数据组装 JQuickRow 列表 return java.util.Collections.emptyList(); }}方式二:使用 XML 代理工厂时,通过构造器第一个参数传入主题编码。
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;
import java.io.OutputStream;import java.util.List;
// rows 为业务数据行,os 为导出目标流JQuickExcelExportXmlParseFactory factory = new JQuickExcelExportXmlParseFactory("jade", rows, os);// 后续按 XML 代理方式 createApi(...) 并调用接口方法即可42 个内置主题编码与名称如下:
| 配置项名称 | 描述 | 用法 |
|---|---|---|
default |
经典皇家蓝(默认主题) | setTheme("default") |
oceanBlue |
海洋蓝 | setTheme("oceanBlue") |
forestGreen |
森林绿 | setTheme("forestGreen") |
sunsetOrange |
落日橙 | setTheme("sunsetOrange") |
lavenderPurple |
薰衣草紫 | setTheme("lavenderPurple") |
sakuraPink |
樱花粉 | setTheme("sakuraPink") |
midnightDark |
午夜深色 | setTheme("midnightDark") |
minimalistGrey |
极简灰 | setTheme("minimalistGrey") |
vintageSepia |
复古棕 | setTheme("vintageSepia") |
tropicalTeal |
热带青 | setTheme("tropicalTeal") |
royalGold |
皇家金 | setTheme("royalGold") |
mintFresh |
清新薄荷 | setTheme("mintFresh") |
amber |
琥珀 | setTheme("amber") |
amethyst |
紫水晶 | setTheme("amethyst") |
azure |
蔚蓝 | setTheme("azure") |
charcoal |
炭灰 | setTheme("charcoal") |
coral |
珊瑚 | setTheme("coral") |
crimsonRed |
深红 | setTheme("crimsonRed") |
cyan |
青色 | setTheme("cyan") |
emerald |
祖母绿 | setTheme("emerald") |
espresso |
浓缩咖啡 | setTheme("espresso") |
indigo |
靛蓝 | setTheme("indigo") |
mustard |
芥末黄 | setTheme("mustard") |
navyBlue |
海军蓝 | setTheme("navyBlue") |
oliveGreen |
橄榄绿 | setTheme("oliveGreen") |
peach |
蜜桃 | setTheme("peach") |
sage |
鼠尾草绿 | setTheme("sage") |
skyBlue |
天空蓝 | setTheme("skyBlue") |
steelBlue |
钢蓝 | setTheme("steelBlue") |
terracotta |
陶土 | setTheme("terracotta") |
turquoise |
绿松石 | setTheme("turquoise") |
wineRed |
酒红 | setTheme("wineRed") |
plum |
紫梅 | setTheme("plum") |
slate |
板岩灰 | setTheme("slate") |
pearl |
珍珠白 | setTheme("pearl") |
bronze |
青铜 | setTheme("bronze") |
roseQuartz |
粉晶 | setTheme("roseQuartz") |
jade |
翡翠绿 | setTheme("jade") |
champagne |
香槟金 | setTheme("champagne") |
denim |
牛仔蓝 | setTheme("denim") |
mahogany |
红木 | setTheme("mahogany") |
periwinkle |
长春花蓝 | setTheme("periwinkle") |
下表按色系分组展示 42 套主题的实际导出效果,每行给出主题编码(code)、主题名称与预览图,便于直接对照选用。
经典与灰色系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
default |
经典皇家蓝 | ![]() |
minimalistGrey |
极简灰 | ![]() |
slate |
板岩灰 | ![]() |
深色与海洋蓝系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
charcoal |
炭灰 | ![]() |
navyBlue |
海军蓝 | ![]() |
oceanBlue |
海洋蓝 | ![]() |
浅蓝与钢蓝系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
skyBlue |
天空蓝 | ![]() |
azure |
蔚蓝 | ![]() |
steelBlue |
钢蓝 | ![]() |
牛仔蓝与靛蓝系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
denim |
牛仔蓝 | ![]() |
indigo |
靛蓝 | ![]() |
periwinkle |
长春花蓝 | ![]() |
森林绿与翡翠绿系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
forestGreen |
森林绿 | ![]() |
emerald |
祖母绿 | ![]() |
jade |
翡翠绿 | ![]() |
薄荷与低饱和绿系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
mintFresh |
清新薄荷 | ![]() |
sage |
鼠尾草绿 | ![]() |
oliveGreen |
橄榄绿 | ![]() |
青绿与青色系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
tropicalTeal |
热带青 | ![]() |
turquoise |
绿松石 | ![]() |
cyan |
青色 | ![]() |
橙色与蜜桃系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
sunsetOrange |
落日橙 | ![]() |
coral |
珊瑚 | ![]() |
peach |
蜜桃 | ![]() |
深红、酒红与樱花系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
crimsonRed |
深红 | ![]() |
wineRed |
酒红 | ![]() |
sakuraPink |
樱花粉 | ![]() |
粉色与紫色系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
roseQuartz |
粉晶 | ![]() |
lavenderPurple |
薰衣草紫 | ![]() |
amethyst |
紫水晶 | ![]() |
紫梅与金色系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
plum |
紫梅 | ![]() |
royalGold |
皇家金 | ![]() |
champagne |
香槟金 | ![]() |
琥珀、芥末与青铜系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
amber |
琥珀 | ![]() |
mustard |
芥末黄 | ![]() |
bronze |
青铜 | ![]() |
复古棕与深棕系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
vintageSepia |
复古棕 | ![]() |
espresso |
浓缩咖啡 | ![]() |
mahogany |
红木 | ![]() |
陶土、深色与珍珠系
| 配置项名称(code) | 描述(名称) | 用法(预览) |
|---|---|---|
terracotta |
陶土 | ![]() |
midnightDark |
午夜深色 | ![]() |
pearl |
珍珠白 | ![]() |
以下示例使用统一的 XML 定义方式。每个示例给出对应的 jquick-excel.xml 定义与完整 Java 代码。
XML 定义:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.SheetNameService"> <excel name="readByName" returnClass="java.util.List"> <![CDATA[ IMPORT WITH SHEET="Students", HEADER=true ]]> </excel></excels>服务接口与调用代码:
package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.param.Param;
import java.util.List;
public interface SheetNameService {
List<JQuickRow> readByName(@Param("field") String field, @Param("value") String value);}package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.im.JQuickExcelImportXmlParseFactory;
import java.io.InputStream;import java.util.List;
public class ReadByNameDemo {
public static void main(String[] args) throws Exception { try (InputStream input = ReadByNameDemo.class.getClassLoader() .getResourceAsStream("students.xlsx")) { JQuickParseHandler parser = new JQuickExcelImportXmlParseFactory(input); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); SheetNameService service = factory.createApi(SheetNameService.class);
List<JQuickRow> rows = service.readByName("field", "value"); rows.forEach(row -> System.out.println(row)); } }}SHEET 使用整数位置时从 1 开始计数,下例读取第一张工作表:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.SheetIndexService"> <excel name="readByIndex" returnClass="java.util.List"> <![CDATA[ IMPORT WITH SHEET=1, HEADER=true ]]> </excel></excels>package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.param.Param;
import java.util.List;
public interface SheetIndexService {
List<JQuickRow> readByIndex(@Param("field") String field, @Param("value") String value);}package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.im.JQuickExcelImportXmlParseFactory;
import java.io.InputStream;import java.util.List;
public class ReadByIndexDemo {
public static void main(String[] args) throws Exception { try (InputStream input = ReadByIndexDemo.class.getClassLoader() .getResourceAsStream("students.xlsx")) { JQuickParseHandler parser = new JQuickExcelImportXmlParseFactory(input); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); SheetIndexService service = factory.createApi(SheetIndexService.class);
List<JQuickRow> rows = service.readByIndex("field", "value"); System.out.println("读取行数:" + rows.size()); } }}XML 定义:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.TransformService"> <excel name="importWithTransform" returnClass="java.util.List"> <![CDATA[ IMPORT WITH HEADER=true, SHEET='Sheet1', MAPPING={ "学号": "no", "姓名": "name", "性别": "sex", "出生日期": "birthday" }, TRANSFORM={ "sex":trans(${dict},${sex}), "birthday":dateFormat(${birthday},'yyyy-MM-dd') } ]]> </excel></excels>package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.param.Param;
import java.util.List;
public interface TransformService {
List<JQuickRow> importWithTransform(@Param("field") String field, @Param("value") String value);}package com.example;
import com.github.paohaijiao.param.JContext;import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.im.JQuickExcelImportXmlParseFactory;
import java.io.InputStream;import java.util.HashMap;import java.util.List;import java.util.Map;
public class TransformImportDemo {
public static void main(String[] args) throws Exception { Map<String, Object> gender = new HashMap<>(); gender.put("男", "1"); gender.put("女", "2"); JContext context = new JContext(); context.put("dict", gender);
try (InputStream input = TransformImportDemo.class.getClassLoader() .getResourceAsStream("templates/student.xlsx")) { JQuickParseHandler parser = new JQuickExcelImportXmlParseFactory(context, input); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); TransformService service = factory.createApi(TransformService.class);
List<JQuickRow> rows = service.importWithTransform("field", "value"); rows.forEach(row -> { // JQuickRow 提供按字段名取值的类型化方法 System.out.println(row.getString("name") + " / " + row.getString("sex")); }); } }}JQuickRow 还提供 toBean(Class) 与静态方法 toBeanList(rows, Class),可直接将结果行转换为 Java Bean:
import com.github.paohaijiao.statement.JQuickRow;
import java.util.List;
public class RowConvertDemo {
public static List<StudentModel> toBeans(List<JQuickRow> rows) { return JQuickRow.toBeanList(rows, StudentModel.class); }}XML 定义:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.ValidationService"> <excel name="importWithValidation" returnClass="java.util.List"> <![CDATA[ IMPORT WITH HEADER=true, SHEET='Sheet1', VALIDATION={ ROW 2..100:{ integer{required:true,msg:'年龄必须是整数'} }, C2:C100:{ email{required:true,msg:'邮箱格式无效'} } } ]]> </excel></excels>package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.param.Param;
import java.util.List;
public interface ValidationService {
List<JQuickRow> importWithValidation(@Param("field") String field, @Param("value") String value);}package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.im.JQuickExcelImportXmlParseFactory;
import java.io.InputStream;import java.util.List;
public class ValidationImportDemo {
public static void main(String[] args) { try (InputStream input = ValidationImportDemo.class.getClassLoader() .getResourceAsStream("students.xlsx")) { JQuickParseHandler parser = new JQuickExcelImportXmlParseFactory(input); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); ValidationService service = factory.createApi(ValidationService.class);
List<JQuickRow> rows = service.importWithValidation("field", "value"); System.out.println("校验通过,导入行数:" + rows.size()); } catch (Exception exception) { // 校验不通过时,异常信息为 DSL 中配置的 msg System.err.println("数据校验失败:" + exception.getMessage()); } }}XML 定义:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.BasicExportService"> <excel name="export" returnClass="void"> <![CDATA[ EXPORT WITH SHEET="Students", HEADER=true, MAPPING={"id":"ID","name":"Name","age":"Age"} ]> </excel></excels>package com.example;
import com.github.paohaijiao.xml.param.Param;
public interface BasicExportService {
void export(@Param("field") String field, @Param("value") String value);}package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.util.JObjectConverter;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;
import java.io.FileOutputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;
public class BasicExportDemo {
public static void main(String[] args) throws Exception { List<Map<String, Object>> data = new ArrayList<>(); Map<String, Object> row = new LinkedHashMap<>(); row.put("id", "1001"); row.put("name", "Alice"); row.put("age", 20); data.add(row);
List<JQuickRow> rows = JQuickRow.toRows(JObjectConverter.convert(data));
try (OutputStream output = new FileOutputStream("students.xlsx")) { JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, output); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); BasicExportService service = factory.createApi(BasicExportService.class);
service.export("field", "value"); } }}XML 定义:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.StyleExportService"> <excel name="export" returnClass="void"> <![CDATA[ EXPORT WITH SHEET="Report", HEADER=true, MAPPING={"id":"ID","name":"Name","amount":"Amount"}, FORMAT={"amount":"currency"}, STYLE={ ROW 1:{ fontName:Arial, bold:true, color:yellow }, A2:C100:{ alignment:center, borderBottom:thin } } ]]> </excel></excels>package com.example;
import com.github.paohaijiao.xml.param.Param;
public interface StyleExportService {
void export(@Param("field") String field, @Param("value") String value);}package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.util.JObjectConverter;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;
import java.io.FileOutputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;
public class StyleExportDemo {
public static void main(String[] args) throws Exception { List<Map<String, Object>> data = new ArrayList<>(); Map<String, Object> row = new LinkedHashMap<>(); row.put("id", "1001"); row.put("name", "Alice"); row.put("amount", 128.5); data.add(row);
List<JQuickRow> rows = JQuickRow.toRows(JObjectConverter.convert(data));
try (OutputStream output = new FileOutputStream("report.xlsx")) { JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, output); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); StyleExportService service = factory.createApi(StyleExportService.class);
service.export("field", "value"); } }}XML 定义:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.MergeExportService"> <excel name="export" returnClass="void"> <![CDATA[ EXPORT WITH HEADER=true, MERGE:{ ROWS 1..1, COLS A..F WITH FIRST } ]]> </excel></excels>package com.example;
import com.github.paohaijiao.xml.param.Param;
public interface MergeExportService {
void export(@Param("field") String field, @Param("value") String value);}package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.util.JObjectConverter;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;
import java.io.FileOutputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.List;import java.util.Map;
public class MergeExportDemo {
public static void main(String[] args) throws Exception { List<Map<String, Object>> data = new ArrayList<>();
List<JQuickRow> rows = JQuickRow.toRows(JObjectConverter.convert(data));
try (OutputStream output = new FileOutputStream("merged.xlsx")) { JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, output); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); MergeExportService service = factory.createApi(MergeExportService.class);
service.export("field", "value"); } }}XML 定义:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.FormulaExportService"> <excel name="export" returnClass="void"> <![CDATA[ EXPORT WITH FORMULAS={ D5:'SUM(D2:D4)', E5:'ROUND(AVERAGE(D2:D4),2)', F5:'RANK(D2,D2:D4)' } ]]> </excel></excels>package com.example;
import com.github.paohaijiao.xml.param.Param;
public interface FormulaExportService {
void export(@Param("field") String field, @Param("value") String value);}package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.util.JObjectConverter;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;
import java.io.FileOutputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.List;import java.util.Map;
public class FormulaExportDemo {
public static void main(String[] args) throws Exception { List<Map<String, Object>> data = new ArrayList<>();
List<JQuickRow> rows = JQuickRow.toRows(JObjectConverter.convert(data));
try (OutputStream output = new FileOutputStream("formula.xlsx")) { JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, output); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); FormulaExportService service = factory.createApi(FormulaExportService.class);
service.export("field", "value"); } }}XML 定义:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-excel.dtd"><excels namespace="com.example.ChartExportService"> <excel name="export" returnClass="void"> <![CDATA[ EXPORT WITH GRAPH = { TYPE = PIE, TITLE = "Market Share", CATEGORIES = ["A", "B", "C"], SERIES = [{NAME = "Share", DATA = [38.5, 22.3, 15.7]}] } ]]> </excel></excels>package com.example;
import com.github.paohaijiao.xml.param.Param;
public interface ChartExportService {
void export(@Param("field") String field, @Param("value") String value);}package com.example;
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.util.JObjectConverter;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;
import java.io.FileOutputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.List;import java.util.Map;
public class ChartExportDemo {
public static void main(String[] args) throws Exception { List<Map<String, Object>> data = new ArrayList<>();
List<JQuickRow> rows = JQuickRow.toRows(JObjectConverter.convert(data));
try (OutputStream output = new FileOutputStream("chart.xlsx")) { JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, output); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); ChartExportService service = factory.createApi(ChartExportService.class);
service.export("field", "value"); } }}框架不依赖 Spring,因此在 Spring Boot 中将响应流(HttpServletResponse.getOutputStream())作为导出目标即可:
package com.example.controller;
import com.example.WebExportService;import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.util.JObjectConverter;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;import java.io.OutputStream;import java.net.URLEncoder;import java.util.ArrayList;import java.util.List;import java.util.Map;
@RestControllerpublic class StudentExportController {
@GetMapping("/students/export") public void exportStudents(HttpServletResponse response) throws Exception { // 业务数据 List<Map<String, Object>> data = new ArrayList<>();
List<JQuickRow> rows = JQuickRow.toRows(JObjectConverter.convert(data));
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("UTF-8"); String fileName = URLEncoder.encode("学生表.xlsx", "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName);
try (OutputStream output = response.getOutputStream()) { JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, output); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); WebExportService service = factory.createApi(WebExportService.class);
service.export("field", "value"); output.flush(); } }}package com.example.controller;
import com.github.paohaijiao.xml.param.Param;
public interface WebExportService {
void export(@Param("field") String field, @Param("value") String value);}导出数据可写入任意 OutputStream:本地文件使用 FileOutputStream,其他场景使用对应目标的输出流。完整示例参见本文「最简导出示例」,核心代码如下:
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.util.JObjectConverter;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;
import java.io.ByteArrayOutputStream;import java.io.FileOutputStream;import java.io.OutputStream;import java.util.List;import java.util.Map;
public class OutputTargetDemo {
// 写入本地文件 public void writeToFile(List<Map<String, Object>> data, String filePath) throws Exception { List<JQuickRow> rows = JQuickRow.toRows(JObjectConverter.convert(data)); try (OutputStream output = new FileOutputStream(filePath)) { JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, output); buildAndExport(parser); } }
// 写入内存输出流(例如后续作为邮件附件、对象存储内容) public byte[] writeToMemory(List<Map<String, Object>> data) throws Exception { List<JQuickRow> rows = JQuickRow.toRows(JObjectConverter.convert(data)); try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, output); buildAndExport(parser); return output.toByteArray(); } }
private void buildAndExport(JQuickParseHandler parser) { // 由 XML 工厂创建代理并完成导出,此处省略服务接口调用 }}高行数导出可通过 JQuickExcelConfig 调整流式行为,默认配置如下:
| 配置项 | 作用 | 默认值 |
|---|---|---|
streamingExportEnabled |
是否允许切换 SXSSF 流式写入 | true |
streamingRowAccessWindowSize |
内存中保留的行数 | 100 |
streamingExportThreshold |
达到该行数时自动切换流式 | 5000 |
streamingCompressTempFiles |
是否压缩流式临时文件 | true |
cellStyleCacheEnabled |
是否启用单元格样式缓存(避免超出 POI 64000 样式上限) | true |
import com.github.paohaijiao.config.JQuickExcelConfig;
public class ConfigDemo {
public static void configure() { JQuickExcelConfig config = JQuickExcelConfig.getInstance(); config.setStreamingExportEnabled(true) .setStreamingRowAccessWindowSize(100) .setStreamingExportThreshold(5000) .setStreamingCompressTempFiles(true) .setCellStyleCacheEnabled(true); }}导入解析工厂以 InputStream 作为输入,从文件路径读取时先构造文件输入流:
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.im.JQuickExcelImportXmlParseFactory;
import java.io.FileInputStream;import java.io.InputStream;import java.util.List;
public class FilePathImportDemo {
public static void main(String[] args) throws Exception { try (InputStream input = new FileInputStream("D:/data/students.xlsx")) { JQuickParseHandler parser = new JQuickExcelImportXmlParseFactory(input); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); FilePathImportService service = factory.createApi(FilePathImportService.class);
List<JQuickRow> rows = service.importExcel("field", "value"); System.out.println("导入行数:" + rows.size()); } }}JQuickExcelImportXmlParseFactory 直接接收任意来源的 InputStream,例如类路径资源、网络流或上传文件流:
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.im.JQuickExcelImportXmlParseFactory;
import java.io.InputStream;import java.util.List;
public class StreamImportDemo {
public List<JQuickRow> doImport(InputStream input) throws Exception { JQuickParseHandler parser = new JQuickExcelImportXmlParseFactory(input); JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml"); StreamImportService service = factory.createApi(StreamImportService.class); return service.importExcel("field", "value"); }}大文件导入建议使用 JExcelImportHandler 的批量读取能力,按固定批大小消费,避免一次性保留全部行:
import com.github.paohaijiao.handler.JExcelImportHandler;import com.github.paohaijiao.model.JExcelImportModel;import com.github.paohaijiao.statement.JQuickRow;
import java.io.InputStream;import java.util.List;
public class BatchImportDemo {
public static void main(String[] args) throws Exception { JExcelImportModel model = new JExcelImportModel();
try (InputStream input = new java.io.FileInputStream("D:/data/big.xlsx")) { JExcelImportHandler handler = new JExcelImportHandler(input);
// 每 5000 行消费一批;返回值为实际读取的总行数 int total = handler.importDataInBatch(model, 5000, (List<JQuickRow> batch) -> { process(batch); // 返回 true 继续读取下一批 return true; }); System.out.println("总计读取:" + total); } }
private static void process(List<JQuickRow> batch) { // 在此处理当前批次数据 }}JQuickExcelConfig 中与导入相关的默认配置:
| 配置项 | 作用 | 默认值 |
|---|---|---|
bigFileImportEnabled |
是否启用 OPCPackage 方式解析以降低峰值内存 | true |
importBatchThreshold |
建议分批处理的行数阈值(供使用方判断,不强制) | 20000 |
导出到本地文件时使用 FileOutputStream 作为输出目标,完整示例见「最简导出示例」。核心写法:
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;
import java.io.FileOutputStream;import java.io.OutputStream;import java.util.List;
public class WriteFileDemo {
public void write(List<JQuickRow> rows, String filePath) throws Exception { try (OutputStream output = new FileOutputStream(filePath)) { JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, output); // 使用 JQuickXmlFactory 创建代理并调用导出方法 } }}导出目标可以是任意 OutputStream:HTTP 响应流、ByteArrayOutputStream、对象存储上传流等。传入工厂的流由调用方负责关闭,建议使用 try-with-resources:
import com.github.paohaijiao.statement.JQuickRow;import com.github.paohaijiao.xml.handler.JQuickParseHandler;import com.github.paohaijiao.xml.ex.JQuickExcelExportXmlParseFactory;
import java.io.OutputStream;import java.util.List;
public class WriteStreamDemo {
public void write(List<JQuickRow> rows, OutputStream target) { // JAssert 要求 rows 与 OutputStream 均不能为 null JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, target); // 使用 JQuickXmlFactory 创建代理并调用导出方法 }}README 未提供与 EasyExcel、原生 POI 的逐项对比表。以下仅为代码层面可确认的事实:
除上述事实外的逐项能力对比,README 暂未提供。
1. XML 定义文件应该放在哪里?
放在类路径(resources 目录)中,创建 JQuickXmlFactory 时以该文件名作为参数,例如 new JQuickXmlFactory(parser, "jquick-excel.xml")。
2. DSL 中的关键字和标点可以改写吗?
不可以。IMPORT、EXPORT、WITH 等关键字以及花括号、冒号、逗号、范围语法需要准确保留,否则解析不通过。
3. 高行数导出时如何避免内存占用过高?
JQuickExcelConfig 默认允许在数据量达到 5000 行时自动切换 SXSSF 流式写入,内存中默认保留 100 行,可按需要调整。
4. 大文件如何导入?
使用 JExcelImportHandler.importDataInBatch(model, pageSize, consumer) 按批消费;配置中推荐的分批阈值默认为 20000 行。
5. 为什么需要 JContext?
TRANSFORM 表达式中以 ${key} 引用的外部值(例如数据字典)通过 JContext 提供;${field} 则读取当前行字段值。
6. 单元格样式数量有上限吗?
POI 存在单元格样式数量上限。框架默认启用样式缓存复用样式,建议保持 cellStyleCacheEnabled=true。
7. TRANSFORM 函数和 FORMULAS 公式有什么区别?
TRANSFORM 函数是 Java 表达式,在导入导出过程中直接计算;FORMULAS 写入的是 Excel 公式,由表格软件打开时计算。两者即使同名也不可互换。
8. 图表的数据需要注意什么?
每个系列的 DATA 数组应与 CATEGORIES 一一对应(长度对齐),否则图表分类与数据会错位。
9. 流需要手动关闭吗?
需要。输入流与输出流由调用方提供并负责关闭,推荐使用 try-with-resources。
10. 如何使用内置导出主题?
创建导出解析工厂时可传入主题编码(例如 new JQuickExcelExportXmlParseFactory("jade", context, rows, output)),框架内置 42 套主题;不传主题时使用默认样式。
以下为代码与 README 可确认的限制:
TRANSFORM 参数与可选的 SPI 函数依赖完成。<excel name> 必须与服务接口方法名一致,namespace 必须与服务接口全限定名一致。jquick-transform-function 提供的 248 个 TRANSFORM 函数只有在显式引入该依赖后才可用。xls 与 xlsx 两种工作簿格式。3.6.0(要求 Java 8 或更高版本)JQuick-Excel 基于 Apache License, Version 2.0 发布。
Copyright (c) [2025-2099] Martin ([email protected])
Licensed under the Apache License, Version 2.0 (the "License");you may not use this file except in compliance with the License.You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, softwaredistributed under the License is distributed on an "AS IS" BASIS,WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the License for the specific language governing permissions andlimitations under the License.以下 Demo 全部来自项目单元测试用例(仓库 src/test/java/com/github/paohaijiao/biz 目录),每个示例由「Java 测试类 + JQuick XML 配置 + 导出产物 xlsx」三部分组成,复制即可运行复现导出效果。
每组按每 10 个 Demo 一批分页,用「上一批 / 下一批」切换。
演示 MERGE 分组区块与 SUBTOTAL / TOTAL 小计合计行:同一分组列纵向合并成一个区块,每个分组后跟一行小计,末尾给出合计。
共 24 个示例
各部门费用明细,每个部门后跟一行小计,末尾一行合计。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按大区切片列出各城市销售额,每个大区后跟一行小计,末尾一行合计;大区名称列纵向合并,视觉上把同区城市归为一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
一张出库单下列多行商品明细,「数量 × 单价 = 金额」,末尾一行合计。 纯 XML 声明式实现,无需编写 Java 业务代码。
报销明细逐行「金额 - 自付金额 = 可报销金额」,末尾一行合计。 纯 XML 声明式实现,无需编写 Java 业务代码。
按「资产类别 → 资产名称」两级归集固定资产,逐行算「原值 = 数量 × 单价」「净值 = 原值 - 已提折旧」;每个资产类别后跟一行小计,末尾一行给出全部类别合计;资产类别列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
项目按阶段排期,逐行算「工期 = 结束日期 - 开始日期」天,每阶段后跟一行工期小计,末尾一行给出总工期与整体平均完成率;阶段名称列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按部门归集员工费用报销明细,每个部门后跟一行金额小计,末尾一行合计;部门名称列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按仓库归集盘点结果,逐行算「差异数量 = 实盘 - 账面」与「差异金额 = 差异数量 × 单价」,每个仓库后跟一行差异小计,末尾一行合计;仓库名称列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按供应商归集采购供货明细,逐行算「供货金额 = 供货数量 × 单价」,每个供应商后跟一行金额小计,末尾一行合计;供应商名称列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按供应商归集逐月往来对账,逐行算「差异金额 = 我方应付 - 供方对账」,每个供应商后跟一行差异小计,末尾一行合计;供应商名称列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按项目归集成员投入工时,逐行算「工时费 = 工时 × 费率」,每个项目后跟一行工时费小计,末尾一行给出全部项目合计;项目名称列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按部门归集各费用科目的年度预算与实际支出,逐行算「预算余额 = 预算 - 实际」与「执行率 = 实际 / 预算」,每个部门后跟一行小计,末尾一行给出全公司合计;部门名称列纵向合并成一个区块,超支科目的执行率标红。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按客户归集各账期的应收与回款,逐行算「未收余额 = 应收金额 - 已收金额」,每个客户后跟一行余额小计,末尾一行给出全部客户合计;客户名称列纵向合并成一个区块,逾期超过 90 天的明细在逾期天数列标红。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按使用部门盘点固定资产,逐行算「盘盈盘亏 = 实盘数量 - 账面数量」,每个部门后跟一行数量小计,末尾一行给出全公司合计;部门名称列纵向合并成一个区块,盘亏(差额为负)的明细标红。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按服务工程师归集售后工单,逐行算「服务费 = 服务时长 × 上门费单价」,每位工程师后跟一行服务费小计,末尾一行给出全部工单合计;工程师列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按项目归集各成本科目发生额,每个项目后跟一行成本小计,末尾一行给出全部项目合计;项目名称列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按门店归集各品类当日销售额与销售笔数,每个门店后跟一行小计,末尾一行给出全部门店合计;门店名称列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按平台归集各月平台结算额与我方账面额,逐行算「差异 = 平台结算 - 我方账面」,每个平台后跟一行小计,末尾一行给出全部平台合计;平台名称列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按合同类型归集各合同金额与已履行金额,逐行算「履行率 = 已履行 / 合同金额」,每类合同后跟一行小计,末尾一行给出全部合同合计;合同类型列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按仓库归集各库位的库存数量与单位成本,逐行算「库存金额 = 数量 × 单位成本」,每个仓库后跟一行小计,末尾一行给出全部仓库合计;仓库名称列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按「费用大类 → 费用明细」两级归集预算与实际,逐行算「差异 = 实际 - 预算」「执行率 = 实际 / 预算」;每个费用大类后跟一行小计,末尾一行给出全部门类合计;费用大类列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按「领用部门 → 用品名称」归集办公用品领用,逐行算「金额 = 数量 × 单价」;每个部门后跟一行小计,末尾一行给出全部门合计;领用部门列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按「仓库 → 物料」归集呆滞物料,逐行算「呆滞金额 = 库存数量 × 单价」;每个仓库后跟一行小计,末尾一行给出全仓合计;仓库列纵向合并成一个区块。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
按大区切片列出各门店一至四季度销售额,大区名称列纵向合并成一个区块,视觉上把同区门店归为一个分组。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
演示 FORMULAS 的行内表达式与聚合函数:逐行算「数量 × 单价」「余额 = 应收 − 已收」等,末行用 SUM 汇总。
共 31 个示例
某日销售明细,逐行「数量 × 单价 = 金额」,末尾一行合计。 纯 XML 声明式实现,无需编写 Java 业务代码。
按经营 / 投资 / 筹资三类活动记录现金流入、流出,逐行算「净额 = 流入 - 流出」,末尾合计。 纯 XML 声明式实现,无需编写 Java 业务代码。
物料入库次数、出库次数、期末库存、库龄,逐行算「周转率 = 出库次数 / 期末库存」, 纯 XML 声明式实现,无需编写 Java 业务代码。
A/B/C 分层客户的年度成交额与贡献占比,末尾一行合计。 纯 XML 声明式实现,无需编写 Java 业务代码。
销量前 N 名的产品排行,逐行给出销售占比,末尾一行合计。 纯 XML 声明式实现,无需编写 Java 业务代码。
按员工录入业绩分 / 能力分 / 态度分,按权重算综合得分,末尾给出各维度平均分;综合得分偏低、等级为「待改进」的员工,其等级单元格字体标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
采购取得的进项增值税发票登记,逐行算「税额 = 不含税金额 × 税率」与「价税合计 = 不含税金额 + 税额」,末尾一行汇总不含税金额、税额与价税合计。 纯 XML 声明式实现,无需编写 Java 业务代码。
企业持有的应收 / 应付票据登记,逐行算「票据期限 = 到期日期 - 出票日期」天,末尾一行汇总票面金额。 纯 XML 声明式实现,无需编写 Java 业务代码。
人事维护的员工档案名册,登记工号、姓名、部门、职位、入职日期与薪酬标准,逐行算「年薪 = 月薪 × 12」,末尾一行汇总月薪与年薪总额。 纯 XML 声明式实现,无需编写 Java 业务代码。
员工出差后按行程登记交通费、住宿费与补贴,逐行算「小计 = 交通费 + 住宿费 + 补贴」,末尾一行汇总各费用项与报销总额。 纯 XML 声明式实现,无需编写 Java 业务代码。
按直线法计算固定资产年折旧额与月折旧额,逐行算「年折旧额 = 原值 × (1 - 残值率) / 使用年限」与「月折旧额 = 年折旧额 / 12」,末尾一行汇总原值、年折旧与月折旧。 纯 XML 声明式实现,无需编写 Java 业务代码。
按月统计员工考勤,逐行算「请假天数 = 应出勤 - 实出勤」与「出勤率 = 实出勤 / 应出勤」,末尾一行汇总应出勤、实出勤、请假天数、加班时数并给出整体出勤率。 纯 XML 声明式实现,无需编写 Java 业务代码。
登记员工培训档案,逐行记录课程、培训日期、学时与考核成绩,考核不及格的记录标红,末尾一行汇总总学时与平均成绩。 纯 XML 声明式实现,无需编写 Java 业务代码。
按物料比对供应商报价与内部基准价,逐行算「价差 = 报价 - 基准价」与「价差率 = 价差 / 基准价」,高于基准价的报价标红,末尾一行给出均价与平均价差率。 纯 XML 声明式实现,无需编写 Java 业务代码。
按订单登记商品明细,逐行算「金额 = 数量 × 单价」,末尾一行汇总发货数量与订单金额,适合直接套打成发货单。 纯 XML 声明式实现,无需编写 Java 业务代码。
期末逐项测算存货跌价,逐行算「账面成本 = 数量 × 单位成本」与「跌价准备 = 账面成本 - 可变现净值」,末尾一行汇总,跌价金额最高的存货标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐项登记领用材料的规格、数量与单价,算「金额 = 领用数量 × 单价」,末尾一行汇总领用总数量与总金额。 纯 XML 声明式实现,无需编写 Java 业务代码。
多家投标单位比价,按「技术得分 × 70% + 业绩得分 × 30%」算综合评分,末尾一行给出平均报价与平均综合评分,报价最高的投标单位标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
仓库按采购到货登记入库明细,逐行算「入库金额 = 入库数量 × 采购单价」,末尾一行汇总合计。 纯 XML 声明式实现,无需编写 Java 业务代码。
登记客户投诉单,末尾一行给出平均处理天数,超期投诉的处理天数标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
按直播场次登记带货商品,逐行算「销售额 = 销售数量 × 单价」与「佣金 = 销售额 × 佣金比例」,末尾一行汇总销售额与佣金。 纯 XML 声明式实现,无需编写 Java 业务代码。
按月份登记单一产品的销售数量与单价,逐行算「销售额 = 数量 × 单价」,并逐月算「环比增长率 = (本期销售额 - 上期销售额) / 上期销售额」,末尾一行汇总数量与销售额。 纯 XML 声明式实现,无需编写 Java 业务代码。
按退货明细登记物料,逐行算「退货金额 = 退货数量 × 单价」,末尾一行汇总退货数量与退货金额。 纯 XML 声明式实现,无需编写 Java 业务代码。
按研发项目登记「进度达成 / 质量 / 成本控制」三项得分,逐行算加权综合得分(进度 × 0.4 + 质量 × 0.35 + 成本 × 0.25),末尾一行给出平均综合得分;低分项目(综合得分最低)标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
登记合格供应商档案(编码 / 名称 / 类别 / 合作年限 / 联系人 / 电话 / 年供货额 / 评级),末尾一行汇总年供货额。 纯 XML 声明式实现,无需编写 Java 业务代码。
按会议登记场地费 / 餐饮费 / 住宿费,逐行算「合计金额 = 场地费 + 餐饮费 + 住宿费」,末尾一行汇总各项费用与合计;费用最高的会议标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐项登记验收资产的规格、数量与单价,算「金额 = 数量 × 单价」,末尾一行汇总验收总数量与总金额。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐部门登记目标完成分 / 工作质量分 / 团队协作分,按权重算「综合得分 = 完成分×0.5 + 质量分×0.3 + 协作分×0.2」,末尾一行给出各项平均分与平均综合得分;综合得分最低的部门标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐条出差记录登记天数与日补贴标准,算「补贴合计 = 出差天数 × 日补贴标准 + 交通补贴」,末尾一行汇总天数、交通补贴与补贴合计。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐产品登记销售额与成本,算「毛利 = 销售额 - 成本」「毛利率 = 毛利 / 销售额」「贡献占比 = 毛利 / 毛利合计」,末尾一行汇总销售额、成本与毛利;毛利率最低的产品标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐退货原因登记退货数量与金额,算「数量占比 = 该项退货数量 / 退货数量合计」,末尾一行汇总退货数量、金额与占比;占比最高的原因标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
演示 STYLE 与条件格式:表头主题配色、斑马纹、合计行高亮,以及按阈值给异常单元格标色。
共 33 个示例
按客户登记应收、已收、未收余额与账龄天数,逐行算「未收余额 = 应收 - 已收」与未收占比,末尾一行合计;账龄超长的异常客户单元格字体标红,提示重点催收。 纯 XML 声明式实现,无需编写 Java 业务代码。
按费用科目对比预算与实际,逐行算「差异 = 实际 - 预算」与「差异率 = 差异 / 预算」,末尾一行合计;结论为「超支」的科目结论单元格字体标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
对外开具的销项增值税发票登记,逐行算「税额 = 不含税金额 × 税率」与「价税合计 = 不含税金额 + 税额」,末尾一行汇总;作废发票的开票状态单元格标红,提示不可计入当期销项。 纯 XML 声明式实现,无需编写 Java 业务代码。
按季度分析增值税税负,逐行算「应纳增值税 = 销项税额 - 进项税额」与「税负率 = 应纳增值税 / 销售收入」,末尾一行汇总;税负最高的季度税负率单元格标红,提示重点关注。 纯 XML 声明式实现,无需编写 Java 业务代码。
质检部门按批次登记抽检数量与缺陷数量,逐行算「缺陷率 = 缺陷数量 / 抽检数量」,末尾一行汇总抽检、缺陷总数与整体缺陷率;缺陷率超标的批次单元格标红,提示判定不合格。 纯 XML 声明式实现,无需编写 Java 业务代码。
把库存按库龄区间分层,统计各区间物料种类数、库存金额与金额占比,末尾一行汇总;库龄最长的区间单元格标红,提示呆滞库存风险。 纯 XML 声明式实现,无需编写 Java 业务代码。
跟踪销售订单的发货执行情况,逐行算「未发货金额 = 订单金额 - 已发货金额」与「执行率 = 已发货金额 / 订单金额」,末尾一行汇总;仍未发货的订单执行率单元格标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
按物料比对当前库存与安全库存,逐行算「库存差额 = 当前库存 - 安全库存」,差额为负即触达预警线,末尾一行汇总两列库存数量并给出总差额。 纯 XML 声明式实现,无需编写 Java 业务代码。
按客户核算信用额度使用情况,逐行算「可用额度 = 信用额度 - 已用额度」与「额度使用率 = 已用额度 / 信用额度」,超出额度的客户使用率标红预警,末尾一行给出整体使用率。 纯 XML 声明式实现,无需编写 Java 业务代码。
按产品统计检验结果,逐行算「不良率 = 不良数量 / 检验数量」与「质量成本 = 不良数量 × 单件返工成本」,不良率超标的产品标红预警,末尾一行给出整体不良率。 纯 XML 声明式实现,无需编写 Java 业务代码。
按销售员核算目标达成情况,逐行算「达成率 = 实际销售额 / 销售目标」与「提成金额 = 实际销售额 × 提成比例」,未达成目标的销售员达成率标红,末尾一行给出整体达成率。 纯 XML 声明式实现,无需编写 Java 业务代码。
按生产工单跟踪计划数量与完成数量,逐行算「完成率 = 完成数量 / 计划数量」,逾期未完成的工单完成率标红,末尾一行给出整体完成率。 纯 XML 声明式实现,无需编写 Java 业务代码。
登记持票企业名下的银承汇票,末尾一行用 SUM 汇总票面金额,逾期汇票的剩余天数标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
登记客户拜访记录,末尾一行汇总拜访总时长,「待跟进」状态的客户标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
按月登记员工应发工资、社保公积金与个人所得税,逐行算「实发工资 = 应发 - 社保公积金 - 个税」,末尾一行汇总各列,个税最高者标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
按设备统计时间开动率、性能开动率与合格品率,逐行算「OEE = 三者之积」,末尾一行给出平均 OEE,OEE 低于标杆的设备标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
按房号登记物业费 / 水费 / 电费应收与已收金额,逐行算「欠缴金额 = 应收 - 已收」,末尾一行汇总应收、已收与欠缴;欠缴户的欠缴金额标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
按生产工单登记计划数量与实际产量,逐行算「达成率 = 实际产量 / 计划数量」,末尾一行给出平均达成率;未达成工单的达成率标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
按区域登记季度销售目标与实际销售额,逐行算「目标差额 = 实际 - 目标」与「达成率 = 实际 / 目标」,末尾一行给出全区域汇总;未达标区域标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
按来料批次登记送检数量与不合格数量,逐行算「不合格率 = 不合格数量 / 送检数量」,末尾一行给出汇总数量与整体不合格率;不合格率超标的批次标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐行登记里程碑的计划工时与实际工时,算「偏差工时 = 计划 - 实际」与「工时达成率 = 计划 / 实际」,末尾一行汇总总计划工时、总实际工时、总偏差与整体达成率;达成率最低的里程碑标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐门店登记销售额与成本,算「毛利 = 销售额 - 成本」「毛利率 = 毛利 / 销售额」「销售占比 = 本店销售额 / 全部销售额」,末尾一行汇总销售额、成本、毛利与整体毛利率;毛利率最低的门店标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
按检验批次登记抽检数量与缺陷数量,逐行算「缺陷率 = 缺陷数量 / 抽检数量」,末尾一行汇总总抽检量、总缺陷量与整体缺陷率;缺陷率超标的批次标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐物料比对账面数量与实盘数量,算「差异数量 = 实盘 - 账面」与「差异率 = 差异 / 账面」,末尾一行汇总账面总量、实盘总量、总差异与整体差异率;盘亏最严重的物料标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐供应商比对「我方应付」与「对方应收」,算「差异 = 我方应付 - 对方应收」,末尾一行汇总两边金额与总差异;有差异的供应商标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
按客户拆分应收账龄区间(30 天内 / 31-60 天 / 61 天以上),算「逾期金额 = 31-60 天 + 61 天以上」「逾期占比 = 逾期金额 / 应收金额」,末尾一行汇总各账龄区间与整体逾期占比;逾期占比最高的客户标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐费用项目对比预算与实际支出,算「偏差金额 = 预算 - 实际」与「偏差率 = 偏差 / 预算」,末尾一行汇总预算、实际与总偏差;超支最大的项目标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐产线登记计划产量与日产能,算「需求天数 = 计划产量 / 日产能」「计划投入工时 = 需求天数 × 8」,末尾一行汇总计划产量与投入工时;排期最长的产品标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐产品登记销售量与客诉数量,算「客诉率 = 客诉数量 / 销售量」「严重客诉占比 = 严重客诉 / 客诉数量」,末尾一行汇总销售量、客诉量、严重客诉量与整体客诉率;客诉率最高的产品标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐产品登记销售数量与退货数量,算「退货金额 = 退货数量 × 单价」「退货率 = 退货数量 / 销售数量」,末尾一行汇总销量、退货量与退货金额;退货率最高的产品标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐利润表项目登记本期与上期金额,算「同比增长额 = 本期 - 上期」「同比增长率 = 同比增长额 / 上期金额」,末尾一行汇总两期金额并给出整体增长;增长率最低项标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐工程节点登记合同金额与完成进度,算「应付进度款 = 合同金额 × 完成进度」「待付金额 = 应付进度款 - 已付金额」,末尾一行汇总合同、应付与已付并给出待付合计;待付金额最大的节点标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
逐销售员登记销售目标与实际业绩,算「完成率 = 实际业绩 / 销售目标」,末尾一行汇总目标与业绩并给出整体完成率;完成率最低的销售员标红。 纯 XML 声明式实现,无需编写 Java 业务代码。
演示 GRAPH 图表:柱形、折线、散点、雷达等图表写入独立 sheet,与数据表共同构成多 sheet 工作簿。
共 7 个示例
账户流水逐笔登记借方 / 贷方,并滚动计算余额(余额 = 上一行余额 + 本行贷方 - 本行借方),末尾一行合计;附余额趋势折线图。 导出为多 sheet 工作簿(银行流水对账单 / 余额趋势)。 纯 XML 声明式实现,无需编写 Java 业务代码。
四个产品的一至四季度销量,行末计算全年合计,末行按季度汇总;附柱形图对比各产品分季走势。 导出为多 sheet 工作簿(产品季度销量 / 产品季度销量图)。 纯 XML 声明式实现,无需编写 Java 业务代码。
各部门预算与实际费用对照,行末算差异(实际 - 预算),末行汇总;附三维条形图对比预实。 导出为多 sheet 工作簿(部门费用对比 / 部门费用对比图)。 纯 XML 声明式实现,无需编写 Java 业务代码。
按月登记期初 / 入库 / 出库,滚动计算期末(期末 = 期初 + 入库 - 出库),末行汇总;附三维面积图看库存趋势。统计与图表全部写在 导出为多 sheet 工作簿(库存量趋势 / 库存量趋势图)。 纯 XML 声明式实现,无需编写 Java 业务代码。
各门店广告投入与销售额,行末算投产比(销售额 / 广告投入),末行汇总;附散点图看投放与产出的关系。统计与图表全部写在 导出为多 sheet 工作簿(广告投产分析 / 广告投产散点图)。 纯 XML 声明式实现,无需编写 Java 业务代码。
三位员工在技术 / 沟通 / 协作 / 执行四个维度的评分,末行算各人平均分;附雷达图看能力画像。统计与图表全部写在 导出为多 sheet 工作簿(员工能力评估 / 员工能力雷达图)。 纯 XML 声明式实现,无需编写 Java 业务代码。
三个产品一至三月的销量矩阵,末行按月份汇总;附曲面图展示二维(产品 × 月份)分布。 导出为多 sheet 工作簿(月度销量矩阵 / 月度销量曲面图)。 纯 XML 声明式实现,无需编写 Java 业务代码。
演示 FOOTER 页脚套打:在单据底部生成制单 / 审核等固定说明行。
共 1 个示例
采购订单逐行「数量 × 单价 = 金额」,再按税率算「含税金额」,末尾一行合计,单据底部由 FOOTER 生成一行制单 / 审核签字说明。 纯 XML 声明式实现,无需编写 Java 业务代码。
演示 VALIDATION 填报表校验:导出填报模板,导入时逐项校验内容是否合规。
共 4 个示例
员工填报信息(工号 / 姓名 / 部门 / 年龄 / 入职日期 / 月薪 / 邮箱 / 手机号),末行给出平均年龄与月薪合计;导入时逐项校验填报内容是否合规。 Java 侧仅做分组与预处理,统计与版式全部由 XML 声明。
逐台设备登记点检人与运行参数,末尾一行汇总总运行时长;导入侧对填报内容做规则校验(编号 / 名称长度、运行时长整数与取值范围、温度上限、结论字典)。 纯 XML 声明式实现,无需编写 Java 业务代码。
汇总各员工的请假申请,末尾一行汇总请假总天数;导入时对填报内容做规则校验(工号 / 姓名长度、部门与请假类型字典、请假天数数值范围)。 纯 XML 声明式实现,无需编写 Java 业务代码。
汇总补录的客户档案(编号 / 名称 / 类型 / 信用等级 / 联系人 / 电话 / 年采购额 / 状态),末尾一行汇总年采购额;导入时对填报内容做规则校验。 纯 XML 声明式实现,无需编写 Java 业务代码。
演示 TRANSFORM 字段转换:码值映射、日期格式化、脱敏等导出期转换规则。
共 3 个示例
员工主数据里性别 / 部门 / 状态以内部码值存储,导出时按字典转成中文; 纯 XML 声明式实现,无需编写 Java 业务代码。
客户台账导出时对联系人姓名脱敏(保留首字),客户等级按字典由内部编码转为客户分层。 纯 XML 声明式实现,无需编写 Java 业务代码。
培训考核中分数换算为等级(≥90 A、≥80 B、≥60 C、其余 D),并规范化考核日期。 纯 XML 声明式实现,无需编写 Java 业务代码。