JQuick-Excel 导出图表使用规范
面向 Java 开发者的 JQuick-Excel 图表导出规范说明。
本文聚焦EXPORT WITH GRAPH = {...}的 DSL 配置、适用边界、代码约束与最佳实践。
1. 适用范围
JQuick-Excel 当前支持通过 DSL 方式直接导出 Excel 图表,底层基于 Apache POI XSSF / XDDF 实现。
当前支持的图表类型
| 类型编码 | 图表名称 | 典型场景 |
|---|---|---|
COLUMN |
柱状图 | 分类对比、销售额对比 |
BAR |
条形图 | 排名展示、横向比较 |
BAR3D |
3D 条形图 | 强展示场景 |
LINE |
折线图 | 趋势分析 |
PIE |
饼图 | 占比分析 |
AREA |
面积图 | 累积趋势 |
AREA3D |
3D 面积图 | 强展示场景 |
SCATTER |
散点图 | 相关性分析 |
RADAR |
雷达图 | 多维能力评估 |
SURFACE |
曲面图 | 三维矩阵数据 |
2. 基本设计原则
图表导出和普通表格导出不同,GRAPH 是“图表驱动”而不是“数据表驱动”。
也就是说:
MAPPING/FORMAT/TRANSFORM主要作用于表格单元格导出GRAPH使用的是独立图表数据模型:TYPE、TITLE、CATEGORIES、SERIES- 图表数据不会自动从
List<Map<String,Object>>中推导,必须在 DSL 中显式声明
建议把 GRAPH 理解为:
一段声明式图表配置 + 一组内嵌类别/序列数据 -> 生成 Excel 图表 Sheet
3. DSL 规范
3.1 标准结构
EXPORT WITH GRAPH = {
TYPE = COLUMN,
TITLE = "销售数据统计",
CATEGORY_AXIS = "产品",
VALUE_AXIS = "销量",
CATEGORIES = ["产品A", "产品B", "产品C", "产品D"],
SERIES = [
{
NAME = "第一季度",
DATA = [120, 200, 150, 180]
},
{
NAME = "第二季度",
DATA = [180, 210, 190, 220]
}
]
}
3.2 字段说明
| 字段 | 是否必填 | 类型 | 说明 |
|---|---|---|---|
TYPE |
是 | 枚举 | 图表类型 |
TITLE |
是 | String | 图表标题 |
CATEGORY_AXIS |
否 | String | 分类轴标题,常用于 X 轴 |
VALUE_AXIS |
否 | String | 数值轴标题,常用于 Y 轴 |
CATEGORIES |
是 | Array | 分类集合 |
SERIES |
是 | Array | 数据系列集合 |
SERIES[].NAME |
是 | String/Identifier | 系列名称 |
SERIES[].DATA |
是 | Array | 系列数值集合 |
4. 数据约束
这是程序员最需要关注的部分。
4.1 CATEGORIES.size() 必须与每个 SERIES.DATA.size() 一致
图表工厂内部使用:
- 第一列写
CATEGORIES - 后续列逐个写每个
SERIES - 然后根据单元格区域创建图表数据源
因此必须满足:
每个系列的数据点数量 == 分类数量
正确示例
CATEGORIES = ["Q1", "Q2", "Q3", "Q4"],
SERIES = [{
NAME = "产品A",
DATA = [100, 120, 130, 150]
}]
错误示例
CATEGORIES = ["Q1", "Q2", "Q3", "Q4"],
SERIES = [{
NAME = "产品A",
DATA = [100, 120]
}]
后者会导致图表数据区域不匹配,表现为:
- 图表数据缺失
- 图表显示异常
- 某些类型下直接生成错误图表
4.2 SERIES 至少要有一组
SERIES = []
这种配置没有业务意义,也不应该用于生产环境。
建议规范:
SERIES.size() >= 1- 每个
SERIES.DATA.size() >= 1
4.3 DATA 中应全部为数值
推荐使用:
intlongdoubleBigDecimal对应的数值字面量
不推荐:
- 文本数字,如
"120" - 带单位字符串,如
"120万" - 空字符串 / null
4.4 PIE 图建议只放一组系列
虽然底层接口允许按统一模型传入多组 SERIES,但从业务语义上:
- 饼图通常应只有 1 个系列
CATEGORIES表示扇区名称DATA表示每个扇区值
推荐写法:
TYPE = PIE,
CATEGORIES = ["Apple", "Samsung", "Xiaomi", "Other"],
SERIES = [{
NAME = "市场份额",
DATA = [38.5, 22.3, 15.7, 23.5]
}]
5. 各图表类型使用建议
5.1 COLUMN
适用于同维度多系列对比。
TYPE = COLUMN,
TITLE = "销售数据统计",
CATEGORY_AXIS = "产品",
VALUE_AXIS = "销量"
推荐场景:
- 产品销量对比
- 班级成绩对比
- 部门人力规模对比
5.2 BAR
适用于排名型数据,尤其分类名较长时优于 COLUMN。
推荐场景:
- 季度销售排行
- 城市 GDP 排名
- TopN 指标横向比较
5.3 LINE
适用于时间序列 / 趋势分析。
推荐场景:
- 月度温度变化
- 订单趋势
- 活跃用户增长曲线
5.4 PIE
适用于单组占比。
不适合:
- 类别过多
- 多系列并列比较
- 时间趋势分析
5.5 AREA / AREA3D
适用于累积型趋势展示,强调面积感。
5.6 SCATTER
适用于二维相关性分析。
例如:
- 身高 / 体重
- 价格 / 销量
- CPU 使用率 / 响应时间
5.7 RADAR
适用于多维能力评估,每组对象在多个维度下对比。
5.8 SURFACE
适用于三维矩阵型数据。这是当前最不适合普通业务报表的图表类型,建议仅在明确有三维展示诉求时使用。
6. Java 调用方式
6.1 最小可用示例
String rule = """
EXPORT WITH GRAPH = {
TYPE = COLUMN,
TITLE = "销售数据统计",
CATEGORY_AXIS = "产品",
VALUE_AXIS = "销量",
CATEGORIES = ["产品A", "产品B", "产品C", "产品D"],
SERIES = [{
NAME = "第一季度",
DATA = [120, 200, 150, 180]
}, {
NAME = "第二季度",
DATA = [180, 210, 190, 220]
}]
}
""";
JQuickExcelCommonExportExecutor executor = new JQuickExcelCommonExportExecutor();
JExcelExportModel config = (JExcelExportModel) executor.execute(rule);
JExcelExportHandler handler = new JExcelExportHandler(config, new ArrayList<>());
try (FileOutputStream out = new FileOutputStream("D://test//column.xlsx")) {
handler.getWorkBook().write(out);
}
6.2 推荐调用姿势
如果只是导出图表:
- 数据行可传空集合
new ArrayList<>() - 只通过
GRAPH生成独立图表工作表
如果要同时导出表格和图表:
- 当前更适合拆成两份导出逻辑
- 一份专门生成表格
- 一份专门生成图表
原因见第 8 节。
7. 底层行为说明
7.1 图表实际写入的是一个新 Sheet
当前实现里,图表工厂会:
workbook.createSheet(sheetName)- 把
CATEGORIES + SERIES先写到这个 sheet - 在这个 sheet 上创建 drawing / anchor / chart
所以:
- 图表不是附着在已有业务表格的指定区域上
TITLE同时也会被用作 sheetName 传入图表工厂- 若标题重复,可能引发工作表重名问题
7.2 GRAPH 会触发“禁用流式导出”
图表导出需要回头访问 workbook 中已写入内容并创建图表锚点,因此会触发 needsRandomRowAccess(config),从而禁用 SXSSF 流式导出。
这意味着:
- 即使全局开启了流式导出
- 只要配置了
GRAPH - 当前导出也会降级为
XSSFWorkbook
对程序员的含义是:
GRAPH 不适合超大数据量导出主链路。
建议:
- 图表导出用于汇总结果页、统计页
- 大批量明细数据导出与图表导出分离
8. 使用边界与限制
8.1 当前图表数据来源不是业务表格区域引用
当前实现是:
- 先把 DSL 中的
CATEGORIES、SERIES.DATA写到图表 sheet 中 - 再基于写入后的区域创建图表
这意味着当前 不支持:
- 从已有 Excel 数据区域自动绑定图表
- 直接引用业务 sheet 某一列作为图表数据源
- 在同一个业务 sheet 指定任意位置插图表
8.2 不适合复杂图表布局
当前没有 DSL 字段可以控制:
- 图表显示位置
- 图表大小
- 多图排版
- 图例位置
- 数据标签
- 颜色主题
- 次坐标轴
- 组合图(柱线混合图)
所以这套 GRAPH 更适合:
- 快速生成标准报表图
- 自动化导出中的基础统计展示
而不适合:
- 高定制 BI 报表
- PPT 级视觉排版
- 复杂 dashboard
8.3 TITLE 建议唯一
由于当前会把标题作为 sheetName 传入图表创建逻辑,因此建议:
- 同一个 workbook 中每个图表标题保持唯一
- 避免同名图表导致 sheetName 冲突
不推荐:
TITLE = "统计图"
推荐:
TITLE = "2025年第一季度销售统计图"
9. 推荐规范
9.1 DSL 编写规范
建议统一风格:
EXPORT WITH GRAPH = {
TYPE = COLUMN,
TITLE = "销售数据统计",
CATEGORY_AXIS = "产品",
VALUE_AXIS = "销量",
CATEGORIES = ["产品A", "产品B", "产品C"],
SERIES = [{
NAME = "第一季度",
DATA = [120, 200, 150]
}]
}
规范要求:
TYPE使用大写枚举值TITLE、CATEGORY_AXIS、VALUE_AXIS使用引号包裹CATEGORIES明确按展示顺序写入SERIES中DATA长度必须与CATEGORIES保持一致- 业务上没有意义时不要使用 3D 图
9.2 业务建模规范
在程序层建议先做图表 DTO,再拼 DSL,不要直接在 Controller 层硬编码字符串。
推荐模型:
class ChartRequest {
String type;
String title;
String categoryAxis;
String valueAxis;
List<String> categories;
List<SeriesRequest> series;
}
class SeriesRequest {
String name;
List<Number> data;
}
这样好处是:
- 便于参数校验
- 便于单元测试
- 便于后续扩展图表模板
9.3 导出前校验规范
在执行 executor.execute(rule) 前,建议先做以下校验:
type非空且属于支持列表title非空categories非空series非空- 每个
series.name非空 - 每个
series.data非空 - 所有
series.data.size()与categories.size()相同
10. 常见错误清单
错误 1:分类数量和数据点数量不一致
CATEGORIES = ["Q1", "Q2", "Q3"],
SERIES = [{
NAME = "产品A",
DATA = [120, 130]
}]
错误 2:饼图传多组系列
TYPE = PIE,
SERIES = [{...}, {...}]
虽然 DSL 可能可解析,但业务上通常不合理。
错误 3:把 GRAPH 当成表格图表绑定器
错误理解:
先导出表格,再让 GRAPH 自动引用表格列生成图表。
当前不是这个模式。
错误 4:大数据量场景仍强依赖 GRAPH
图表会禁用流式导出,不适合海量明细导出主流程。
错误 5:图表标题过于泛化
TITLE = "图表"
可能带来 sheetName 冲突,也不利于后续维护。
11. 推荐模板
11.1 柱状图模板
EXPORT WITH GRAPH = {
TYPE = COLUMN,
TITLE = "年度销售统计",
CATEGORY_AXIS = "产品",
VALUE_AXIS = "销量",
CATEGORIES = ["产品A", "产品B", "产品C", "产品D"],
SERIES = [{
NAME = "2025",
DATA = [120, 200, 150, 180]
}]
}
11.2 折线图模板
EXPORT WITH GRAPH = {
TYPE = LINE,
TITLE = "月度访问趋势",
CATEGORY_AXIS = "月份",
VALUE_AXIS = "访问量",
CATEGORIES = ["1月", "2月", "3月", "4月"],
SERIES = [{
NAME = "官网",
DATA = [1000, 1200, 1150, 1500]
}, {
NAME = "小程序",
DATA = [800, 900, 980, 1300]
}]
}
11.3 饼图模板
EXPORT WITH GRAPH = {
TYPE = PIE,
TITLE = "渠道占比",
CATEGORIES = ["官网", "小程序", "App", "线下"],
SERIES = [{
NAME = "占比",
DATA = [35, 25, 20, 20]
}]
}
11.4 雷达图模板
EXPORT WITH GRAPH = {
TYPE = RADAR,
TITLE = "工程师能力评估",
CATEGORIES = ["编码", "设计", "沟通", "测试", "学习"],
SERIES = [{
NAME = "张三",
DATA = [90, 85, 75, 80, 95]
}, {
NAME = "李四",
DATA = [80, 88, 82, 78, 85]
}]
}
12. 最佳实践总结
推荐
- 用
COLUMN / BAR / LINE / PIE解决 80% 报表图需求 - 导出前先做数据长度一致性校验
- 图表导出和大批量明细导出分离
- 保持
TITLE唯一且可读 - 在服务层先构建图表 DTO,再拼 DSL
不推荐
- 在 Controller 直接手写复杂 GRAPH DSL
- 在一个超大导出任务里混用 GRAPH
- 依赖 GRAPH 自动绑定已有表格区域
- 给
PIE、RADAR配置不合理的多系列结构 - 使用含义不明确的标题和类别值
13. 一句话结论
如果你站在程序员角度使用 JQuick-Excel 的图表能力,最重要的认知是:
GRAPH 是一套“声明式图表数据模型”,适合生成标准化统计图;它不是 BI 画布,也不是对已有表格区域的自动绑定器。
因此最佳用法是:
先在业务层整理好 categories + series,再用 DSL 生成标准图表导出。