新增JeecgBoot BPM流程自动生成器,包含流程创建、修改及审批人配置功能,支持自然语言描述转化为BPMN XML,并通过API与JeecgBoot系统交互。
This commit is contained in:
266
.trae/skills/jimureport/references/chart-config.md
Normal file
266
.trae/skills/jimureport/references/chart-config.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# 图表配置参考
|
||||
|
||||
## 图表模板文件位置
|
||||
|
||||
`src/main/resources/static/jmreport/desreport_/chartjson/`
|
||||
|
||||
生成图表报表时,应先读取对应的模板 JSON 文件作为 ECharts 配置基础。
|
||||
|
||||
## 可用图表类型
|
||||
|
||||
| 文件名 | chartType | 说明 |
|
||||
|--------|-----------|------|
|
||||
| `bar.simple.json` | `bar.simple` | 柱状图(单系列) |
|
||||
| `bar.multi.json` | `bar.multi` | 柱状图(多系列) |
|
||||
| `bar.stack.json` | `bar.stack` | 堆叠柱状图 |
|
||||
| `bar.horizontal.json` | `bar.horizontal` | 横向柱状图 |
|
||||
| `bar.multi.horizontal.json` | `bar.multi.horizontal` | 横向多系列柱状图 |
|
||||
| `bar.stack.horizontal.json` | `bar.stack.horizontal` | 横向堆叠柱状图 |
|
||||
| `bar.negative.json` | `bar.negative` | 正负柱状图 |
|
||||
| `bar.background.json` | `bar.background` | 带背景柱状图 |
|
||||
| `line.simple.json` | `line.simple` | 折线图(单系列) |
|
||||
| `line.multi.json` | `line.multi` | 折线图(多系列) |
|
||||
| `line.smooth.json` | `line.smooth` | 平滑曲线图 |
|
||||
| `line.area.json` | `line.area` | 面积图 |
|
||||
| `line.step.json` | `line.step` | 阶梯折线图 |
|
||||
| `pie.simple.json` | `pie.simple` | 饼图 |
|
||||
| `pie.doughnut.json` | `pie.doughnut` | 环形图 |
|
||||
| `pie.rose.json` | `pie.rose` | 玫瑰图 |
|
||||
| `mixed.linebar.json` | `mixed.linebar` | 柱状+折线混合图 |
|
||||
| `radar.basic.json` | `radar.basic` | 雷达图 |
|
||||
| `radar.custom.json` | `radar.custom` | 自定义雷达图 |
|
||||
| `scatter.simple.json` | `scatter.simple` | 散点图 |
|
||||
| `scatter.bubble.json` | `scatter.bubble` | 气泡图 |
|
||||
| `funnel.simple.json` | `funnel.simple` | 漏斗图 |
|
||||
| `funnel.pyramid.json` | `funnel.pyramid` | 金字塔图 |
|
||||
| `gauge.simple.json` | `gauge.simple` | 仪表盘 |
|
||||
| `gauge.simple180.json` | `gauge.simple180` | 半圆仪表盘 |
|
||||
| `graph.simple.json` | `graph.simple` | 关系图 |
|
||||
| `map.simple.json` | `map.simple` | 地图 |
|
||||
| `map.scatter.json` | `map.scatter` | 地图散点 |
|
||||
| `pictorial.spirits.json` | `pictorial.spirits` | 象形柱图 |
|
||||
|
||||
## echartslist.json 主要 key 对照
|
||||
|
||||
| key | 对应图表 |
|
||||
|-----|---------|
|
||||
| `bar` | 单系列柱状图 |
|
||||
| `bar2` | dataset 模式柱状图 |
|
||||
| `bar3` | 多系列柱状图 |
|
||||
| `line` | 单系列折线图 |
|
||||
| `line3` | 平滑曲线 |
|
||||
| `line4` | 多系列折线图 |
|
||||
| `line5` | 阶梯折线图 |
|
||||
| `pie` | 饼图 |
|
||||
| `pie1` | 环形图 |
|
||||
| `pie2` | 玫瑰图 |
|
||||
| `linebar` | 柱状+折线混合 |
|
||||
| `map` | 地图 |
|
||||
| `scatter` | 散点图 |
|
||||
|
||||
## 图表在 jsonStr 中的配置
|
||||
|
||||
图表通过**单元格占位 + chartList 配置**实现,不是绝对定位。需要两部分配合:
|
||||
|
||||
### 1. chartList 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"chartList": [
|
||||
{
|
||||
"row": 5,
|
||||
"col": 1,
|
||||
"colspan": 0,
|
||||
"rowspan": 0,
|
||||
"width": "500",
|
||||
"height": "350",
|
||||
"config": "ECharts配置JSON字符串",
|
||||
"url": "",
|
||||
"extData": {
|
||||
"chartType": "bar.simple",
|
||||
"dataType": "sql",
|
||||
"dataId": "数据集ID",
|
||||
"dbCode": "数据集编码",
|
||||
"axisX": "name",
|
||||
"axisY": "value",
|
||||
"series": "type",
|
||||
"xText": "",
|
||||
"yText": "",
|
||||
"apiStatus": "1"
|
||||
},
|
||||
"layer_id": "唯一层ID",
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"backgroud": {"enabled": false, "color": "#fff", "image": ""},
|
||||
"virtualCellRange": [[5,1],[5,2],[5,3],[6,1],[6,2],[6,3]]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> **关键字段说明:**
|
||||
>
|
||||
> | 字段 | 类型 | 说明 |
|
||||
> |------|------|------|
|
||||
> | `row` / `col` | number | 图表起始位置(行号/列号),**不是 left/top 像素值** |
|
||||
> | `width` / `height` | **string** | 图表宽高像素,**必须是字符串**(如 `"500"`,不是 `500`) |
|
||||
> | `virtualCellRange` | array | 图表占据的所有单元格坐标 `[[row,col], ...]` |
|
||||
> | `layer_id` | string | 唯一标识,对应 rows 中 cells 的 `virtual` 属性 |
|
||||
> | `backgroud` | object | 图表背景(注意拼写是 `backgroud` 不是 `background`) |
|
||||
> | `offsetX` / `offsetY` | number | 偏移量,通常为 0 |
|
||||
|
||||
### 2. rows 中的 virtual 占位
|
||||
|
||||
图表占据的每个单元格必须在 `rows` 中声明 `"virtual": "layer_id"`:
|
||||
|
||||
```json
|
||||
"rows": {
|
||||
"5": {
|
||||
"cells": {
|
||||
"1": {"text": " ", "virtual": "chart_xxx"},
|
||||
"2": {"text": " ", "virtual": "chart_xxx"},
|
||||
"3": {"text": " ", "virtual": "chart_xxx"}
|
||||
}
|
||||
},
|
||||
"6": {
|
||||
"cells": {
|
||||
"1": {"text": " ", "virtual": "chart_xxx"},
|
||||
"2": {"text": " ", "virtual": "chart_xxx"},
|
||||
"3": {"text": " ", "virtual": "chart_xxx"}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **注意:**
|
||||
> - `virtual` 的值必须和 `chartList[].layer_id` 一致
|
||||
> - `text` 设为 `" "`(一个空格),不能为空字符串
|
||||
> - 图表区域的行数 × 列数 = `virtualCellRange` 的元素数量
|
||||
> - 图表区域不能和列表数据行重叠
|
||||
|
||||
### extData 关键字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `chartType` | 图表类型(如 `bar.simple`, `line.multi`, `pie.simple`) |
|
||||
| `dataType` | 数据来源:`"sql"` / `"api"` / `"json"` / `"javabean"` / `"files"`(前端文本值,与 dbType 数字不同) |
|
||||
| `dataId` | 数据集ID(saveDb 返回的 id) |
|
||||
| `dbCode` | 数据集编码 |
|
||||
| `axisX` | X轴/分类字段名,**固定为 `name`** |
|
||||
| `axisY` | Y轴/数值字段名,**固定为 `value`** |
|
||||
| `series` | 系列/分组字段名,**固定为 `type`**(单系列也要传 `"type"`) |
|
||||
| `apiStatus` | API 数据集是否启用(`"1"` = 启用) |
|
||||
| `dataId1` | 第二数据集ID(关系图 `graph.simple` 使用) |
|
||||
| `isCustomPropName` | 是否自定义字段映射(默认不填,使用 name/value/type) |
|
||||
|
||||
### 图表字段映射规则
|
||||
|
||||
> **重要:图表数据绑定使用固定的三个字段名,不是数据集的原始字段名。**
|
||||
|
||||
| extData 字段 | 固定值 | 含义 | 示例 |
|
||||
|-------------|--------|------|------|
|
||||
| `axisX` | `name` | X轴/分类 | 产品名称 |
|
||||
| `axisY` | `value` | Y轴/数值 | 销售额 |
|
||||
| `series` | `type` | 系列/分组(多系列) | 月份、类别 |
|
||||
|
||||
前端渲染时会将数据集查询结果按 `name`/`value`/`type` 进行映射:
|
||||
- 单系列图表:`series` 也传 `"type"`(数据中 type 字段可为空字符串)
|
||||
- 多系列图表:`series` = `"type"`,按 `type` 值分组生成多条系列
|
||||
|
||||
**SQL 数据集示例(需要 AS 别名映射到 name/value):**
|
||||
```sql
|
||||
SELECT product_name AS name, sales_amount AS value FROM sales_table
|
||||
```
|
||||
|
||||
**多系列 SQL 示例(加 type 字段):**
|
||||
```sql
|
||||
SELECT month AS name, amount AS value, category AS type FROM sales_table
|
||||
```
|
||||
|
||||
**JSON 数据集示例:**
|
||||
```json
|
||||
{"data": [
|
||||
{"name": "螺丝钉", "value": 5000, "type": ""},
|
||||
{"name": "电阻器", "value": 3200, "type": ""}
|
||||
]}
|
||||
```
|
||||
|
||||
## 使用流程
|
||||
|
||||
1. 根据需求确定 `chartType`
|
||||
2. 读取对应的 `chartjson/{chartType}.json` 文件作为 ECharts 配置模板
|
||||
3. 修改模板中的 `title.text`、`series` 等,`data` 留空(由数据集驱动)
|
||||
4. 将配置 JSON 字符串化后放入 `chartList[].config`
|
||||
5. 配置 `extData`:`axisX`=`name`,`axisY`=`value`,`series`=`type`
|
||||
6. 数据集字段必须包含 `name` 和 `value`(SQL 用 AS 别名,JSON 直接命名)
|
||||
7. 确定图表占位区域(起始 row/col,占几行几列)
|
||||
8. 在 `rows` 中为每个占位 cell 添加 `"virtual": "layer_id"`
|
||||
9. 构造 `virtualCellRange`(所有占位坐标数组)
|
||||
10. 将 `chartList` 放入 jsonStr 顶层
|
||||
|
||||
## 完整示例(列表 + 柱状图)
|
||||
|
||||
### 数据集配置
|
||||
|
||||
列表和图表使用**两个独立数据集**(db_code 唯一):
|
||||
|
||||
| 数据集 | dbCode | dbType | 用途 | 字段 |
|
||||
|--------|--------|--------|------|------|
|
||||
| 进库列表 | `stocklist` | 3(JSON) | 列表展示 | name, quantity, stock_time |
|
||||
| 进库图表 | `stockchart` | 3(JSON) | 柱状图 | **name, value** |
|
||||
|
||||
### Python 生成图表占位的关键代码
|
||||
|
||||
```python
|
||||
layer_id = "chart_" + gen_id()
|
||||
|
||||
# 图表占据 row5~row14, col1~col5
|
||||
chart_row_start, chart_row_end = 5, 14
|
||||
chart_col_start, chart_col_end = 1, 5
|
||||
|
||||
# 1. 构造 virtualCellRange
|
||||
virtual_cell_range = []
|
||||
for r in range(chart_row_start, chart_row_end + 1):
|
||||
for c in range(chart_col_start, chart_col_end + 1):
|
||||
virtual_cell_range.append([r, c])
|
||||
|
||||
# 2. 构造 rows 中的 virtual 占位 cells
|
||||
chart_rows = {}
|
||||
for r in range(chart_row_start, chart_row_end + 1):
|
||||
cells = {}
|
||||
for c in range(chart_col_start, chart_col_end + 1):
|
||||
cells[str(c)] = {"text": " ", "virtual": layer_id}
|
||||
chart_rows[str(r)] = {"cells": cells}
|
||||
|
||||
# 3. 合并到 all_rows
|
||||
all_rows.update(chart_rows)
|
||||
|
||||
# 4. chartList 配置
|
||||
chart_item = {
|
||||
"row": chart_row_start,
|
||||
"col": chart_col_start,
|
||||
"colspan": 0,
|
||||
"rowspan": 0,
|
||||
"width": "500", # 字符串!
|
||||
"height": "350", # 字符串!
|
||||
"config": json.dumps(chart_config, ensure_ascii=False),
|
||||
"url": "",
|
||||
"extData": {
|
||||
"chartType": "bar.simple",
|
||||
"dataType": "json",
|
||||
"dataId": chart_db_id,
|
||||
"dbCode": "stockchart",
|
||||
"axisX": "name",
|
||||
"axisY": "value",
|
||||
"series": "type",
|
||||
"xText": "",
|
||||
"yText": "",
|
||||
"apiStatus": "1"
|
||||
},
|
||||
"layer_id": layer_id,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"backgroud": {"enabled": False, "color": "#fff", "image": ""},
|
||||
"virtualCellRange": virtual_cell_range
|
||||
}
|
||||
```
|
||||
343
.trae/skills/jimureport/references/chart-templates.md
Normal file
343
.trae/skills/jimureport/references/chart-templates.md
Normal file
@@ -0,0 +1,343 @@
|
||||
# 图表模板快速参考
|
||||
|
||||
积木报表内置 30+ 图表模板,文件位于 `static/jmreport/desreport_/chartjson/`。
|
||||
可通过 `GET /jmreport/addChart?chartType=bar.simple` 获取模板配置。
|
||||
|
||||
生成图表时,从模板中取 ECharts 配置,修改 `title.text`,清空 `data`(由数据集驱动),然后放入 `chartList[].config`。
|
||||
|
||||
## 图表分类速查
|
||||
|
||||
### 柱状图 (Bar)
|
||||
|
||||
| chartType | 说明 | 数据集要求 |
|
||||
|-----------|------|-----------|
|
||||
| `bar.simple` | 单系列柱状图 | `name, value` |
|
||||
| `bar.multi` | 多系列柱状图 | `name, value, type` |
|
||||
| `bar.stack` | 堆叠柱状图 | `name, value, type` |
|
||||
| `bar.horizontal` | 横向柱状图 | `name, value` |
|
||||
| `bar.multi.horizontal` | 横向多系列 | `name, value, type` |
|
||||
| `bar.stack.horizontal` | 横向堆叠 | `name, value, type` |
|
||||
| `bar.negative` | 正负柱状图 | `name, value, type` |
|
||||
| `bar.background` | 带背景柱状图 | `name, value` |
|
||||
|
||||
### 折线图 (Line)
|
||||
|
||||
| chartType | 说明 | 数据集要求 |
|
||||
|-----------|------|-----------|
|
||||
| `line.simple` | 单系列折线图 | `name, value` |
|
||||
| `line.multi` | 多系列折线图 | `name, value, type` |
|
||||
| `line.smooth` | 平滑曲线图 | `name, value` |
|
||||
| `line.area` | 面积图 | `name, value, type` |
|
||||
| `line.step` | 阶梯折线图 | `name, value` |
|
||||
|
||||
### 饼图 (Pie)
|
||||
|
||||
| chartType | 说明 | 数据集要求 |
|
||||
|-----------|------|-----------|
|
||||
| `pie.simple` | 饼图 | `name, value` |
|
||||
| `pie.doughnut` | 环形图 | `name, value` |
|
||||
| `pie.rose` | 玫瑰图(南丁格尔) | `name, value` |
|
||||
|
||||
### 混合图
|
||||
|
||||
| chartType | 说明 | 数据集要求 |
|
||||
|-----------|------|-----------|
|
||||
| `mixed.linebar` | 柱状+折线混合 | `name, value, type` |
|
||||
|
||||
### 其他图表
|
||||
|
||||
| chartType | 说明 | 数据集要求 |
|
||||
|-----------|------|-----------|
|
||||
| `gauge.simple` | 仪表盘 | `name, value` |
|
||||
| `gauge.simple180` | 半圆仪表盘 | `name, value` |
|
||||
| `radar.basic` | 雷达图 | 特殊(indicator) |
|
||||
| `radar.custom` | 自定义雷达图 | 特殊 |
|
||||
| `funnel.simple` | 漏斗图 | `name, value` |
|
||||
| `funnel.pyramid` | 金字塔图 | `name, value` |
|
||||
| `scatter.simple` | 散点图 | 特殊 |
|
||||
| `scatter.bubble` | 气泡图 | 特殊 |
|
||||
| `map.simple` | 地图 | 特殊 |
|
||||
| `map.scatter` | 地图散点 | 特殊 |
|
||||
| `graph.simple` | 关系图 | 特殊(需两个数据集) |
|
||||
| `pictorial.spirits` | 象形柱图 | `name, value` |
|
||||
|
||||
## 常用图表 ECharts 配置模板
|
||||
|
||||
### bar.simple — 单系列柱状图
|
||||
|
||||
```python
|
||||
{
|
||||
"title": {"show": True, "text": "标题", "left": "left", "top": "5",
|
||||
"padding": [5,20,5,20],
|
||||
"textStyle": {"fontSize": 18, "fontWeight": "bolder", "color": "#c23531"}},
|
||||
"grid": {"left": 60, "top": 60, "right": 100, "bottom": 60},
|
||||
"tooltip": {"show": True, "textStyle": {"color": "#fff", "fontSize": 18}},
|
||||
"xAxis": {"show": True, "name": "", "data": [],
|
||||
"axisLabel": {"textStyle": {"fontSize": 12, "color": "#333"}},
|
||||
"axisLine": {"lineStyle": {"color": "#333"}}},
|
||||
"yAxis": {"show": True, "name": "",
|
||||
"axisLabel": {"textStyle": {"fontSize": 12, "color": "#333"}},
|
||||
"axisLine": {"lineStyle": {"color": "#333"}}},
|
||||
"series": [{"name": "", "type": "bar", "data": [],
|
||||
"barWidth": 50, "barMinHeight": 2,
|
||||
"itemStyle": {"barBorderRadius": 0, "color": "#c43632"}}]
|
||||
}
|
||||
```
|
||||
|
||||
### bar.multi — 多系列柱状图
|
||||
|
||||
```python
|
||||
{
|
||||
"title": {"show": True, "text": "标题", "left": "left",
|
||||
"padding": [5,20,5,20],
|
||||
"textStyle": {"fontSize": 18, "fontWeight": "bolder", "color": "#c23531"}},
|
||||
"legend": {"show": True, "data": [], "top": "top", "left": "center",
|
||||
"orient": "horizontal", "padding": [25,20,25,10],
|
||||
"textStyle": {"color": "#333", "fontSize": 12}},
|
||||
"grid": {"left": 60, "top": 60, "right": 100, "bottom": 60},
|
||||
"tooltip": {"show": True, "trigger": "axis",
|
||||
"axisPointer": {"type": "shadow"},
|
||||
"textStyle": {"color": "#fff", "fontSize": 18}},
|
||||
"xAxis": {"show": True, "type": "category", "data": [],
|
||||
"axisLabel": {"textStyle": {"fontSize": 12, "color": "#333"}},
|
||||
"axisLine": {"lineStyle": {"color": "#333"}}},
|
||||
"yAxis": {"show": True,
|
||||
"axisLabel": {"textStyle": {"fontSize": 12, "color": "#333"}},
|
||||
"axisLine": {"lineStyle": {"color": "#333"}}},
|
||||
"series": [
|
||||
{"name": "系列1", "type": "bar", "data": [], "barWidth": 0, "barMinHeight": 2,
|
||||
"label": {"show": True, "position": "top", "textStyle": {"color": "black", "fontSize": 12}},
|
||||
"itemStyle": {"barBorderRadius": 0, "color": ""}},
|
||||
{"name": "系列2", "type": "bar", "data": [], "barWidth": 0, "barMinHeight": 2,
|
||||
"label": {"show": True, "position": "top", "textStyle": {"color": "black", "fontSize": 12}},
|
||||
"itemStyle": {"barBorderRadius": 0, "color": ""}}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### line.simple — 单系列折线图
|
||||
|
||||
```python
|
||||
{
|
||||
"title": {"show": True, "text": "标题",
|
||||
"textStyle": {"fontSize": 18, "fontWeight": "bolder", "color": "#c23531"}},
|
||||
"grid": {"left": 60, "top": 60, "right": 100, "bottom": 60},
|
||||
"xAxis": {"show": True, "data": []},
|
||||
"yAxis": {"show": True, "name": ""},
|
||||
"series": [{"name": "", "type": "line", "data": [],
|
||||
"smooth": False, "showSymbol": True, "symbolSize": 5,
|
||||
"lineStyle": {"width": 2, "color": "#c43632"}}]
|
||||
}
|
||||
```
|
||||
|
||||
### pie.simple — 饼图
|
||||
|
||||
```python
|
||||
{
|
||||
"title": {"show": True, "text": "标题",
|
||||
"textStyle": {"fontSize": 18, "fontWeight": "bolder", "color": "#c23531"}},
|
||||
"tooltip": {"show": True, "formatter": "{a} <br/>{b} : {c}"},
|
||||
"legend": {"show": True, "data": [], "orient": "horizontal",
|
||||
"textStyle": {"color": "#333", "fontSize": 12}},
|
||||
"series": [{"name": "数据", "type": "pie",
|
||||
"radius": "55%", "minAngle": 0,
|
||||
"center": [320, 180],
|
||||
"label": {"show": True, "position": "outside"},
|
||||
"data": []}]
|
||||
}
|
||||
```
|
||||
|
||||
### pie.doughnut — 环形图
|
||||
|
||||
```python
|
||||
{
|
||||
"title": {"show": True, "text": "标题",
|
||||
"textStyle": {"fontSize": 18, "fontWeight": "bolder", "color": "#c23531"}},
|
||||
"tooltip": {"show": True, "formatter": "{a} <br/>{b} : {c}"},
|
||||
"legend": {"show": True, "data": [], "orient": "horizontal",
|
||||
"textStyle": {"color": "#333", "fontSize": 12}},
|
||||
"series": [{"name": "数据", "type": "pie",
|
||||
"isRadius": True,
|
||||
"radius": ["45%", "55%"], # 内外半径 → 环形
|
||||
"minAngle": 0, "roseType": "", "isRose": False,
|
||||
"center": [320, 180],
|
||||
"label": {"show": True, "position": "outside"},
|
||||
"data": []}]
|
||||
}
|
||||
```
|
||||
|
||||
### mixed.linebar — 柱线混合图
|
||||
|
||||
```python
|
||||
{
|
||||
"chartType": "linebar",
|
||||
"title": {"show": True, "text": "标题",
|
||||
"textStyle": {"fontSize": 18, "fontWeight": "bolder", "color": "#c23531"}},
|
||||
"legend": {"data": []},
|
||||
"xAxis": {"type": "category", "data": []},
|
||||
"yAxis": [
|
||||
{"type": "value", "name": "左轴"},
|
||||
{"type": "value", "name": "右轴"}
|
||||
],
|
||||
"series": [
|
||||
{"name": "柱状", "type": "bar", "data": []},
|
||||
{"name": "折线", "type": "line", "data": []},
|
||||
{"name": "右轴数据", "type": "bar", "yAxisIndex": 1, "data": []}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### gauge.simple — 仪表盘
|
||||
|
||||
```python
|
||||
{
|
||||
"title": {"show": True, "text": "标题",
|
||||
"textStyle": {"fontSize": 18, "fontWeight": "bolder", "color": "#c23531"}},
|
||||
"tooltip": {"show": True, "formatter": "{b} : {c}"},
|
||||
"series": [{"name": "业务指标", "type": "gauge",
|
||||
"radius": "75%", "center": [330, 200],
|
||||
"itemStyle": {"color": "#63869E"},
|
||||
"pointer": {"show": True},
|
||||
"detail": {"formatter": "{value}%",
|
||||
"textStyle": {"color": "rgba(0,0,0,1)", "fontSize": 25}},
|
||||
"axisLine": {"lineStyle": {
|
||||
"color": [[0.2, "#91c7ae"], [0.8, "#63869E"], [1, "#C23531"]],
|
||||
"width": 25}},
|
||||
"data": [{"value": 50, "name": "完成率"}]}]
|
||||
}
|
||||
```
|
||||
|
||||
### funnel.simple — 漏斗图
|
||||
|
||||
```python
|
||||
{
|
||||
"title": {"show": True, "text": "标题",
|
||||
"textStyle": {"fontSize": 18, "fontWeight": "bolder", "color": "#c23531"}},
|
||||
"legend": {"show": True, "data": []},
|
||||
"tooltip": {"show": True, "trigger": "item", "formatter": "{b} : {c}"},
|
||||
"series": [{"name": "漏斗图", "type": "funnel",
|
||||
"left": "10%", "top": 60, "bottom": 60, "width": "80%",
|
||||
"sort": "descending", "gap": 2, "orient": "vertical",
|
||||
"label": {"show": True, "position": "inside",
|
||||
"textStyle": {"fontSize": 16}},
|
||||
"itemStyle": {"borderColor": "#fff", "borderWidth": 1},
|
||||
"data": []}]
|
||||
}
|
||||
```
|
||||
|
||||
### radar.basic — 雷达图
|
||||
|
||||
```python
|
||||
{
|
||||
"title": {"show": True, "text": "标题",
|
||||
"textStyle": {"fontSize": 18, "fontWeight": "bolder", "color": "#c23531"}},
|
||||
"legend": {"show": True, "data": []},
|
||||
"tooltip": {"show": True},
|
||||
"radar": [{"shape": "polygon", "center": [320, 200],
|
||||
"name": {"formatter": "【{value}】",
|
||||
"textStyle": {"fontSize": 14, "color": "#72ACD1"}},
|
||||
"indicator": [
|
||||
{"name": "维度1", "max": 100},
|
||||
{"name": "维度2", "max": 100},
|
||||
{"name": "维度3", "max": 100}
|
||||
]}],
|
||||
"series": [{"name": "", "type": "radar",
|
||||
"data": [{"value": [80, 60, 70], "name": "系列1"}]}]
|
||||
}
|
||||
```
|
||||
|
||||
## 数据集 SQL 映射规则
|
||||
|
||||
图表数据集使用固定的 `name`/`value`/`type` 字段映射:
|
||||
|
||||
```sql
|
||||
-- 单系列(bar.simple, line.simple, pie.simple 等)
|
||||
SELECT product_name AS name, sales_amount AS value, '' AS type
|
||||
FROM sales_table
|
||||
|
||||
-- 多系列(bar.multi, line.multi, mixed.linebar 等)
|
||||
SELECT month AS name, amount AS value, category AS type
|
||||
FROM sales_table
|
||||
|
||||
-- 仪表盘(gauge)
|
||||
SELECT '完成率' AS name, ROUND(done*100/total) AS value, '' AS type
|
||||
FROM task_summary
|
||||
|
||||
-- 漏斗图(funnel)
|
||||
SELECT stage AS name, count AS value, '' AS type
|
||||
FROM funnel_data ORDER BY count DESC
|
||||
```
|
||||
|
||||
## 快速生成图表的 Python 代码
|
||||
|
||||
```python
|
||||
def create_chart(chart_type, title, db_code, db_id, row_start, col_start,
|
||||
rows=10, cols=5, width="650", height="350"):
|
||||
"""快速生成图表配置"""
|
||||
layer_id = "chart_" + gen_id()
|
||||
|
||||
# 根据类型选择基础配置
|
||||
base_configs = {
|
||||
"bar.simple": {
|
||||
"title": {"text": title, "left": "center", "top": "10"},
|
||||
"tooltip": {"trigger": "axis", "axisPointer": {"type": "shadow"}},
|
||||
"grid": {"left": "3%", "right": "4%", "bottom": "3%", "containLabel": True},
|
||||
"xAxis": [{"type": "category", "data": []}],
|
||||
"yAxis": [{"type": "value"}],
|
||||
"series": [{"type": "bar", "data": [], "barWidth": "40%"}]
|
||||
},
|
||||
"pie.simple": {
|
||||
"title": {"text": title, "left": "center"},
|
||||
"tooltip": {"trigger": "item", "formatter": "{b}: {c} ({d}%)"},
|
||||
"legend": {"bottom": "5%", "left": "center"},
|
||||
"series": [{"type": "pie", "radius": "55%", "center": ["50%", "50%"], "data": []}]
|
||||
},
|
||||
"line.simple": {
|
||||
"title": {"text": title, "left": "center"},
|
||||
"tooltip": {"trigger": "axis"},
|
||||
"grid": {"left": "3%", "right": "4%", "bottom": "3%", "containLabel": True},
|
||||
"xAxis": [{"type": "category", "data": []}],
|
||||
"yAxis": [{"type": "value"}],
|
||||
"series": [{"type": "line", "smooth": True, "data": []}]
|
||||
},
|
||||
"gauge.simple": {
|
||||
"title": {"text": title, "left": "center"},
|
||||
"series": [{"type": "gauge", "radius": "75%",
|
||||
"detail": {"formatter": "{value}%"},
|
||||
"data": [{"value": 0, "name": ""}]}]
|
||||
}
|
||||
}
|
||||
config = base_configs.get(chart_type, base_configs["bar.simple"])
|
||||
|
||||
# virtual cells
|
||||
row_end = row_start + rows - 1
|
||||
col_end = col_start + cols - 1
|
||||
virtual_cells = [[r,c] for r in range(row_start, row_end+1) for c in range(col_start, col_end+1)]
|
||||
|
||||
# rows 占位
|
||||
chart_rows = {}
|
||||
for r in range(row_start, row_end + 1):
|
||||
cells = {}
|
||||
for c in range(col_start, col_end + 1):
|
||||
cells[str(c)] = {"text": " ", "virtual": layer_id}
|
||||
chart_rows[str(r)] = {"cells": cells}
|
||||
|
||||
chart_item = {
|
||||
"row": row_start, "col": col_start, "colspan": 0, "rowspan": 0,
|
||||
"width": width, "height": height,
|
||||
"config": json.dumps(config, ensure_ascii=False),
|
||||
"url": "",
|
||||
"extData": {
|
||||
"chartType": chart_type, "dataType": "sql",
|
||||
"dataId": str(db_id), "dbCode": db_code,
|
||||
"axisX": "name", "axisY": "value", "series": "type",
|
||||
"xText": "", "yText": "", "apiStatus": "1"
|
||||
},
|
||||
"layer_id": layer_id,
|
||||
"offsetX": 0, "offsetY": 0,
|
||||
"backgroud": {"enabled": False, "color": "#fff", "image": ""},
|
||||
"virtualCellRange": virtual_cells
|
||||
}
|
||||
|
||||
return chart_item, chart_rows
|
||||
```
|
||||
328
.trae/skills/jimureport/references/components.md
Normal file
328
.trae/skills/jimureport/references/components.md
Normal file
@@ -0,0 +1,328 @@
|
||||
# 报表组件参考
|
||||
|
||||
积木报表支持 4 种图层组件(Layer),它们以独立数组存储在 jsonStr 顶层,与 rows/cols 平级。
|
||||
|
||||
## 组件类型总览
|
||||
|
||||
| 组件 | jsonStr 字段 | 类型标识 | 用途 |
|
||||
|------|-------------|---------|------|
|
||||
| 图片 | `imgList` | `img` | 外部图片/Logo/背景图 |
|
||||
| 图表 | `chartList` | `chart` | ECharts 可视化图表 |
|
||||
| 条形码 | `barcodeList` | `barcode` | CODE128/EAN 等一维码 |
|
||||
| 二维码 | `qrcodeList` | `qrcode` | QR Code 二维码 |
|
||||
|
||||
所有组件共享以下基础属性:
|
||||
|
||||
```python
|
||||
{
|
||||
"row": 0, # 起始行号(从0开始)
|
||||
"col": 0, # 起始列号(从0开始)
|
||||
"colspan": 0, # 列跨度
|
||||
"rowspan": 0, # 行跨度
|
||||
"width": 300, # 宽度(像素,图表必须是字符串如"300")
|
||||
"height": 200, # 高度(像素,图表必须是字符串如"200")
|
||||
"layer_id": "唯一ID", # 唯一标识,对应 virtual cell
|
||||
"offsetX": 0, # X偏移
|
||||
"offsetY": 0, # Y偏移
|
||||
"virtualCellRange": [[row,col], ...] # 占据的所有单元格坐标
|
||||
}
|
||||
```
|
||||
|
||||
## 1. 图片组件 (imgList)
|
||||
|
||||
### JSON 结构
|
||||
|
||||
```python
|
||||
img_item = {
|
||||
"row": 0,
|
||||
"col": 1,
|
||||
"colspan": 0,
|
||||
"rowspan": 0,
|
||||
"width": 315, # 数字
|
||||
"height": 151, # 数字
|
||||
"src": "/jmreport/img/upload/xxx.png", # 图片路径(相对或绝对)
|
||||
"isBackend": False, # 是否作为前置遮罩
|
||||
"isBackendImg": False, # 是否作为背景图
|
||||
"commonBackend": None, # 共享遮罩/背景属性
|
||||
"layer_id": "img_xxx",
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"virtualCellRange": [[0,1],[0,2],[1,1],[1,2]]
|
||||
}
|
||||
```
|
||||
|
||||
### 图片路径说明
|
||||
|
||||
| 类型 | 示例 |
|
||||
|------|------|
|
||||
| 上传图片 | `/jmreport/img/upload/xxx.png` |
|
||||
| 外部URL | `https://example.com/logo.png` |
|
||||
| 数据绑定 | `${dbCode.imgField}` — 字段值为图片URL |
|
||||
|
||||
## 2. 图表组件 (chartList)
|
||||
|
||||
详见 `chart-config.md`,这里列出关键结构。
|
||||
|
||||
### JSON 结构
|
||||
|
||||
```python
|
||||
chart_item = {
|
||||
"row": 5,
|
||||
"col": 1,
|
||||
"colspan": 0,
|
||||
"rowspan": 0,
|
||||
"width": "650", # 必须是字符串!
|
||||
"height": "350", # 必须是字符串!
|
||||
"config": json.dumps(echarts_option), # ECharts 配置 JSON 字符串
|
||||
"url": "", # 外部数据 URL(通常为空)
|
||||
"extData": {
|
||||
"chartType": "bar.simple", # 图表类型
|
||||
"dataType": "sql", # 数据来源: "sql"/"api"/"json"/"javabean"/"files"
|
||||
"dataId": "数据集ID", # saveDb 返回的 id
|
||||
"dbCode": "数据集编码", # 数据集编码
|
||||
"axisX": "name", # 固定值
|
||||
"axisY": "value", # 固定值
|
||||
"series": "type", # 固定值
|
||||
"xText": "",
|
||||
"yText": "",
|
||||
"apiStatus": "1"
|
||||
},
|
||||
"layer_id": "chart_xxx",
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"backgroud": {"enabled": False, "color": "#fff", "image": ""},
|
||||
"virtualCellRange": [[5,1],[5,2],...]
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 条形码组件 (barcodeList)
|
||||
|
||||
### JSON 结构
|
||||
|
||||
```python
|
||||
barcode_item = {
|
||||
"row": 3,
|
||||
"col": 0,
|
||||
"colspan": 0,
|
||||
"rowspan": 0,
|
||||
"width": 300,
|
||||
"height": 200,
|
||||
"layer_id": "barcode_xxx",
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"jsonString": json.dumps({
|
||||
"barcodeContent": "jmreport", # 条码内容(支持 ${dbCode.field} 动态绑定)
|
||||
"format": "CODE128", # 条码格式
|
||||
"width": 2, # 条线宽度
|
||||
"height": 100, # 条码高度
|
||||
"displayValue": False, # 是否显示文字
|
||||
"text": "jmreport", # 显示文字内容
|
||||
"fontOptions": "", # 字体选项(bold/italic)
|
||||
"font": "monospace", # 字体
|
||||
"textAlign": "center", # 文字对齐
|
||||
"textPosition": "bottom", # 文字位置
|
||||
"textMargin": 2, # 文字间距
|
||||
"fontSize": 20, # 字体大小
|
||||
"background": "#fff", # 背景色
|
||||
"lineColor": "#000", # 条线颜色
|
||||
"margin": 10 # 边距
|
||||
}),
|
||||
"virtualCellRange": [[3,0],[3,1],[4,0],[4,1]]
|
||||
}
|
||||
```
|
||||
|
||||
### 支持的条码格式
|
||||
|
||||
| format | 说明 |
|
||||
|--------|------|
|
||||
| `CODE128` | Code 128(默认,最常用) |
|
||||
| `CODE39` | Code 39 |
|
||||
| `EAN13` | EAN-13 |
|
||||
| `EAN8` | EAN-8 |
|
||||
| `UPC` | UPC-A |
|
||||
| `ITF14` | ITF-14 |
|
||||
|
||||
### 动态数据绑定
|
||||
|
||||
条码内容支持表达式:`${dbCode.fieldName}`,运行时替换为数据集字段值。
|
||||
|
||||
## 4. 二维码组件 (qrcodeList)
|
||||
|
||||
### JSON 结构
|
||||
|
||||
```python
|
||||
qrcode_item = {
|
||||
"row": 5,
|
||||
"col": 0,
|
||||
"colspan": 0,
|
||||
"rowspan": 0,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"layer_id": "qrcode_xxx",
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"jsonString": json.dumps({
|
||||
"text": "http://jimureport.com/", # 二维码内容(支持 ${dbCode.field})
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"colorDark": "#000000", # 前景色
|
||||
"colorLight": "#ffffff" # 背景色
|
||||
}),
|
||||
"virtualCellRange": [[5,0],[5,1],[6,0],[6,1]]
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 单元格内嵌组件 (displayConfig)
|
||||
|
||||
除了图层组件,单元格本身也可以通过 `display` 属性渲染为特殊组件:
|
||||
|
||||
```python
|
||||
# 在 rows 的 cell 中设置
|
||||
cell = {
|
||||
"text": "#{dbCode.imgUrl}",
|
||||
"display": "img" # 渲染为图片
|
||||
}
|
||||
|
||||
# 或
|
||||
cell = {
|
||||
"text": "#{dbCode.code}",
|
||||
"display": "barcode" # 渲染为条形码
|
||||
}
|
||||
|
||||
# 或
|
||||
cell = {
|
||||
"text": "#{dbCode.url}",
|
||||
"display": "qrcode" # 渲染为二维码
|
||||
}
|
||||
```
|
||||
|
||||
**display 可选值:**
|
||||
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| `normal` | 普通文本(默认) |
|
||||
| `img` | 图片(text 为图片 URL) |
|
||||
| `barcode` | 条形码(text 为条码内容) |
|
||||
| `qrcode` | 二维码(text 为二维码内容) |
|
||||
| `base64Img` | Base64 图片 |
|
||||
| `richText` | 富文本/HTML |
|
||||
|
||||
## Virtual Cell 占位规则
|
||||
|
||||
所有图层组件都需要在 `rows` 中声明 virtual 占位:
|
||||
|
||||
```python
|
||||
# 1. 确定组件占据的行列范围
|
||||
row_start, row_end = 5, 8
|
||||
col_start, col_end = 1, 4
|
||||
layer_id = "chart_xxx"
|
||||
|
||||
# 2. 构造 virtualCellRange
|
||||
virtual_cells = []
|
||||
for r in range(row_start, row_end + 1):
|
||||
for c in range(col_start, col_end + 1):
|
||||
virtual_cells.append([r, c])
|
||||
|
||||
# 3. 在 rows 中添加 virtual 占位
|
||||
for r in range(row_start, row_end + 1):
|
||||
cells = {}
|
||||
for c in range(col_start, col_end + 1):
|
||||
cells[str(c)] = {"text": " ", "virtual": layer_id}
|
||||
rows_data[str(r)] = {"cells": cells}
|
||||
|
||||
# 4. 组件中设置 virtualCellRange
|
||||
component["virtualCellRange"] = virtual_cells
|
||||
```
|
||||
|
||||
**注意事项:**
|
||||
- `virtual` 值必须和组件的 `layer_id` 一致
|
||||
- `text` 必须为 `" "`(一个空格),不能为空
|
||||
- 组件区域不能和数据绑定行重叠
|
||||
- 一个组件的 virtual cells 不能和其他组件重叠
|
||||
|
||||
## Python 构造完整示例
|
||||
|
||||
```python
|
||||
# 构造一个包含 表格 + 柱状图 + 二维码 的报表
|
||||
|
||||
layer_chart_id = "chart_" + gen_id()
|
||||
layer_qr_id = "qrcode_" + gen_id()
|
||||
|
||||
rows_data = {
|
||||
# 标题行
|
||||
"1": {"cells": {"1": {"text": "销售报表", "style": 5}}, "height": 40},
|
||||
# 表头
|
||||
"2": {"cells": {
|
||||
"1": {"text": "产品", "style": 4},
|
||||
"2": {"text": "销量", "style": 4},
|
||||
"3": {"text": "金额", "style": 4}
|
||||
}, "height": 34},
|
||||
# 数据行
|
||||
"3": {"cells": {
|
||||
"1": {"text": "#{ds.name}", "style": 2},
|
||||
"2": {"text": "#{ds.qty}", "style": 2},
|
||||
"3": {"text": "#{ds.amount}", "style": 2}
|
||||
}},
|
||||
# 空行
|
||||
"4": {"cells": {}, "height": 15},
|
||||
"len": 200
|
||||
}
|
||||
|
||||
# 柱状图占位 (row 5-12, col 1-5)
|
||||
for r in range(5, 13):
|
||||
cells = {}
|
||||
for c in range(1, 6):
|
||||
cells[str(c)] = {"text": " ", "virtual": layer_chart_id}
|
||||
rows_data[str(r)] = {"cells": cells}
|
||||
|
||||
# 二维码占位 (row 5-8, col 6-7)
|
||||
for r in range(5, 9):
|
||||
cells = rows_data.get(str(r), {"cells": {}})["cells"]
|
||||
for c in range(6, 8):
|
||||
cells[str(c)] = {"text": " ", "virtual": layer_qr_id}
|
||||
rows_data[str(r)] = {"cells": cells}
|
||||
|
||||
# 柱状图
|
||||
chart_config = {
|
||||
"title": {"text": "销量统计", "left": "center"},
|
||||
"tooltip": {"trigger": "axis"},
|
||||
"xAxis": [{"type": "category", "data": []}],
|
||||
"yAxis": [{"type": "value"}],
|
||||
"series": [{"type": "bar", "data": [], "itemStyle": {"color": "#01b0f1"}}]
|
||||
}
|
||||
|
||||
chart_list = [{
|
||||
"row": 5, "col": 1, "colspan": 0, "rowspan": 0,
|
||||
"width": "500", "height": "300",
|
||||
"config": json.dumps(chart_config, ensure_ascii=False),
|
||||
"url": "",
|
||||
"extData": {
|
||||
"chartType": "bar.simple", "dataType": "sql",
|
||||
"dataId": chart_db_id, "dbCode": "saleschart",
|
||||
"axisX": "name", "axisY": "value", "series": "type",
|
||||
"xText": "", "yText": "", "apiStatus": "1"
|
||||
},
|
||||
"layer_id": layer_chart_id,
|
||||
"offsetX": 0, "offsetY": 0,
|
||||
"backgroud": {"enabled": False, "color": "#fff", "image": ""},
|
||||
"virtualCellRange": [[r,c] for r in range(5,13) for c in range(1,6)]
|
||||
}]
|
||||
|
||||
# 二维码
|
||||
qrcode_list = [{
|
||||
"row": 5, "col": 6, "colspan": 0, "rowspan": 0,
|
||||
"width": 128, "height": 128,
|
||||
"layer_id": layer_qr_id,
|
||||
"offsetX": 0, "offsetY": 0,
|
||||
"jsonString": json.dumps({"text": "https://example.com", "width": 128, "height": 128, "colorDark": "#000000", "colorLight": "#ffffff"}),
|
||||
"virtualCellRange": [[r,c] for r in range(5,9) for c in range(6,8)]
|
||||
}]
|
||||
|
||||
# 最终保存数据中包含
|
||||
save_data = {
|
||||
# ... 其他字段
|
||||
"chartList": chart_list,
|
||||
"qrcodeList": qrcode_list,
|
||||
# imgList 和 barcodeList 为空时可省略或传 []
|
||||
}
|
||||
```
|
||||
170
.trae/skills/jimureport/references/constraints.md
Normal file
170
.trae/skills/jimureport/references/constraints.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# 积木报表重要约束
|
||||
|
||||
1. **数据集编码 `db_code` 不能重复且只支持英文字符** — 同一个报表内的多个数据集,每个的 `db_code` 必须唯一。编码只能使用英文字母、数字和下划线,不能包含中文或特殊字符。重复会导致数据覆盖或查询异常。
|
||||
2. **`is_page` 分页只能有一个** — 一个报表中只能有一个数据集设置 `is_page=1`(启用分页),其余数据集必须为 `is_page=0`。多个数据集同时分页会导致分页冲突。
|
||||
|
||||
## 数据绑定语法
|
||||
|
||||
| 语法 | 说明 | 场景 |
|
||||
|------|------|------|
|
||||
| `${db.field}` | 单值绑定 | 主表字段、固定值 |
|
||||
| `#{db.field}` | 列表绑定 | 明细行、循环数据 |
|
||||
| `#{db.group(field)}` | 纵向分组 | 按字段分组汇总 |
|
||||
| `#{db.groupRight(field)}` | 横向分组 | 按字段横向展开 |
|
||||
| `#{db.dynamic(field)}` | 动态聚合 | 交叉表数据 |
|
||||
| `#{db.customGroup(field)}` | 自定义分组 | 横向自定义展开 |
|
||||
| `=SUM(D7)` | Excel 公式 | 列汇总 |
|
||||
|
||||
## 单元格属性
|
||||
|
||||
| 属性 | 说明 | 值 |
|
||||
|------|------|-----|
|
||||
| `merge` | 合并 | `[行数,列数]`,如 `[0,2]` 向右合并2列 |
|
||||
| `style` | 样式索引 | 引用 `styles` 数组下标 |
|
||||
| `loopBlock` | 循环块标记 | 1=属于循环块 |
|
||||
| `zonedEdition` | 分版标记 | 1/2/... 分版编号 |
|
||||
| `fixedHead` | 固定表头 | 1=固定 |
|
||||
| `fixedTail` | 固定表尾 | 1=固定 |
|
||||
| `aggregate` | 聚合类型 | 见下方分组配置 |
|
||||
| `subtotal` | 小计配置 | 见下方分组配置 |
|
||||
| `funcname` | 聚合函数 | 见下方分组配置 |
|
||||
| `subtotalText` | 小计行文本 | `"合计"` / `"小计"` |
|
||||
| `direction` | 展开方向 | 见下方分组配置 |
|
||||
| `sort` | 排序 | 见下方分组配置 |
|
||||
| `rendered` | 渲染标记 | `""` |
|
||||
| `config` | 配置标记 | `""` |
|
||||
| `decimalPlaces` | 小数位 | `"0"`/`"1"`/`"4"` |
|
||||
| `display` | 显示格式 | 见下方 display 值表 |
|
||||
| `fillForm` | 填报组件 | 组件配置对象 |
|
||||
|
||||
## 分组相关配置
|
||||
|
||||
### aggregate 聚合方式(polyWayList)
|
||||
|
||||
| 值 | 说明 |
|
||||
|-----|------|
|
||||
| `select` | 列表(普通列,不分组) |
|
||||
| `group` | 分组(相同值合并单元格) |
|
||||
|
||||
### subtotal 是否启用小计
|
||||
|
||||
| 值 | 说明 |
|
||||
|-----|------|
|
||||
| `"-1"` | 否(不显示小计行) |
|
||||
| `"groupField"` | 是(分组切换时显示小计/合计行) |
|
||||
|
||||
### funcname 聚合函数(aggregateList)
|
||||
|
||||
| 值 | 说明 | 用于 |
|
||||
|-----|------|------|
|
||||
| `"-1"` | 无(不计算,仅显示 subtotalText 文本) | 分组字段(地区、销售员等) |
|
||||
| `"SUM"` | 求和 | 数值字段(金额、数量等) |
|
||||
| `"MAX"` | 最大值 | 数值字段 |
|
||||
| `"MIN"` | 最小值 | 数值字段 |
|
||||
| `"AVERAGE"` | 平均值 | 数值字段 |
|
||||
| `"COUNT"` | 计数 | 任意字段 |
|
||||
|
||||
### direction 展开方向(directionList)
|
||||
|
||||
| 值 | 说明 |
|
||||
|-----|------|
|
||||
| `"down"` | 纵向(默认) |
|
||||
| `"right"` | 横向 |
|
||||
|
||||
### sort 排序(sortType)
|
||||
|
||||
| 值 | 说明 |
|
||||
|-----|------|
|
||||
| `"default"` | 默认(不排序) |
|
||||
| `"asc"` | 正序 |
|
||||
| `"desc"` | 倒序 |
|
||||
|
||||
### aggregate 高级模式(advancedList)
|
||||
|
||||
| 值 | 说明 |
|
||||
|-----|------|
|
||||
| `"default"` | 普通属性 |
|
||||
| `"dynamic"` | 动态属性(交叉表) |
|
||||
|
||||
### 分组单元格配置示例
|
||||
|
||||
**分组字段(地区,一级分组 — 合计):**
|
||||
```json
|
||||
{
|
||||
"text": "#{sales.group(region)}",
|
||||
"style": 4,
|
||||
"aggregate": "group",
|
||||
"subtotal": "groupField",
|
||||
"funcname": "-1",
|
||||
"subtotalText": "合计",
|
||||
"rendered": "",
|
||||
"config": ""
|
||||
}
|
||||
```
|
||||
|
||||
**分组字段(销售员,二级分组 — 小计):**
|
||||
```json
|
||||
{
|
||||
"text": "#{sales.group(salesman)}",
|
||||
"style": 4,
|
||||
"aggregate": "group",
|
||||
"subtotal": "groupField",
|
||||
"funcname": "-1",
|
||||
"subtotalText": "小计"
|
||||
}
|
||||
```
|
||||
|
||||
**数值字段(销售额,小计/合计行自动求和):**
|
||||
```json
|
||||
{
|
||||
"text": "#{sales.amount}",
|
||||
"style": 4,
|
||||
"funcname": "SUM",
|
||||
"subtotal": "-1",
|
||||
"display": "number",
|
||||
"rendered": "",
|
||||
"config": ""
|
||||
}
|
||||
```
|
||||
|
||||
**注意:** 数值字段的 `subtotal` 为 `"-1"`(不是 `"groupField"`),`funcname` 为 `"SUM"`。含义是:该字段不触发分组切换,但在分组切换产生的小计/合计行中自动按 SUM 聚合。
|
||||
|
||||
## display 显示格式(JmConst.CELL_FORMAT_*)
|
||||
|
||||
| display 值 | 说明 | 示例 |
|
||||
|------------|------|------|
|
||||
| `normal` | 默认文本 | — |
|
||||
| `number` | 数值(数值类型字段默认) | 58000 |
|
||||
| `percent` | 百分比 | 85% |
|
||||
| `rmb` | 人民币 | ¥58,000.00 |
|
||||
| `usd` | 美元 | $58,000.00 |
|
||||
| `eur` | 欧元 | €58,000.00 |
|
||||
| `date` | 日期 | 2026-03-20 |
|
||||
| `date2` | 日期(斜杠) | 2026/03/20 |
|
||||
| `time` | 时间 | 12:30:00 |
|
||||
| `datetime` | 日期时间 | 2026-03-20 12:30:00 |
|
||||
| `year` | 年 | 2026 |
|
||||
| `month` | 月 | 03 |
|
||||
| `base64Img` | Base64图片 | — |
|
||||
| `img` | 图片 | — |
|
||||
| `qrcode` | 二维码 | — |
|
||||
| `barcode` | 条形码 | — |
|
||||
| `richText` | 富文本 | — |
|
||||
|
||||
## 顶层配置
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `loopBlockList` | 循环块定义(含 `loopTime` 分栏次数) |
|
||||
| `zonedEditionList` | 分版区域定义 |
|
||||
| `fixedPrintHeadRows` | 固定打印表头 |
|
||||
| `fixedPrintTailRows` | 固定打印表尾 |
|
||||
| `groupField` | 分组字段 |
|
||||
| `isGroup` | 是否启用分组 |
|
||||
| `submitHandlers` | 填报提交处理器 |
|
||||
| `background` | 背景图配置 |
|
||||
| `imgList` | 图片列表 |
|
||||
| `displayConfig` | 二维码/条码显示配置 |
|
||||
| `dicts` | 引用的字典编码列表 |
|
||||
| `printConfig` | 打印配置(纸张/方向/边距) |
|
||||
| `merges` | 合并单元格列表(如 `"B1:H1"`) |
|
||||
377
.trae/skills/jimureport/references/dataset-skills.md
Normal file
377
.trae/skills/jimureport/references/dataset-skills.md
Normal file
@@ -0,0 +1,377 @@
|
||||
# 数据集的用法
|
||||
|
||||
所有接口均需要提供token
|
||||
|
||||
## 数据源管理
|
||||
|
||||
数据集默认使用应用本身的数据库。如需连接外部数据库,需要先添加数据源,再在数据集中通过 `dbSource` 关联。
|
||||
|
||||
### 查询已有数据源
|
||||
|
||||
- **地址**:`GET /jmreport/getDataSourceByPage`
|
||||
- **返回**:所有数据源列表,每个包含 `id` 和 `name`
|
||||
|
||||
```python
|
||||
ds_resp = api_request('/jmreport/getDataSourceByPage')
|
||||
ds_list = ds_resp.get('result', [])
|
||||
# 按名称查找数据源 ID
|
||||
ds_map = {ds['name']: ds['id'] for ds in ds_list}
|
||||
# 示例: ds_map['ws_mysql'] -> '1010703895600087040'
|
||||
```
|
||||
|
||||
### 添加/编辑数据源
|
||||
|
||||
- **地址**:`POST /jmreport/addDataSource`
|
||||
- **新增不传 id,编辑传 id**
|
||||
- **请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "",
|
||||
"reportId": "报表ID",
|
||||
"code": "",
|
||||
"name": "mysql",
|
||||
"dbType": "MYSQL5.7",
|
||||
"dbDriver": "com.mysql.cj.jdbc.Driver",
|
||||
"dbUrl": "jdbc:mysql://127.0.0.1:3306/jimureport?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&tinyInt1isBit=false",
|
||||
"dbUsername": "root",
|
||||
"dbPassword": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `id` | 数据源ID,新增不传,编辑传已有ID |
|
||||
| `reportId` | 关联的报表ID |
|
||||
| `name` | 数据源名称 |
|
||||
| `dbType` | 数据库类型(如 `MYSQL5.7`、`ORACLE`、`POSTGRESQL` 等) |
|
||||
| `dbDriver` | JDBC驱动类名 |
|
||||
| `dbUrl` | JDBC连接URL |
|
||||
| `dbUsername` | 数据库用户名 |
|
||||
| `dbPassword` | 数据库密码 |
|
||||
|
||||
### 数据源与数据集的关联
|
||||
|
||||
数据集通过 `dbSource` 字段关联数据源,值为数据源的 `id`:
|
||||
|
||||
```json
|
||||
{
|
||||
"dbCode": "aa",
|
||||
"dbType": "0",
|
||||
"dbDynSql": "select name from demo",
|
||||
"dbSource": "1010703895600087040",
|
||||
"dbSourceType": "mysql"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `dbSource` | 数据源ID(`addDataSource` 返回或编辑时传入的 `id`)。空字符串 `""` = 使用默认数据源 |
|
||||
| `dbSourceType` | 数据库类型(`mysql`、`oracle` 等),后端会自动识别,也可手动指定 |
|
||||
|
||||
**注意:** 如果开启了数据源安全模式(`firewall.dataSourceSafe: true`),SQL 数据集**必须**指定 `dbSource`,不允许使用默认数据源。
|
||||
|
||||
## SQL数据集
|
||||
|
||||
### SQL语句用法
|
||||
|
||||
- 如果id字段为字符串类型则需要加单引号:`select * from table where id='${id}'`
|
||||
- 您可以编写`${id}`做为一个参数,这里id是参数的名称。例如:`select * from table where id='${id}'`
|
||||
- 您可以编写`#{sysUserCode}`做为一个系统变量,这里sysUserCode是当前登录人。例如:`select * from table where create_by='#{sysUserCode}'`
|
||||
- 您可以编写存储过程`CALL proc_sys_role(${pageNo}, ${pageSize})`,CALL为开启存储过程
|
||||
- MongoDB和Elasticsearch支持sql语句,表名需要增加数据库标识(MongoDB:mongo,Elasticsearch:es)。例如:`select * from mongo.table`
|
||||
|
||||
### SQL解析接口
|
||||
|
||||
- **地址**:`POST /jmreport/queryFieldBySql`
|
||||
- **请求参数**:
|
||||
```json
|
||||
{
|
||||
"sql": "select * from test_order_product where order_fk_id = ${order_fk_id}",
|
||||
"type": "0",
|
||||
"paramArray": "[{\"id\":\"1047395275108601856\",\"jimuReportHeadId\":\"1047395274039054336\",\"paramName\":\"order_fk_id\",\"paramTxt\":\"order_fk_id\",\"paramValue\":\"1\",\"orderNum\":1,\"createBy\":\"admin\",\"createTime\":\"2025-02-06 16:21:43\",\"updateBy\":null,\"updateTime\":null,\"searchFlag\":0,\"widgetType\":null,\"searchMode\":null,\"dictCode\":null,\"searchFormat\":null,\"extJson\":\"\",\"tableIndex\":1,\"_index\":0,\"_rowKey\":105}]"
|
||||
}
|
||||
```
|
||||
- **返回结果**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "解析成功",
|
||||
"code": 200,
|
||||
"result": {
|
||||
"paramList": [],
|
||||
"fieldList": [
|
||||
{
|
||||
"fieldName": "id",
|
||||
"fieldText": "id",
|
||||
"widgetType": "String",
|
||||
"orderNum": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API数据集
|
||||
|
||||
### API数据集用法
|
||||
|
||||
- 如果id字段为字符串类型则需要加单引号:`http://127.0.0.1:8080/jeecg-boot/jimureport/test?id=${id}`
|
||||
- 您可以编写`#{sysDateTime}`做为一个系统变量,这里sysDateTime是当前系统时间。例如:`http://127.0.0.1:8080/jeecg-boot/jimureport/test?riqi=#{sysDateTime}`
|
||||
- 您可以简写访问路径,如:`#{domainURL}/jimureport/test/getList`
|
||||
|
||||
### API解析接口
|
||||
|
||||
- **地址**:`POST /jmreport/executeSelectApi`
|
||||
- **请求参数**:
|
||||
```json
|
||||
{
|
||||
"api": "http://localhost:8085/jimureport/test/getList?pid=&name=",
|
||||
"method": "0",
|
||||
"paramArray": "[{\"id\":\"1066517440772788224\",\"jimuReportHeadId\":\"1066517440441438208\",\"paramName\":\"pid\",\"paramTxt\":null,\"paramValue\":\"\",\"orderNum\":1,\"createBy\":\"admin\",\"createTime\":\"2025-03-31 10:46:23\",\"updateBy\":null,\"updateTime\":null,\"searchFlag\":0,\"widgetType\":null,\"searchMode\":null,\"dictCode\":\"\",\"searchFormat\":null,\"extJson\":\"\",\"tableIndex\":1,\"_index\":0,\"_rowKey\":231}]"
|
||||
}
|
||||
```
|
||||
- **返回结果**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "",
|
||||
"code": 200,
|
||||
"result": [
|
||||
{
|
||||
"fieldName": "ctotal",
|
||||
"fieldText": "ctotal",
|
||||
"widgetType": "String",
|
||||
"isShow": true,
|
||||
"orderNum": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## JSON数据集
|
||||
|
||||
自动将data中的字段解析成fieldName/fieldText/widgetType格式。
|
||||
|
||||
- **输入格式**:
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"ctotal": "125箱",
|
||||
"cname": "牛奶0",
|
||||
"cprice": "56",
|
||||
"riqi": "2022年10月21日",
|
||||
"id": "1",
|
||||
"dtotal": "1256箱",
|
||||
"tp": "7000",
|
||||
"ztotal": "589箱",
|
||||
"cnum": "每箱12瓶"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## JavaBean数据集
|
||||
|
||||
- **地址**:`POST /jmreport/queryFieldByBean`
|
||||
- **请求参数**:
|
||||
```json
|
||||
{
|
||||
"javaType": "spring-key",
|
||||
"javaValue": "testRpSpringBean",
|
||||
"isPage": false,
|
||||
"param": {}
|
||||
}
|
||||
```
|
||||
- **返回结果**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "解析成功",
|
||||
"code": 200,
|
||||
"result": [
|
||||
{
|
||||
"fieldName": "name",
|
||||
"fieldText": "name",
|
||||
"widgetType": "String",
|
||||
"orderNum": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 查询已有数据集
|
||||
|
||||
更新数据集前必须先查到已有数据集的 `dbId`,流程如下:
|
||||
|
||||
### Step 1: 获取数据集列表(含 dbId)
|
||||
|
||||
- **地址**:`GET /jmreport/field/tree/{reportId}`
|
||||
- **返回结构**:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": [
|
||||
[
|
||||
{
|
||||
"code": "userlist",
|
||||
"dbId": "1194900477760331776",
|
||||
"title": "用户列表",
|
||||
"type": "0",
|
||||
"isList": "1",
|
||||
"izSharedSource": 0,
|
||||
"children": [
|
||||
{"title": "username", "fieldText": "username"},
|
||||
{"title": "realname", "fieldText": "realname"}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**提取 dbCode → dbId 映射:**
|
||||
```python
|
||||
tree = api_request(f'/jmreport/field/tree/{report_id}')
|
||||
db_map = {} # dbCode -> dbId
|
||||
for group in tree.get('result', []):
|
||||
if group and len(group) > 0:
|
||||
info = group[0]
|
||||
db_map[info['code']] = info['dbId']
|
||||
```
|
||||
|
||||
### Step 2: 获取单个数据集详情
|
||||
|
||||
- **地址**:`GET /jmreport/loadDbData/{dbId}?reportId={reportId}`
|
||||
- **返回结构**:`result` 包含三个顶层字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"dbId": "数据集ID",
|
||||
"reportDb": {
|
||||
"id": "数据集ID",
|
||||
"dbCode": "userlist",
|
||||
"dbDynSql": "SELECT ... FROM ...",
|
||||
"dbSource": "1010703895600087040",
|
||||
"dbSourceType": "mysql",
|
||||
"isPage": "1",
|
||||
"isList": "1"
|
||||
},
|
||||
"fieldList": [...],
|
||||
"paramList": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **注意:`dbDynSql` 和 `dbSource` 在 `result.reportDb` 中,不在 `result` 顶层。**
|
||||
|
||||
```python
|
||||
detail = api_request(f'/jmreport/loadDbData/{db_id}?reportId={report_id}').get('result', {})
|
||||
report_db = detail.get('reportDb', {})
|
||||
existing_sql = report_db.get('dbDynSql', '')
|
||||
existing_db_source = report_db.get('dbSource', '')
|
||||
existing_fields = detail.get('fieldList', [])
|
||||
existing_params = detail.get('paramList', [])
|
||||
```
|
||||
|
||||
### Step 3: 查询参数列表
|
||||
|
||||
- **地址**:`GET /jmreport/getListReportDb?reportId={reportId}`
|
||||
- **返回**:每个 dbCode 对应的参数列表
|
||||
|
||||
```json
|
||||
{"result": {"reportDbParam": {"userlist": [{"paramName": "username", ...}], "userpie": []}}}
|
||||
```
|
||||
|
||||
## 保存或修改数据集
|
||||
|
||||
**新增不传 id,更新必须传 id。** 后端 `saveOrUpdate` 逻辑:有 id 则更新,无 id 则新增。
|
||||
|
||||
- **地址**:`POST /jmreport/saveDb`
|
||||
- **请求参数**:
|
||||
```json
|
||||
{
|
||||
"id": "1193767090018410496",
|
||||
"izSharedSource": 0,
|
||||
"jimuReportId": "1193766682428530688",
|
||||
"dbCode": "aa",
|
||||
"dbChName": "aa",
|
||||
"dbType": "0",
|
||||
"dbSource": "",
|
||||
"jsonData": "",
|
||||
"apiConvert": "",
|
||||
"jimuSharedSourceId": null,
|
||||
"isList": "1",
|
||||
"isPage": "1",
|
||||
"dbDynSql": "select * from demo",
|
||||
"fieldList": [
|
||||
{
|
||||
"id": "1193767090198765568",
|
||||
"jimuReportDbId": "1193767090018410496",
|
||||
"fieldName": "id",
|
||||
"fieldText": "id",
|
||||
"widgetType": "String",
|
||||
"widgetWidth": null,
|
||||
"orderNum": 0,
|
||||
"searchFlag": null,
|
||||
"searchMode": null,
|
||||
"searchValue": null,
|
||||
"dictCode": "",
|
||||
"createBy": "admin",
|
||||
"createTime": "2026-03-17 14:11:04",
|
||||
"updateBy": null,
|
||||
"updateTime": null,
|
||||
"searchFormat": null,
|
||||
"extJson": "",
|
||||
"fieldNamePhysics": null,
|
||||
"tableIndex": 1,
|
||||
"_index": 0,
|
||||
"_rowKey": 44
|
||||
}
|
||||
],
|
||||
"paramList": []
|
||||
}
|
||||
```
|
||||
- **返回结果**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "",
|
||||
"code": 200,
|
||||
"result": {
|
||||
"id": "1193767090018410496",
|
||||
"jimuReportId": "1193766682428530688",
|
||||
"dbCode": "aa",
|
||||
"dbChName": "aa",
|
||||
"dbType": "0",
|
||||
"dbDynSql": "select * from demo",
|
||||
"fieldList": [],
|
||||
"paramList": [],
|
||||
"isPage": "1",
|
||||
"isList": "1",
|
||||
"dbSource": "",
|
||||
"dbSourceType": "mysql",
|
||||
"createBy": "admin",
|
||||
"updateBy": "admin",
|
||||
"createTime": "2026-03-19 17:12:34",
|
||||
"updateTime": "2026-03-19 17:12:34",
|
||||
"apiConvert": "",
|
||||
"izSharedSource": 0,
|
||||
"jimuSharedSourceId": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### dbType 值说明
|
||||
|
||||
| dbType | 类型 | 关键字段 |
|
||||
|--------|------|----------|
|
||||
| `"0"` | SQL数据集 | `dbDynSql` |
|
||||
| `"1"` | API数据集 | `apiUrl` + `apiMethod` |
|
||||
| `"2"` | JavaBean数据集 | `javaType` + `javaValue` |
|
||||
| `"3"` | JSON数据集 | `jsonData` |
|
||||
| `"4"` | 共享数据集 | — |
|
||||
| `"5"` | 多文件数据集 | — |
|
||||
| `"6"` | 单文件数据集 | — |
|
||||
369
.trae/skills/jimureport/references/query-config.md
Normal file
369
.trae/skills/jimureport/references/query-config.md
Normal file
@@ -0,0 +1,369 @@
|
||||
# 报表查询配置完整指南
|
||||
|
||||
## 1. 报表参数配置
|
||||
|
||||
### 参数语法
|
||||
|
||||
| 语法 | 类型 | 说明 | 示例 |
|
||||
|------|------|------|------|
|
||||
| `${paramName}` | 用户参数 | 需要在报表参数中声明,用户可查询输入 | `${id}` `${name}` |
|
||||
| `#{sysVar}` | 系统变量 | 无需声明,自动解析 | `#{sysUserCode}` `#{sysDate}` |
|
||||
|
||||
**注意:** `$` 或 `#` 与 `{` 之间不能有空格。
|
||||
|
||||
### SQL 参数示例
|
||||
|
||||
```sql
|
||||
select * from sys_user where id='${id}' and sex='${sex}' and create_by='#{sysUserCode}'
|
||||
```
|
||||
|
||||
### API 参数示例
|
||||
|
||||
```
|
||||
http://192.168.1.116/jmreport/test/getMessage?name='${name}'&createBy='#{sysUserCode}'
|
||||
```
|
||||
|
||||
### 系统变量列表
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `#{sysUserCode}` | 当前登录用户名 |
|
||||
| `#{sysDate}` | 当前系统日期 |
|
||||
| `#{sysDateTime}` | 当前系统日期时间 |
|
||||
| `#{domainURL}` | 系统域名地址 |
|
||||
|
||||
### 参数优先级(高→低)
|
||||
|
||||
1. **查询条件值**(用户在查询栏输入的)
|
||||
2. **URL参数**(通过URL传递的)
|
||||
3. **默认值**(配置的默认值)
|
||||
|
||||
### 参数合并规则
|
||||
|
||||
- 多个数据集中**同名参数**会合并为一个查询控件
|
||||
- 同名**数据集字段**不会合并
|
||||
- URL参数会传递给所有匹配的数据集参数
|
||||
|
||||
## 2. 查询控件类型
|
||||
|
||||
在数据集字段详情中勾选"查询"复选框,即可生成查询控件。
|
||||
|
||||
| 控件类型 | 查询模式值 | 说明 |
|
||||
|---------|-----------|------|
|
||||
| 文本输入 | 空或"输入框" | 默认类型 |
|
||||
| 下拉单选 | "下拉单选" | 可搜索,默认显示10条 |
|
||||
| 下拉多选 | "下拉多选" | 可搜索,默认显示10条 |
|
||||
| 范围查询 | "范围查询" | 日期/数值范围;**报表参数不支持** |
|
||||
| 模糊查询 | "模糊查询" | **报表参数不支持** |
|
||||
| 下拉树 | 通过配置实现 | 层级树形结构 |
|
||||
| 自定义下拉 | JS增强实现 | 数据需含 `value` 和 `text` 字段 |
|
||||
|
||||
### 下拉数据源配置
|
||||
|
||||
**方式一:系统字典**
|
||||
- 配置字典编码(如 `sex`)
|
||||
|
||||
**方式二:SQL字典**
|
||||
```sql
|
||||
SELECT username AS value, realname AS text FROM sys_user
|
||||
```
|
||||
必须别名为 `value` 和 `text`。
|
||||
|
||||
**方式三:API**
|
||||
- 相对路径:`/jmreport/test/getDictSex?createBy=#{sysUserCode}`
|
||||
- 绝对路径:`http://127.0.0.1:8080/jeecg-boot/jmreport/test/getDictSex`
|
||||
- 返回格式:`[{"text":"男","value":"1"},{"text":"女","value":"2"}]`
|
||||
|
||||
**方式四:JS增强**
|
||||
```javascript
|
||||
this.updateSelectOptions('dbCode', 'fieldName', options)
|
||||
```
|
||||
|
||||
### 下拉显示条数配置
|
||||
|
||||
在字段的配置设置中:
|
||||
```json
|
||||
{"selectSearchPageSize": 20}
|
||||
```
|
||||
默认显示10条。
|
||||
|
||||
## 3. 查询控件默认值
|
||||
|
||||
三种方式:
|
||||
|
||||
| 方式 | 示例 |
|
||||
|------|------|
|
||||
| 静态值 | 直接输入字符串 |
|
||||
| 动态表达式 | `=dateStr('yyyy-MM-dd')` |
|
||||
| 系统变量 | `#{sysUserCode}` |
|
||||
|
||||
## 4. 时间控件
|
||||
|
||||
### 支持的日期格式
|
||||
|
||||
| 格式 | 示例 |
|
||||
|------|------|
|
||||
| `yyyy-MM-dd HH:mm:ss` | 2021-07-29 12:11:10 |
|
||||
| `yyyy-MM-dd` | 2021-07-29 |
|
||||
| `yyyy-MM` | 2021-07 |
|
||||
| `yyyy` | 2021 |
|
||||
| `MM` | 07 |
|
||||
| `HH:mm:ss` | 12:11:10 |
|
||||
| `HH:mm` | 12:11 |
|
||||
|
||||
**重要:** 日期控件传递的值始终为字符串类型。
|
||||
|
||||
### 数据库日期转换
|
||||
|
||||
不同数据库需要用对应的日期转换函数作为查询条件字段:
|
||||
|
||||
| 数据库 | 转换函数 | 示例 |
|
||||
|--------|---------|------|
|
||||
| MySQL | `DATE_FORMAT(field, '%Y')` | `DATE_FORMAT(birthday, '%Y') nian` |
|
||||
| Oracle | `to_char(field, 'yyyy')` | `to_char(birthday, 'yyyy') nian` |
|
||||
| SQL Server | `year(field)` | `year(birthday) nian` |
|
||||
|
||||
**SQL示例(MySQL):**
|
||||
```sql
|
||||
SELECT name, birthday, DATE_FORMAT(birthday, '%Y') nian FROM demo
|
||||
```
|
||||
将转换后的列 `nian` 配置为查询条件,而非原始日期字段。
|
||||
|
||||
### 时间默认值函数
|
||||
|
||||
#### dateStr(date, format, offset)(v1.3.79+)
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| date | 时间字符串(可选,默认当前时间) |
|
||||
| format | 格式化模式(默认 `yyyy-MM-dd HH:mm:ss`) |
|
||||
| offset | 数值偏移量 |
|
||||
|
||||
**示例(当前时间 2020-08-11 12:00:01):**
|
||||
|
||||
| 表达式 | 结果 |
|
||||
|--------|------|
|
||||
| `=dateStr()` | 2020-08-11 12:00:01 |
|
||||
| `=dateStr('yyyy-MM-dd')` | 2020-08-11 |
|
||||
| `=dateStr('MM', 2)` | 10 |
|
||||
| `=dateStr('dd', -1)` | 10 |
|
||||
| `=dateStr('2020-08-15 12:00:01', 'yyyy-MM-dd', 1)` | 2020-08-16 |
|
||||
| `=dateStr('yyyy-MM', -1)` | 2020-07(v1.4.0+) |
|
||||
|
||||
#### date2Str(date, format, offset)(v1.7.5+)
|
||||
|
||||
与 `dateStr()` 相同但保留前导零(如 `01` 而非 `1`)。
|
||||
|
||||
## 5. SQL条件表达式(FreeMarker语法)
|
||||
|
||||
v1.3.79+ 支持动态SQL条件,使用 FreeMarker 模板语法。
|
||||
|
||||
### isNotEmpty() 函数
|
||||
|
||||
对 `null` 和空字符串 `""` 都返回 `false`。
|
||||
|
||||
### 基础示例
|
||||
|
||||
```sql
|
||||
select id, name, age from demo where create_by = '#{sysUserCode}'
|
||||
<#if isNotEmpty(age)> and age = '${age}'</#if>
|
||||
<#if isNotEmpty(name)> and name = '${name}'</#if>
|
||||
```
|
||||
|
||||
### LIKE模糊查询
|
||||
|
||||
```sql
|
||||
select * from demo where 1=1
|
||||
<#if name?? && name?length gt 0>
|
||||
and name like concat('%', '${name}', '%')
|
||||
</#if>
|
||||
```
|
||||
|
||||
### 多数据集共享参数
|
||||
|
||||
```sql
|
||||
-- 数据集1
|
||||
select username, sex, phone, create_time from user where 1=1
|
||||
<#if isNotEmpty(begin_date)>
|
||||
and DATE_FORMAT(create_time, '%Y-%m-%d') >= '${begin_date}'
|
||||
</#if>
|
||||
<#if isNotEmpty(end_date)>
|
||||
and DATE_FORMAT(create_time, '%Y-%m-%d') <= '${end_date}'
|
||||
</#if>
|
||||
|
||||
-- 数据集2(共享 begin_date 和 end_date 参数)
|
||||
select count(1) as value, DATE_FORMAT(create_time, '%Y-%m-%d') as name
|
||||
from user where 1=1
|
||||
<#if isNotEmpty(begin_date)>
|
||||
and DATE_FORMAT(create_time, '%Y-%m-%d') >= '${begin_date}'
|
||||
</#if>
|
||||
<#if isNotEmpty(end_date)>
|
||||
and DATE_FORMAT(create_time, '%Y-%m-%d') <= '${end_date}'
|
||||
</#if>
|
||||
GROUP BY name
|
||||
```
|
||||
|
||||
## 6. SQL表达式函数(DaoFormat)
|
||||
|
||||
v1.6.2+ 支持在SQL中使用 `DaoFormat` 函数。
|
||||
|
||||
### DaoFormat.in() — 字符串IN查询
|
||||
|
||||
输入:`male,female` → 输出:`'male','female'`
|
||||
|
||||
```sql
|
||||
select * from demo where sex in(${DaoFormat.in('${sex}')})
|
||||
```
|
||||
|
||||
### DaoFormat.inNumber() — 数字IN查询
|
||||
|
||||
输入:`21,22` → 输出:`21,22`
|
||||
|
||||
```sql
|
||||
select * from demo where age in(${DaoFormat.inNumber('${age}')})
|
||||
```
|
||||
|
||||
### DaoFormat.concat() — 字符串拼接
|
||||
|
||||
```sql
|
||||
select * from demo where create_time between
|
||||
'${DaoFormat.concat('${beginTime}', ' 00:00:00')}'
|
||||
and '${DaoFormat.concat('${endTime}', ' 23:59:59')}'
|
||||
```
|
||||
|
||||
## 7. 下拉树控件
|
||||
|
||||
v1.3.79+ 支持层级树形下拉。
|
||||
|
||||
### 配置格式
|
||||
|
||||
```json
|
||||
{"loadTree": "{{ domainURL }}/sys/user/treeTest"}
|
||||
```
|
||||
或绝对路径:
|
||||
```json
|
||||
{"loadTree": "https://api.jeecg.com/mock/26/queryTree"}
|
||||
```
|
||||
|
||||
### 接口返回格式
|
||||
|
||||
```json
|
||||
[
|
||||
{"id": "001", "pid": "", "value": "A01", "title": "节点1", "izLeaf": 0},
|
||||
{"id": "002", "pid": "001", "value": "A02", "title": "子节点1", "izLeaf": 1}
|
||||
]
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `id` | 节点标识 |
|
||||
| `pid` | 父节点ID,空=根节点 |
|
||||
| `value` | 实际查询值 |
|
||||
| `title` | 显示文本 |
|
||||
| `izLeaf` | 1=叶子节点(无展开图标),0=父节点 |
|
||||
|
||||
### 穿透场景(v1.5.0+)
|
||||
|
||||
```json
|
||||
{
|
||||
"loadTree": ".../treeTest",
|
||||
"loadTreeByValue": ".../loadTreeByValue"
|
||||
}
|
||||
```
|
||||
|
||||
**限制:** 下拉树不支持默认值配置。
|
||||
|
||||
## 8. 范围查询默认值
|
||||
|
||||
使用管道符 `|` 分隔起止值。
|
||||
|
||||
| 场景 | 默认值表达式 |
|
||||
|------|------------|
|
||||
| 数字范围 | `16\|22` |
|
||||
| 固定日期 | `2021-11-01\|2021-11-30` |
|
||||
| 本月1日到今天 | `=concat(string.substring(dateStr('yyyy-MM-dd'),0,8),'01')\|=dateStr('yyyy-MM-dd')` |
|
||||
| 最近10天 | `=concat(dateStr('yyyy-MM-dd',-10),' 00:00:00')\|=dateStr('yyyy-MM-dd HH:mm:ss')` |
|
||||
| 最近3个月 | `=concat(dateStr('yyyy',-1),'-',dateStr('MM',-3),'-',dateStr('dd'))\|=dateStr('yyyy-MM-dd')` |
|
||||
|
||||
## 9. JS增强与CSS增强
|
||||
|
||||
v1.3.79+ 支持。
|
||||
|
||||
### JS API方法
|
||||
|
||||
| 方法 | 参数 | 用途 |
|
||||
|------|------|------|
|
||||
| `updateSelectOptions(dbCode, fieldName, options)` | 数据集编码, 字段名, 选项数组 | 动态更新下拉选项 |
|
||||
| `onSearchFormChange(dbCode, fieldName, callback)` | 数据集编码, 字段名, 回调函数 | 监听控件值变化 |
|
||||
| `updateSearchFormValue(dbCode, fieldName, value)` | 数据集编码, 字段名, 值 | 设置控件初始值 |
|
||||
| `getSelectOptions(dbCode, fieldName)` | 数据集编码, 字段名 | 获取当前下拉选项 |
|
||||
| `notLoadDataWhenShow()` | 无 | 预览时不自动加载数据(v1.6.7+) |
|
||||
|
||||
### 三级联动下拉示例
|
||||
|
||||
```javascript
|
||||
function init(){
|
||||
// 加载省份
|
||||
$http.metaGet('http://localhost:8080/jeecg-boot/ces/ai/customSelect')
|
||||
.then(res => {
|
||||
this.updateSelectOptions('pca', 'pro', res.data)
|
||||
})
|
||||
// 省→市联动
|
||||
this.onSearchFormChange('pca', 'pro', (value) => {
|
||||
$http.metaGet('http://localhost:8080/jeecg-boot/ces/ai/customSelect', {pid: value})
|
||||
.then(res => { this.updateSelectOptions('pca', 'city', res.data) })
|
||||
})
|
||||
// 市→区联动
|
||||
this.onSearchFormChange('pca', 'city', (value) => {
|
||||
$http.metaGet('http://localhost:8080/jeecg-boot/ces/ai/customSelect', {pid: value})
|
||||
.then(res => { this.updateSelectOptions('pca', 'area', res.data) })
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 设置下拉默认选中第一项(v1.4.0+)
|
||||
|
||||
```javascript
|
||||
function init(){
|
||||
let ops = this.getSelectOptions('de', 'sex');
|
||||
if(ops && ops.length > 0){
|
||||
this.updateSearchFormValue('de', 'sex', ops[0].value)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### CSS增强示例
|
||||
|
||||
```css
|
||||
.jm-query-form .ivu-btn-primary {
|
||||
background-color: red;
|
||||
border-color: red;
|
||||
}
|
||||
```
|
||||
|
||||
## 10. 参数配置设置
|
||||
|
||||
| 配置项 | 用途 | 适用控件 |
|
||||
|--------|------|---------|
|
||||
| `loadTree` | 树结构加载URL | 下拉树 |
|
||||
| `loadTreeByValue` | 按值检索树URL | 下拉树 |
|
||||
| `dictSplit` | 字典分隔符(仅英文字符) | 下拉单选/多选 |
|
||||
| `selectSearchPageSize` | 每页显示条数(默认10) | 下拉单选/多选 |
|
||||
| `order` | SQL排序 | 数据集字段详情(非报表参数) |
|
||||
| `required` | 必填标记(v1.7.9+) | 所有类型 |
|
||||
|
||||
必填配置:`{"required": true}`,默认 `false`。v1.9.6+ 支持可视化配置界面。
|
||||
|
||||
## 11. 查询设置(querySetting)
|
||||
|
||||
```json
|
||||
"querySetting": {
|
||||
"izOpenQueryBar": false,
|
||||
"izDefaultQuery": true
|
||||
}
|
||||
```
|
||||
|
||||
| 设置 | 默认值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `izDefaultQuery` | true | 是否自动执行查询(关闭后需手动点击查询按钮) |
|
||||
| `izOpenQueryBar` | false | 是否默认展开查询栏 |
|
||||
211
.trae/skills/jimureport/references/signature.md
Normal file
211
.trae/skills/jimureport/references/signature.md
Normal file
@@ -0,0 +1,211 @@
|
||||
# 接口签名机制 (JimuSignature)
|
||||
|
||||
## 概述
|
||||
|
||||
积木报表部分接口使用 `@JimuSignature` 注解标记,调用时需要在请求 Header 中携带签名参数,否则返回 `code: 1001` 签名校验失败错误。
|
||||
|
||||
**源码位置:**
|
||||
- 后端拦截器:`jimureport-spring-boot-starter/.../common/interceptor/JimuReportSignatureInterceptor.java`
|
||||
- 前端签名工具:`static/jmreport/desreport_/js/biz/SignMd5Util.js`
|
||||
- 前端请求拦截器:`static/jmreport/desreport_/js/core/request.js`
|
||||
|
||||
## 需要签名的接口
|
||||
|
||||
| 接口 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/jmreport/queryFieldBySql` | POST | SQL 解析获取字段 |
|
||||
| `/jmreport/executeSelectApi` | POST | API 数据集解析 |
|
||||
| `/jmreport/loadTableData` | POST | 加载表数据 |
|
||||
| `/jmreport/testConnection` | POST | 测试数据源连接 |
|
||||
| `/jmreport/download/image` | GET | 下载图片 |
|
||||
| `/jmreport/dictCodeSearch` | GET | 字典编码搜索 |
|
||||
| `/jmreport/getDataSourceByPage` | GET | 分页查询数据源 |
|
||||
| `/jmreport/getDataSourceById` | GET | 按ID查询数据源 |
|
||||
|
||||
**不需要签名的接口(常用):**
|
||||
- `/jmreport/save` — 保存报表
|
||||
- `/jmreport/saveDb` — 保存数据集
|
||||
- `/jmreport/get/{id}` — 获取报表
|
||||
- `/jmreport/field/tree/{reportId}` — 获取数据集树
|
||||
- `/jmreport/loadDbData/{dbId}` — 加载数据集详情
|
||||
|
||||
## 签名算法
|
||||
|
||||
### 请求 Headers
|
||||
|
||||
| Header | 值 | 说明 |
|
||||
|--------|------|------|
|
||||
| `X-Sign` | MD5 签名(大写) | 见下方计算方法 |
|
||||
| `X-TIMESTAMP` | 当前时间戳(毫秒) | `int(time.time() * 1000)` |
|
||||
|
||||
### 计算步骤
|
||||
|
||||
```
|
||||
1. 收集所有请求参数(URL query参数 + POST body参数)
|
||||
2. 按 key 字母升序排序(SortedMap / TreeMap)
|
||||
3. 转为 JSON 字符串:JSON.stringify(sortedParams) 或 JSONObject.toJSONString(sortedMap)
|
||||
4. 拼接签名密钥:jsonStr + signatureSecret
|
||||
5. 计算 MD5 并转大写:MD5(jsonStr + secret).toUpperCase()
|
||||
```
|
||||
|
||||
### 签名密钥 (signatureSecret)
|
||||
|
||||
**默认值:** `dd05f1c54d63749eda95f9fa6d49v442a`
|
||||
|
||||
解析优先级:
|
||||
1. `JmReportBaseConfig.getSignatureSecret()` — 代码配置
|
||||
2. Spring 属性 `jeecg.signatureSecret` — application.yml 配置
|
||||
3. 默认值 `dd05f1c54d63749eda95f9fa6d49v442a`
|
||||
|
||||
> **注意:** 默认密钥中第29个字符是字母 `v`,不是数字 `4`。
|
||||
|
||||
### 时间戳校验
|
||||
|
||||
服务端校验时间戳有效期为 **5 分钟(300秒)**。如果客户端与服务器时间差超过5分钟,会返回 "签名验证失败:X-TIMESTAMP已过期"。
|
||||
|
||||
### 参数值类型转换规则
|
||||
|
||||
前端在签名前会统一类型(后端用 `json.getString(key)` 读取,也是字符串):
|
||||
- **数字** → 转为字符串(如 `0` → `"0"`)
|
||||
- **布尔** → 转为字符串(如 `false` → `"false"`)
|
||||
- **对象/数组** → 转为 JSON 字符串
|
||||
- **null/空** → 不参与签名
|
||||
|
||||
### 后端校验逻辑
|
||||
|
||||
```java
|
||||
// 1. 收集参数到 SortedMap (TreeMap, 自动按key排序)
|
||||
SortedMap<String, String> map = new TreeMap<>();
|
||||
|
||||
// 2. 从 request.getParameterMap() 取 URL/form 参数
|
||||
// 3. 从 request.getQueryString() 取 GET 参数
|
||||
// 4. 从 POST body JSON 取参数 (json.getString(key))
|
||||
|
||||
// 5. 计算签名
|
||||
String paramsJsonStr = JSONObject.toJSONString(map); // fastjson
|
||||
String signValue = DigestUtils.md5DigestAsHex(
|
||||
(paramsJsonStr + CommonUtils.getSignatureSecret()).getBytes()
|
||||
).toUpperCase();
|
||||
|
||||
// 6. 比对 header 中的 X-Sign
|
||||
```
|
||||
|
||||
> **关键细节:** 后端用 fastjson 的 `JSONObject.toJSONString(map)` 序列化 SortedMap,输出格式为 `{"key1":"value1","key2":"value2"}`(无空格)。Python 端必须用 `json.dumps(sorted_dict, separators=(',', ':'))` 匹配(无空格)。
|
||||
|
||||
## Python 实现
|
||||
|
||||
```python
|
||||
import hashlib
|
||||
import json
|
||||
import time
|
||||
|
||||
SIGNATURE_SECRET = "dd05f1c54d63749eda95f9fa6d49v442a"
|
||||
|
||||
def compute_sign(params_dict):
|
||||
"""
|
||||
计算积木报表接口签名
|
||||
params_dict: 请求参数字典(POST body 或 GET query 参数)
|
||||
"""
|
||||
# 1. 所有值转为字符串(与前端/后端保持一致)
|
||||
str_params = {}
|
||||
for k, v in params_dict.items():
|
||||
if v is None:
|
||||
continue
|
||||
if isinstance(v, bool):
|
||||
str_params[k] = str(v).lower() # True -> "true"
|
||||
elif isinstance(v, (int, float)):
|
||||
str_params[k] = str(v)
|
||||
elif isinstance(v, (dict, list)):
|
||||
str_params[k] = json.dumps(v, ensure_ascii=False, separators=(',', ':'))
|
||||
else:
|
||||
str_params[k] = str(v)
|
||||
|
||||
# 2. 按 key 字母升序排序
|
||||
sorted_params = dict(sorted(str_params.items()))
|
||||
|
||||
# 3. 转为 JSON 字符串(无空格,与 fastjson 一致)
|
||||
params_json = json.dumps(sorted_params, ensure_ascii=False, separators=(',', ':'))
|
||||
|
||||
# 4. 拼接密钥并计算 MD5
|
||||
sign_str = params_json + SIGNATURE_SECRET
|
||||
sign_value = hashlib.md5(sign_str.encode('utf-8')).hexdigest().upper()
|
||||
|
||||
return sign_value
|
||||
|
||||
def get_sign_headers(params_dict):
|
||||
"""获取签名相关的请求头"""
|
||||
return {
|
||||
'X-Sign': compute_sign(params_dict),
|
||||
'X-TIMESTAMP': str(int(time.time() * 1000))
|
||||
}
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```python
|
||||
# POST /jmreport/queryFieldBySql
|
||||
body = {"sql": "select * from demo", "dbSource": "", "type": "0"}
|
||||
sign_headers = get_sign_headers(body)
|
||||
# sign_headers = {'X-Sign': 'AB12CD34...', 'X-TIMESTAMP': '1774019281912'}
|
||||
|
||||
# GET /jmreport/getDataSourceByPage?pageNo=1&pageSize=10
|
||||
query_params = {"pageNo": "1", "pageSize": "10"}
|
||||
sign_headers = get_sign_headers(query_params)
|
||||
```
|
||||
|
||||
### 完整的带签名 API 请求函数
|
||||
|
||||
```python
|
||||
def api_request(path, data=None, method=None):
|
||||
"""发送 API 请求,自动判断是否需要签名"""
|
||||
url = f'{API_BASE}{path}'
|
||||
headers = {
|
||||
'X-Access-Token': TOKEN,
|
||||
'Content-Type': 'application/json; charset=UTF-8'
|
||||
}
|
||||
|
||||
# 需要签名的接口列表
|
||||
SIGNED_ENDPOINTS = [
|
||||
'/jmreport/queryFieldBySql',
|
||||
'/jmreport/executeSelectApi',
|
||||
'/jmreport/loadTableData',
|
||||
'/jmreport/testConnection',
|
||||
'/jmreport/download/image',
|
||||
'/jmreport/dictCodeSearch',
|
||||
'/jmreport/getDataSourceByPage',
|
||||
'/jmreport/getDataSourceById',
|
||||
]
|
||||
|
||||
# 判断是否需要签名
|
||||
need_sign = any(path.rstrip('/').endswith(ep.rstrip('/')) for ep in SIGNED_ENDPOINTS)
|
||||
|
||||
if need_sign:
|
||||
sign_params = data if data else {}
|
||||
headers['X-TIMESTAMP'] = str(int(time.time() * 1000))
|
||||
headers['X-Sign'] = compute_sign(sign_params)
|
||||
|
||||
if data is not None:
|
||||
json_data = json.dumps(data, ensure_ascii=False).encode('utf-8')
|
||||
req = urllib.request.Request(url, data=json_data, headers=headers, method=method or 'POST')
|
||||
else:
|
||||
req = urllib.request.Request(url, headers=headers, method=method or 'GET')
|
||||
|
||||
resp = urllib.request.urlopen(req, context=ctx)
|
||||
return json.loads(resp.read().decode('utf-8'))
|
||||
```
|
||||
|
||||
## 常见错误
|
||||
|
||||
| 错误信息 | 原因 | 解决方案 |
|
||||
|---------|------|---------|
|
||||
| `签名验证失败: 签名参数不存在` | 未传 X-Sign 或 X-TIMESTAMP Header | 添加签名 Headers |
|
||||
| `签名验证失败:X-TIMESTAMP已过期` | 客户端与服务器时间差超过5分钟 | 检查系统时间,使用当前时间戳 |
|
||||
| `签名校验失败,参数有误!` | 签名值不匹配 | 检查参数排序、JSON序列化格式、密钥是否正确 |
|
||||
| `code: 1001` | 签名相关错误的统一错误码 | 查看 message 详情 |
|
||||
|
||||
## 调试技巧
|
||||
|
||||
1. **打印签名输入**:输出 `params_json + secret` 字符串,对比前后端是否一致
|
||||
2. **对比 JSON 格式**:确保使用 `separators=(',', ':')` 无空格格式
|
||||
3. **检查类型转换**:数字/布尔/对象必须转为字符串
|
||||
4. **验证密钥**:确认使用的密钥与服务端配置一致(默认 `dd05f1c54d63749eda95f9fa6d49v442a`)
|
||||
152
.trae/skills/jimureport/references/template-analysis.md
Normal file
152
.trae/skills/jimureport/references/template-analysis.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# 积木报表模板分析参考
|
||||
|
||||
## 模板报表查询
|
||||
|
||||
通过 `getReportByUser` 接口获取模板报表:
|
||||
```
|
||||
GET /jmreport/getReportByUser?reportId=&template=1
|
||||
```
|
||||
|
||||
## 46个模板分类统计
|
||||
|
||||
| 分类 | 数量 | 示例 |
|
||||
|------|------|------|
|
||||
| 基础表格 | 30 | 信息采集表、简单分组报表 |
|
||||
| 图表报表 | 9 | 全国各大城市化员数据、物业实时监控 |
|
||||
| 循环报表 | 4 | 订单表循环打印、班级循环套打表 |
|
||||
| 图片报表 | 4 | 员工信息表、证书打印 |
|
||||
| 条码/二维码 | 3 | 实习证明、凭证条码报表 |
|
||||
|
||||
## 图表数据绑定
|
||||
|
||||
### extData 数据类型 (dataType)
|
||||
|
||||
**前端使用文本字符串,非数字:**
|
||||
- `"sql"` - SQL数据集
|
||||
- `"api"` - API数据集
|
||||
- `"json"` - JSON数据集
|
||||
- `"javabean"` - JavaBean数据集
|
||||
- `"files"` - 文件数据集
|
||||
- `null` - 静态图表(无数据绑定,使用ECharts配置中的硬编码数据)
|
||||
|
||||
### 字段映射规则
|
||||
|
||||
固定三个字段名映射:
|
||||
```python
|
||||
extData = {
|
||||
"axisX": "name", # X轴/分类字段
|
||||
"axisY": "value", # Y轴/数值字段
|
||||
"series": "type" # 系列/分组字段(多系列图表用)
|
||||
}
|
||||
```
|
||||
|
||||
SQL查询需要AS别名:
|
||||
```sql
|
||||
SELECT category AS name, COUNT(*) AS value, '' AS type FROM table GROUP BY category
|
||||
```
|
||||
|
||||
### xText / yText 轴标题
|
||||
|
||||
工作正常的模板中这两个字段常常为空字符串,轴标题主要通过ECharts配置设置:
|
||||
```python
|
||||
chart_config = {
|
||||
"xAxis": {
|
||||
"name": "表单类型", # 轴标题
|
||||
"type": "category"
|
||||
},
|
||||
"yAxis": {
|
||||
"name": "数量",
|
||||
"type": "value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## displayConfig 单元格组件
|
||||
|
||||
用于在普通单元格中渲染条码、二维码、图片。
|
||||
|
||||
### 配置结构
|
||||
```json
|
||||
{
|
||||
"displayConfig": {
|
||||
"1": {"barcodeContent": "#{pop.id}", "format": "CODE128", "width": "50", "height": "100", "displayValue": false},
|
||||
"11": {"text": "#{uiu.tm}", "width": 227, "height": 227, "colorDark": "#000000", "colorLight": "#ffffff"},
|
||||
"111": {"barcodeContent": "固定值", "format": "QR", "width": "6", "height": 39}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 键名规则
|
||||
- 键名 = `列号`(从1开始)
|
||||
- 行号通过cells中的display属性关联
|
||||
|
||||
### 条码配置 (barcodeContent)
|
||||
```json
|
||||
{
|
||||
"barcodeContent": "#{字段变量}", // 动态值
|
||||
"format": "CODE128|CODE39|QR", // 条码格式
|
||||
"width": "2", // 条码宽度
|
||||
"height": 80, // 条码高度
|
||||
"displayValue": false // 是否显示值
|
||||
}
|
||||
```
|
||||
|
||||
### 二维码配置 (text)
|
||||
```json
|
||||
{
|
||||
"text": "#{字段变量}", // 二维码内容
|
||||
"width": 112, // 宽度
|
||||
"height": 112, // 高度
|
||||
"colorDark": "#000000", // 前景色
|
||||
"colorLight": "#ffffff" // 背景色
|
||||
}
|
||||
```
|
||||
|
||||
## 循环报表 (loopBlockList)
|
||||
|
||||
### 结构
|
||||
```json
|
||||
{
|
||||
"loopBlockList": [
|
||||
{
|
||||
"sci": 1, // 起始列
|
||||
"sri": 2, // 起始行
|
||||
"eci": 5, // 结束列
|
||||
"eri": 5, // 结束行
|
||||
"index": 1, // 块索引
|
||||
"db": "jm", // 数据集别名
|
||||
"loopTime": 3 // 循环次数(可选)
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 单元格变量语法
|
||||
```
|
||||
#{数据集别名.字段名}
|
||||
#{jm.name}
|
||||
#{pop.group(id)}
|
||||
```
|
||||
|
||||
## 常见图表类型数据要求
|
||||
|
||||
| 图表类型 | dataType | axisX | axisY | series | 示例数据 |
|
||||
|---------|----------|-------|-------|--------|---------|
|
||||
| bar.simple | sql/api | name | value | type | 单系列柱状 |
|
||||
| bar.multi | sql/api | name | value | type | 多系列柱状 |
|
||||
| line.simple | sql/api | name | value | type | 单线折线 |
|
||||
| pie.simple | sql/api | name | value | - | 饼图 |
|
||||
| gauge.simple | sql/api | name | value | - | 仪表盘 |
|
||||
| radar.basic | sql/api | name | value | type | 雷达图 |
|
||||
| map.scatter | sql/api | name | value | - | 地图散点 |
|
||||
|
||||
## 模板报表ID参考
|
||||
|
||||
| 报表名称 | ID | 特点 |
|
||||
|---------|---|------|
|
||||
| 全国各大城市化员数据 | 1339859143477039104 | 9个图表(sql+api混合) |
|
||||
| 图表数据联动示例 | 1356492523694067712 | 19个图表(静态+动态) |
|
||||
| 物业实时监控 | 1339478701846433792 | 9个图表+地图 |
|
||||
| 凭证条码报表 | 1338370016550195200 | displayConfig条码 |
|
||||
| 实习证明 | 1350035590569136128 | displayConfig二维码 |
|
||||
| 图片展示平台 | 1334074491629867008 | 8个图片+4个图表 |
|
||||
Reference in New Issue
Block a user