Install
$ agentstack add skill-jeecgboot-skills-jeecg-onlchart ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
JeecgBoot Online 图表 AI 自动生成器
将自然语言的图表需求描述转换为 Online 图表配置,并通过 API 在 JeecgBoot 系统中自动创建/编辑图表。
> 重要:本 skill 处理「Online 图表」(SQL 驱动的数据可视化图表),不涉及「Online 报表」(cgreport 数据列表)或「Online 表单」(cgform)。
前置条件
用户必须提供以下信息(或由 AI 引导确认):
- API 地址:JeecgBoot 后端地址(如
https://boot3.jeecg.com/jeecgboot) - X-Access-Token:JWT 登录令牌(从浏览器 F12 获取)
如果用户未提供,提示: > 请提供 JeecgBoot 后端地址和 X-Access-Token(从浏览器 F12 → Network → 任意请求的 Request Headers 中复制)。
交互流程
Step 0: 判断操作类型
| 用户意图关键词 | 操作类型 | |---------------|---------| | 创建/新建/做一个/生成图表 | 新增图表 → Step 1A | | 修改图表/改字段/换图表类型 | 编辑图表 → Step 1B |
Step 1A: 新增图表 — 解析需求
从用户描述中提取:
| 信息 | 必填 | 默认值 | 示例 | |------|------|--------|------| | 图表编码 (code) | 是 | 自动生成 snake_case | tj_user_sex | | 图表名称 (name) | 是 | 用户指定 | "统计男女比例" | | SQL 语句 (cgrSql) | 是 | 从需求推导或用户提供 | select count(*) cout, sex from sys_user group by sex | | X 轴字段 (xaxisField) | 是 | 从 SQL 推导 | sex | | Y 轴字段 (yaxisField) | 是 | 从 SQL 推导 | cout | | 图表类型 (graphType) | 是 | bar | bar、line、pie、line,bar | | 展示模板 (displayTemplate) | 否 | tab | tab、single、double | | 数据源 (dbSource) | 否 | 空(默认数据源) | second_db | | 数据类型 (dataType) | 否 | sql | sql |
X/Y 轴推导规则:
- X 轴 (xaxisField):通常是分类/维度字段(如 sex、dept、month、category)
- Y 轴 (yaxisField):通常是度量/聚合字段(如 count、sum、avg 的结果)
Step 1B: 编辑图表 — 查询现有配置
- 用户提供图表 ID 或编码
- 通过 API 查询现有图表配置(参考 API 列表)
- 展示现有配置,根据用户需求进行修改
Step 2: 解析字段(根据 dataType 选择接口)
2A. SQL 类型 — 复用 parseSql 接口
GET /online/cgreport/head/parseSql?sql={urlEncodedSql}&dbKey={dbKey}
sql:URL 编码后的 SQL 语句dbKey:数据源编码,默认数据源可不传
返回结构:
{
"success": true,
"result": {
"fields": [
{ "fieldName": "cout", "fieldTxt": "cout", "fieldType": "String", "isShow": 1, "orderNum": 1 }
],
"params": []
}
}
> 注意:parseSql 返回的 isShow 是数字 (0/1),但图表接口需要字符串 "Y"/"N",需要转换。
2B. JSON/API 类型 — 使用 parseField 接口
POST /online/graphreport/head/parseField?type=JSON
POST /online/graphreport/head/parseField?type=API
请求体(JSON 类型传 JSON 字符串,API 类型传接口 URL):
{ "data": "[{\"month\":\"01\",\"amount\":1000}]" }
返回结构(与 parseSql 相同格式):
{
"success": true,
"result": {
"fields": [
{ "fieldName": "month", "fieldTxt": "month", "fieldType": "String", "isShow": 1 },
{ "fieldName": "amount", "fieldTxt": "amount", "fieldType": "Integer", "isShow": 1 }
],
"params": []
}
}
> JSON/API 类型无需配置 dbSource,cgrSql 字段存放 JSON 字符串或 API URL。
API 数据格式要求
API 接口返回的数据必须包裹在 {"data": [...]} 结构中,否则图表无法解析:
// ✓ 正确
{"data": [{"name": "一月", "value": 120}, {"name": "二月", "value": 200}]}
// ✗ 错误(裸数组,图表无法识别)
[{"name": "一月", "value": 120}]
parseField 失败时的处理
parseField?type=API 要求 JeecgBoot 服务端能访问该 URL。若服务端无法访问外网导致失败,跳过 parseField,手动构造 items(字段已知时可直接定义)。
使用 YApi Mock 创建 API 数据源
项目内置 YApi Mock 平台(https://api.jeecg.com),可快速创建 mock 接口作为 API 数据源。 使用 scripts/yapi_mock.py 脚本操作,凭证获取规则,禁止硬编码:
- 优先从当前上下文(系统提示、memory、全局配置等任意来源)中查找 YApi 邮箱和密码
- 上下文中找不到时,必须询问用户:
> 需要使用 YApi Mock 创建数据源,请提供登录邮箱和密码(平台地址:https://api.jeecg.com)。
> yapi_mock.py 内部使用 http.cookiejar.CookieJar + build_opener 管理会话,兼容 Python 3.6 / 3.9 / 3.12 及以上所有版本,直接调用 init_yapi(email, password) 即可。
import sys
sys.path.insert(0, '/scripts')
from yapi_mock import init_yapi, create_mock
# 凭证由 Claude 从 memory 读取后注入,不得硬编码
init_yapi(email='', password='')
# 创建 mock 接口并写入数据,返回完整 mock URL
mock_url = create_mock(
path='/staff', # 路径后缀,不含 basepath(/claude)
title='职员信息',
data=[
{"name": "张三", "salary": 18000},
{"name": "李四", "salary": 15000},
]
)
print(mock_url) # https://api.jeecg.com/mock/57/claude/staff
路径规则(重要):项目 basepath 为 /claude,接口路径只写后缀:
| 传入 path | 完整 mock URL | |-----------|--------------| | /staff | https://api.jeecg.com/mock/57/claude/staff | | /line | https://api.jeecg.com/mock/57/claude/line |
Step 3: 智能字段配置
3.1 字段属性映射(图表 vs 报表的差异)
关键差异:图表字段使用 "Y"/"N" 字符串,而非数字 0/1。
| 属性 | 图表 (graphreport) | 报表 (cgreport) | 说明 | |------|-------------------|-----------------|------| | 关联头ID | graphreportHeadId | cgrheadId | 字段名不同 | | 是否显示 | isShow: "Y"/"N" | isShow: 0/1 | 类型不同 | | 是否合计 | isTotal: "Y"/"N" | isTotal: "0"/"1" 或 null | 类型不同 | | 是否查询 | searchFlag: "Y"/"N" | isSearch: 0/1 | 字段名和类型都不同 | | 查询模式 | searchMode | searchMode | 相同 | | 字典 | dictCode | dictCode | 相同 | | 排序 | orderNum | orderNum | 相同 |
3.2 字段显示名称 (fieldTxt)
parseSql 返回的 fieldTxt 默认等于 fieldName,AI 需要根据语义翻译为中文:
| 字段名模式 | 推导中文名 | |-----------|-----------| | count / cout / cnt | 数量/人数/次数 | | sum / total / amount | 合计/总额 | | avg / average | 平均值 | | sex | 性别 | | dept / department | 部门 | | status | 状态 | | type / category | 类型/分类 | | month / year / date | 月份/年份/日期 | | name / title | 名称 | | age | 年龄 | | salary | 薪资 |
3.3 是否显示 (isShow)
| 规则 | isShow | |------|--------| | 所有字段(默认) | "Y"(图表通常字段不多,全部显示) | | id / 主键字段 | "N" |
3.4 是否查询 (searchFlag) + 查询模式 (searchMode)
| 字段类型 | searchFlag | searchMode | |---------|------------|------------| | 分类/维度字段 | "Y" | single | | 日期/时间字段 | "Y" | group | | 度量/聚合字段 | "N" | null |
3.5 是否合计 (isTotal)
| 规则 | isTotal | |------|---------| | 度量/聚合字段 | "Y" | | 维度/分类字段 | "N" |
3.6 字典配置 (dictCode) — 列表数据值替换显示
作用:列表(明细表格区域)中,将数据库存储的原始值替换为可读文本显示。
> 例:性别字段数据库存 1/2,配置字典后显示为"男"/"女"。
支持两种方式:
方式一:系统字典编码
填写系统字典的 dictCode,由系统字典表自动解析值 → 文本。
"dictCode": "sex"
常用系统字典编码:
| dictCode | 说明 | |----------|------| | sex | 性别(1=男,2=女) | | priority | 优先级 | | valid_status | 有效状态 | | yn | 是/否 |
方式二:字典 SQL
在 dictCode 处直接写一条 SELECT 语句,动态替换显示值。SQL 必须返回两列:value(数据库存的值)和 text(展示的文本)。
"dictCode": "SELECT id as value, name as text FROM sys_category WHERE pid = '1'"
"dictCode": "SELECT code as value, name as text FROM sys_depart ORDER BY depart_order"
> 注意:字典 SQL 每次渲染都会执行查询,数据量大时建议加 WHERE 条件限制范围。
Step 4: 图表类型选择
根据数据特征推荐图表类型:
| 数据场景 | 推荐 graphType | 说明 | |---------|---------------|------| | 分类对比(如男女人数) | bar | 柱状图 | | 趋势变化(如月度销售) | line | 折线图 | | 占比分布(如部门比例) | pie | 饼图 | | 趋势+对比(如月度销售对比) | line,bar | 组合图表 | | 纯数据明细 | table | 数据表格(只渲染在底部明细区,不占图表位) |
graphType 支持逗号分隔多种类型,如 "bar,pie" 会同时生成柱状图和饼图两个区域。
组合图表配置(折线+柱状同坐标系):
graphType:"line,bar"(逗号分隔多种类型)isCombination:"combination"(标记为组合图表)- 非组合图表
isCombination为 null 或不传
Step 5: 展示摘要并确认
必须展示以下内容,等待用户确认后再执行:
## Online 图表配置摘要
- 图表编码:tj_user_sex
- 图表名称:统计男女比例
- 图表类型:bar(柱状图)
- X 轴字段:sex(性别)
- Y 轴字段:cout(人数)
- 数据源:默认
- 目标环境:https://boot3.jeecg.com/jeecgboot
### SQL 语句
select count(*) cout, sex from sys_user group by sex
### 字段配置
| 序号 | 字段名 | 显示名称 | 类型 | 显示 | 查询 | 字典 | 合计 |
|------|--------|---------|------|------|------|------|------|
| 0 | cout | 人数 | String | Y | N | - | Y |
| 1 | sex | 性别 | String | Y | N | sex | N |
### 参数
(无)
确认以上配置?(y/n)
Step 6: 校验编码可用性(仅新增时)
用户确认后,新增图表前必须先校验 code 是否已被占用:
GET /sys/duplicate/check?tableName=onl_graphreport_head&fieldName=code&fieldVal={code}
返回结构:
{ "success": true, "result": true } // true = 可用(未重复)
{ "success": true, "result": false } // false = 已存在,需换一个 code
若 result 为 false,提示用户更换编码,不继续执行创建。
Python 示例:
encoded_code = urllib.parse.quote(report_code)
check = api_request(f'/sys/duplicate/check?tableName=onl_graphreport_head&fieldName=code&fieldVal={encoded_code}')
if not check.get('result'):
print(f'图表编码 "{report_code}" 已存在,请换一个编码')
exit(1)
print(f'编码 "{report_code}" 可用,继续创建...')
Step 7: 调用 API 创建/编辑图表
用户确认且编码校验通过后执行。
6.1 新增图表 — 请求结构
POST /online/graphreport/head/add
{
"dbSource": "",
"name": "统计男女比例",
"code": "tj_user_sex",
"displayTemplate": "tab",
"xaxisField": "sex",
"yaxisField": "cout",
"dataType": "sql",
"graphType": "bar",
"cgrSql": "select count(*) cout, sex from sys_user group by sex",
"onlGraphreportItemList": [
{
"id": "前端生成的长数字ID",
"cgrheadId": null,
"fieldName": "cout",
"fieldTxt": "人数",
"fieldWidth": null,
"fieldType": "String",
"searchMode": null,
"isOrder": null,
"isSearch": null,
"dictCode": null,
"fieldHref": null,
"isShow": "Y",
"orderNum": 0,
"replaceVal": null,
"isTotal": null,
"createBy": null,
"createTime": null,
"updateBy": null,
"updateTime": null,
"groupTitle": null
}
],
"paramsList": []
}
> 注意(add 接口):add 时 items 中的关联ID字段名为 cgrheadId(值为 null),虽然查询/编辑时返回的是 graphreportHeadId。
6.2 编辑图表 — 请求结构
PUT /online/graphreport/head/edit
{
"id": "1290934362649460737",
"name": "统计男女比例",
"code": "tj_user_bysex",
"cgrSql": "select count(*) cout, sex from sys_user group by sex",
"xaxisField": "sex",
"yaxisField": "cout",
"yaxisText": "yaxis_text",
"content": null,
"extendJs": null,
"graphType": "line,bar",
"isCombination": "combination",
"displayTemplate": "tab",
"dataType": "sql",
"dbSource": "",
"tenantId": 0,
"lowAppId": null,
"onlGraphreportItemList": [
{
"id": "1290934166687383554",
"graphreportHeadId": "1290934362649460737",
"fieldName": "cout",
"fieldTxt": "人数",
"isShow": "Y",
"isTotal": "N",
"searchFlag": "N",
"searchMode": null,
"dictCode": "",
"fieldHref": null,
"fieldType": "String",
"orderNum": 0,
"replaceVal": null,
"createBy": "admin",
"createTime": "2020-08-05 17:03:06",
"updateBy": null,
"updateTime": null
}
],
"paramsList": []
}
add 与 edit 字段差异:
| 字段 | add | edit | 说明 | |------|-----|------|------| | id (head) | 不传 | 必传 | 图表头ID | | yaxisText | 不传 | 可选 | Y轴标签文字 | | content | 不传 | 可选 | 自定义内容 | | extendJs | 不传 | 可选 | 扩展JS | | isCombination | 不传 | 可选 | 组合图表标记 | | tenantId | 不传 | 传回原值 | 租户ID | | Item 关联ID字段 | cgrheadId: null | graphreportHeadId: headId | 字段名不同 | | Item isShow | "Y"/"N" | "Y"/"N" | 一致 | | Item searchFlag | 不存在,用 isSearch | searchFlag: "Y"/"N" | add 和 edit 可能不同 |
6.3 字段 ID 生成规则
- add 时使用雪花ID格式(19位数字字符串),如
"2033369959277633538" - 可用 Python 的
str(int(time.time() * 1000) * 1000000 + random.randint(100000, 999999))近似生成
6.4 使用 Python 调用 API
重要限制:
- Windows 环境下 curl 发送中文/长 JSON 会出错,必须使用 Python
- 禁止使用
python3 -c "..."内联方式 - 必须先用 Write 工具写入
.py临时文件,再用 Bash 执行,最后删除临时文件
推荐方式:使用 onlchart_api.py 封装脚本(与 jeecg-onlreport 的 onlreport_api.py 同模式)
脚本路径: skill 加载时开头已提供 Base directory for this skill: ,scripts 目录即 \scripts。
import sys
sys.path.insert(0, r'\scripts')
from onlchart_api import init_api, build_item, build_param, create_chart
init_api('', '')
items = [
build_item('sex', '性别', search_flag='Y', search_mode='single', dict_code='sex', order_num=0),
build_item('cout', '人数', is_total='Y', order_num=1),
]
# 默认只创建图表,不挂菜单、不授权
# 如需发布,改用 create_and_publish() 或单独调用 publish_chart()
result = create_chart(
code='tj_user_sex',
name='用户性别分布',
sql='SELECT sex, count(*) cout FROM sys_user GROUP BY sex',
x='sex', y='cout',
graph_type='bar',
items=items,
)
可用函数一览:
| 函数 | 说明 | |------|------| | init_api(api_base, token) | 初始化连接(必须最先调用) | | build_item(field_name, ...) | 构建字段配置 | | build_param(param_name, ...) | 构建自定义参数 | | parse_sql(sql, db_key) | 解析 SQL 字段 | | check_code_available(code) | 校验编码是否可用 | | create_chart(code, name, sql, x, y, items, ...) | 创建图表 | | edit_chart(head_id, ...) | 编辑图表 | | publish_chart(head_id, name, role_code) | 挂载菜单 + 授权角色 | | create_and_publish(code, name, sql, x, y, items, ...) | 一键全流程 | | list_charts() | 查询图表列表 | | query_chart(head_id) | 查询图表详情 | | get_chart_id_by_code(code) | 按编码查 head_id |
低级手写脚本模板(不推荐,优先使用上方封装):
import urllib.request
import json
import time
import random
import ssl
import urllib.parse
API_BASE = '{用户提供的后端地址}'
TOKEN = '{用户提供的 X-Access-Token}'
# 忽略SSL验证(开发环境)
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
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'
}
if data is not None:
json_data = json.dumps(data, ensure_ascii=False).encode('utf-8')
if method is None:
method = 'POST'
req = urllib.request.Request(url, data=json_data, headers=headers, method=method)
else:
if method is None:
method = 'GET'
req = urllib.request.Request(url, headers=headers, method=method)
resp = urllib.request.urlopen(req, context=ctx)
return json.loads(resp.read().decode('utf-8'))
def gen_id():
"""生成雪花ID格式的字符串(19位数字)"""
return str(int(time.time() * 1000) * 1000000 + random.randint(100000, 999999))
# ====== Step 1: 调用 parseSql 解析字段 ======
sql = "select count(*) cout, sex from sys_user group by sex"
encoded_sql = urllib.parse.quote(sql, safe='')
parse_result = api_request(f'/online/cgreport/head/parseSql?sql={encoded_sql}')
print('解析结果:', json.dumps(parse_result, ensure_ascii=False, indent=2))
if not parse_result.get('success'):
print('SQL 解析失败:', parse_result.get('message'))
exit(1)
# ====== Step 2: 构造字段配置 ======
items = [
{
"id": gen_id(), "cgrheadId": None,
"fieldName": "cout", "fieldTxt": "人数",
"fieldWidth": None, "fieldType": "String",
"searchMode": None, "isOrder": None, "isSearch": None,
"dictCode": None, "fieldHref": None,
"isShow": "Y", "orderNum": 0,
"replaceVal": None, "isTotal": None,
"createBy": None, "createTime": None,
"updateBy": None, "updateTime": None, "groupTitle": None
},
{
"id": gen_id(), "cgrheadId": None,
"fieldName": "sex", "fieldTxt": "性别",
"fieldWidth": None, "fieldType": "String",
"searchMode": None, "isOrder": None, "isSearch": None,
"dictCode": "sex", "fieldHref": None,
"isShow": "Y", "orderNum": 1,
"replaceVal": None, "isTotal": None,
"createBy": None, "createTime": None,
"updateBy": None, "updateTime": None, "groupTitle": None
}
]
# ====== Step 3: 构造请求 ======
graph_data = {
"dbSource": "",
"name": "统计男女比例",
"code": "tj_user_sex",
"displayTemplate": "tab",
"xaxisField": "sex",
"yaxisField": "cout",
"dataType": "sql",
"graphType": "bar",
"cgrSql": sql,
"onlGraphreportItemList": items,
"paramsList": []
}
# ====== Step 4: 调用 add API 创建图表 ======
result = api_request('/online/graphreport/head/add', graph_data)
print('创建结果:', json.dumps(result, ensure_ascii=False, indent=2))
if result.get('success'):
print('\n图表创建成功!')
else:
print('\n创建失败:', result.get('message'))
编辑图表脚本差异:
# 编辑时用 PUT 方法,且 items 使用 graphreportHeadId
graph_data = {
"id": "existing_head_id",
"name": "统计男女比例",
"code": "tj_user_bysex",
"cgrSql": sql,
"xaxisField": "sex",
"yaxisField": "cout",
"yaxisText": "",
"content": None,
"extendJs": None,
"graphType": "line,bar",
"isCombination": "combination",
"displayTemplate": "tab",
"dataType": "sql",
"dbSource": "",
"tenantId": 0,
"lowAppId": None,
"onlGraphreportItemList": [
{
"id": "existing_item_id",
"graphreportHeadId": "existing_head_id",
"fieldName": "cout", "fieldTxt": "人数",
"isShow":
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [jeecgboot](https://github.com/jeecgboot)
- **Source:** [jeecgboot/skills](https://github.com/jeecgboot/skills)
- **License:** Apache-2.0
- **Homepage:** https://jeecg.com
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.