技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
字符(字段默认值/fieldHref 的天坑)\n\n**现象**:`fieldDefaultValue`(如 `${order_num_rule}`、`#{sysUserId}`)和 `fieldHref`(如 `?name=${name_required}`)中含 `
jeecg-onlform【数据与研究】JeecgBoot Online表单(cgform)全生命周期管理——通过API自动创建/编辑数据库表和表单配置, 支持单表、主子表、树表,26种控件类型,以及JS/Java/SQL增强、权限配置、数据CRUD、积木报表集成。 只要用户意图涉及「Online表单」就必须使用本技能,包括但不限于: 创建或配置数据库表("建一张请假表"、"创建online表"、"做一个带下拉选择的表"、"低代码表单"、"在线表单"、"配置表")、 修改已有Online表字段("加个字段"、"改字段类型"、"加子表"、"删除字段")、 配置表单增强("JS增强"、"自定义按钮"、"表单联动"、"Java增强"、"SQL增强")、 配置权限("字段权限"、"按钮权限"、"数据权限"、"授权给角色")、 管理表单数据("…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
将自然语言的表单需求描述转换为 Online 表单配置 JSON,并通过 API 在 JeecgBoot 系统中自动创建/编辑表单。
重要:本 skill 处理「Online 表单」(元数据驱动,运行时 CRUD),不涉及「设计器表单」(desform)。两者是完全独立的表单体系。
⚠️ 目标判定(2026-09-03 实测防再犯):文字 ≠ URL。 用户话术含「租户 + 应用 + 表单/工作表」和「新增时隐藏/隐藏标题」等面板属性词 → 目标是敲敲云(lowApp)工作表,走 jeecg-lowcode-lowapp,即使同一请求贴了 Online/jmreport URL。jmreport
generateTemplateByOnline?onlineId=…里的 onlineId 是某 Online 表的 headId(只佐证它配过打印模板),URL 内token=是 jmreport 会话令牌,不能当 API 令牌(会 401)——贴了这个 URL 不代表用户要操作该 Online 表。按文字锁目标,禁止凭 URL 误入本技能。
| 用户需求 | 应使用的技能 |
|---|---|
| 元数据驱动的表/表单配置(字段定义、控件类型、数据库建表) | 本技能 (jeecg-onlform) |
| 拖拽式可视化表单设计(自由布局、表单设计器) | jeecg-desform |
| SQL查询结果以列表展示 | jeecg-onlreport |
| SQL查询结果以图形展示(柱状图/饼图/折线图) | jeecg-onlchart |
| 复杂Excel样式报表(打印、分组、循环) | jimureport |
scripts/
├── onlform_creator.py # 表单创建/编辑(单表/主子表/树表)
├── onlform_jimureport.py # 积木报表集成(创建/删除报表并关联)
├── onlform_enhance.py # JS/Java/SQL增强 + 自定义按钮
├── onlform_auth.py # 权限配置(字段/按钮/数据权限)
├── onlform_data.py # 数据 CRUD(增删改查/树数据/导出CSV)
└── onlform_menu.py # 菜单挂载 + 路由缓存 + 角色授权
templates/ # ⚡ 可直接复用的 JSON 模板(优先读取,不要现场设计)
└── all_controls_master_sub.json # 全控件主子表(26种控件,主表+一对多+一对一)
# ⚠️ 模板内的表名仅为占位符,创建时必须按业务语义自定义表名,不能原样照用
references/
├── onlform-field-types.md # 字段类型/控件/字典/校验/默认值/扩展配置
├── onlform-widget-types.md # 控件速查表(fieldShowType 完整清单)
├── onlform-head-field-types.md # head 级字段说明(tableType/themeTemplate 等)
├── onlform-full-widget-template.md # 26种控件配置速查(dictTable/dictField/fieldExtendJson)
├── onlform-enhance-js.md # JS增强参考(onlChange/loaded/列表拦截/API,~310行)
├── onlform-enhance-java.md # Java增强参考 + 实战案例(~140行)
├── onlform-enhance-misc.md # SQL增强/自定义按钮/fieldHref/实战(~130行)
├── onlform-auth.md # 权限配置(字段/按钮/数据权限 API)
├── onlform-data-crud.md # 数据 CRUD API + 存储格式
├── onlform-jimureport.md # 积木报表集成 8 步流程
├── onlform-misc.md # 杂项:表类型/布局/BPM/视图/错误处理
├── onlform-api-reference.md # 完整 JSON 数据结构和字段枚举
└── onlform-route-cache.md # 路由缓存配置(动态/静态路由、组件名称映射)
用户必须提供以下信息(或由 AI 引导确认):
https://boot3.jeecg.com/jeecgboot)所有 HTTP 调用必须遵循,否则用户会感到响应明显变慢。本节为强制要求,违反会直接被用户吐槽"太慢了"。
现象:Windows 的 Bash tool 会把 python / python -c / skill 脚本当作长命令自动 run_in_background,tool 立即返回一个 background ID,真正的执行输出要等系统通知才到——把毫秒级调用放大到数秒,用户会立即感到"卡"。
规则:
python -c 探测)都用 PowerShell tool,不要用 Bash tool。curl 在任何平台都不用——跨平台不一致,且 Windows Bash 下同样被后台化。Windows 正确示例:
PowerShell: python C:/path/to/onlform_creator.py --api-base http://localhost:8080 --token xxx --config config.json
PowerShell: python -X utf8 -c "import urllib.request as u, json; r=u.urlopen(...); print(r.read().decode())"
Windows 错误示例(会被后台化):
Bash: python onlform_creator.py ... ← 会返回 "Command running in background"
Bash: python -c "..." ← 同上
Bash: curl -X POST ... ← 同上
历史教训:本 skill 早期版本错误地声称"Python 在所有 shell 下都同步返回"——实测 Windows Bash tool 对 python 也会后台化。曾因此被用户连续吐槽"执行太慢了"。
$ 字符(字段默认值/fieldHref 的天坑)现象:fieldDefaultValue(如 ${order_num_rule}、#{sysUserId})和 fieldHref(如 ?name=${name_required})中含 $,用 Bash python -c "..." 执行时,shell 会把 ${xxx} 当作变量展开——xxx 不是 shell 变量就变成空字符串,写入值残缺(如 ?name= 丢了变量名)。
根因:Bash 在解析双引号字符串时优先展开 ${...},即使它在 Python 字符串字面量内。
规则:
$ 时,禁止用 python -c "..." inline 执行(Bash 和 PowerShell 都不安全——PowerShell 虽不展开 ${},但会展开 ${} 在某些场景下仍出问题)。.py 临时文件 → Bash/PowerShell 执行 .py 文件。文件内容不被 shell 解析,$ 安全。${order_num_rule} → bash 报 order_num_rule_param: onl_watch=text_pure: command not found${name_required} → 后端收到的值变成 ?name=(变量被吃)正确示例(Write → Execute):
Write: C:\Users\zhang\AppData\Local\Temp\onl_create_xxx.py ← 含 $ 的配置写进 .py
Bash: python "C:\Users\zhang\AppData\Local\Temp\onl_create_xxx.py"
历史教训:
${name_required}被 bash 截断为?name=,用户发现后质疑 skill 有缺陷。实际是 shell 行为,不是 Python/后端的问题。此后所有含$的字段配置一律走文件。
Windows 中文编码规则(
python -c必须加-X utf8): PowerShell 默认编码为 UTF-16 LE,python -c "..."inline 代码中含中文字面量时,shell 传参过程会乱码,导致SyntaxError: unterminated string literal。
- 解法:始终用
python -X utf8 -c "..."而不是python -c "..."- 含中文的复杂逻辑:写入临时
.py文件再执行,彻底避免 inline 中文- 结果含中文的 print:在 inline 脚本开头加
import sys; sys.stdout.reconfigure(encoding='utf-8')
需要多个 reference 文件时,在同一条消息中并发发出所有 Read 调用,不要一个读完再读下一个。
// ✅ 正确:一条消息同时 Read 多个文件(并发)
Read(onlform-field-types.md) + Read(onlform-master-detail-checklist.md) + Read(onlform-enhance-js.md)
// ❌ 错误:串行读取
Read(field-types) → 等结果 → Read(checklist) → 等结果 → Read(enhance)
常用并发组合:建表 → 同时读
field-types.md+master-detail-checklist.md;JS增强 → 同时读enhance.md(若已在建表时读过则跳过)。
skill 已提供 onlform_creator.py / onlform_jimureport.py 等脚本,它们封装了鉴权、重试、head 解析、主子表关联等细节。需要一次性 HTTP 探测再用 python -c。
表名查重:仅在"用户暗示要复用已有表 / 表名看上去像已有资源"时才查。普通新建场景直接跑 onlform_creator.py——遇重名接口会返回明确错误,预查反而增加一次往返。onlform_creator.py 已内置自动加 _1/_2 后缀重试,主表冲突无需手动干预。
字典存在性:以下系统内置字典直接使用,禁止发 API 查询,值已固化:
| 字典编码 | 值 → 含义 | 控件类型 |
|---|---|---|
yn | 1=是 / 0=否 | list / radio / switch |
sex | 1=男 / 2=女 | list / radio |
valid_status | 1=有效 / 0=无效 | list / radio |
priority | L=低 / M=中 / H=高 | list / radio |
bpm_status | 1=待提交 / 2=审批中 / 3=审批通过 / 4=审批拒绝 | list |
仅在字典编码明显是业务自定义且不确定是否创建过时才发 sys/dict/list 查询。
❌ 严禁拼造 /sys/dict/queryDictItemsByCode/{code} 这种 RESTful 风格的字典查询路径:后端根本没有这个端点,会返回 "路径不存在,请检查路径是否正确"。
GET /sys/api/queryDictItemsByCode?code={dictCode}(code 走 query 串,不是 path variable)。sys/dict/list?dictCode=xxx + sys/dictItem/list?dictId={id} 两步法(见 references/onlform-data-crud.md)。link_table 引用表存在性:这项仍然必须查(见 references/onlform-field-types.md),引用不存在的表会让创建成功但运行时报错,排查成本高。
一次 tool call 拿到所有结果;拆成多条 shell 既有进程启动开销,在 Windows 下还会被后台化。
# 用 shell tool 跑(Windows: PowerShell,Linux/macOS: Bash)
python -c "
import urllib.request as u, json
h = {'X-Access-Token':'<token>'}; base = 'http://host'
def g(p): return json.loads(u.urlopen(u.Request(base+p, headers=h), timeout=10).read())
dup = g('/sys/duplicate/check?tableName=onl_cgform_head&fieldName=table_name&fieldVal=xxx')
dct = g('/sys/dict/list?dictCode=xxx')
print('dup=', dup.get('result'), '; dict.total=', dct.get('result', {}).get('total'))
"
JeecgBoot 后端的 context path 因部署而异:
http://host:port/jeecg-boothttp://host:port(无 /jeecg-boot 后缀)首次不确定时:直接按用户给的原样用。如果 onlform_creator.py 返回 "路径不存在,请检查路径是否正确",去掉 /jeecg-boot 重试一次(或反之)——这个错误是确定性的,不要改别的参数。
「路径不存在」也可能是接口路径本身写错了(context path 没问题,但端点本身后端没有)。常见反例:
/sys/dict/queryDictItemsByCode/{code} —— 路径 + 风格全错,后端没有这个端点GET /sys/api/queryDictItemsByCode?code={code}(注意是 /sys/api,参数走 query)排查顺序:先按 context path 加/去 /jeecg-boot 重试;仍然 404 时,回到 SKILL 文档/references 里复核接口路径——不要凭印象拼 RESTful 风格的 URL。
listByHeadId,禁止其他接口历史教训:曾因依次试错
field/list(返回跨表乱序垃圾)→ 读脚本源码 →getByHead(返回空)→ 才到listByHeadId,浪费了 4 次往返,被用户吐槽"不应该 30s 就搞定吗"。
强制规则(新增/删除/修改字段前获取现有字段时必须遵守):
GET /online/cgform/field/listByHeadId?headId={headId}field/list——不带 cgformHeadId 过滤时会返回所有表的混合数据getByHead——实测返回空 fieldsonlform_creator.py 源码——直接用上面的接口即可多表并行查询(一次 shell tool 调用,不要串行;Windows: PowerShell,Linux/macOS: Bash):
for tbl, hid in [('t1','id1'), ('t2','id2')]:
req = u.Request(base + '/online/cgform/field/listByHeadId?headId=' + hid, headers=h)
fields = json.loads(u.urlopen(req, timeout=10).read()).get('result', [])
# 按 orderNum 排序后直接使用
fields.sort(key=lambda f: f.get('orderNum', 999))
历史教训:
dept_info冲突 → 改名 dept_info_1 → 子表再次冲突 → 再改名 → 额外更新 subTableStr,三步重试链浪费 ~50s。 🔴 2026-08-05 用户反馈:自动加业务名_YYYYMMDD日期后缀(如leave_apply_20260805)表名冗长难记,明确禁止。
强制规则: 直接用纯业务名(不加日期后缀),遇到 数据库表[xxx]已存在 错误时,用短序号后缀 xxx_1 → xxx_2 递增重试,禁止加 _YYYYMMDD 日期后缀。
# ✅ 首选(不加后缀)
dept_info ← 主表
dept_archive ← 一对一子表
dept_employee ← 一对多子表
# ⚠️ 冲突时(仅当 addAll 报已存在)
dept_info_1 ← 短序号后缀
dept_employee_1
onlform_creator.py 已内置 _1/_2 自动重试并同步子表 mainTable 引用,无需手动干预历史教训:2026-08-05 用户反馈:通过 skills 创建的 Online 表单,数据库属性页里
create_by/create_time/update_by/update_time/sys_org_code等系统字段排在业务字段上方,查看和编辑时很不方便。
强制规则: onlform_creator.py 创建表单时,字段顺序必须按以下规则排列:
id 始终排在第一位id(按用户给出的字段顺序)create_by / create_time / update_by / update_time / sys_org_codehas_child 始终放在业务字段最后{mainTable}_id)作为隐藏字段,排在业务字段后面效果示例:
# ✅ 正确顺序
1 id
2 applicant ← 业务字段
3 apply_date
4 expense_type
5 total_amount
6 create_by ← 系统字段
7 create_time
8 update_by
9 update_time
10 sys_org_code
该规则已写入
scripts/onlform_creator.py的build_fields_from_config(),所有通过本 skill 新建的 Online 表单自动遵守。
历史教训:3 个 JSON 文件拆成 3 次 Write tool call,每次有固定开销,合计浪费 ~12s。
强制规则: 多个临时 JSON 配置必须在同一个 Python 脚本里批量写入,禁止逐个调用 Write tool。用 Python 的 json.dump 写文件——跨平台一致,无 BOM 风险。
# 正确:在同一个 Python 脚本里批量写入多个 JSON(跨平台)
import tempfile, json, os, subprocess
tmp = tempfile.gettempdir() # 自动适配 Windows %TEMP% / Linux /tmp / macOS /var/folders/.../T
CREATOR = r'<skill目录>/scripts/onlform_creator.py'
configs = {
'onl_main.json': {"action": "create", "tables": [{"tableName": "...", ...}]},
'onl_sub1.json': {"action": "create", "tables": [{"tableName": "...", ...}]},
}
paths = {}
for fname, cfg in configs.items():
path = os.path.join(tmp, fname)
with open(path, 'w', encoding='utf-8') as f:
json.dump(cfg, f, ensure_ascii=False)
paths[fname] = path
# 依次调用 onlform_creator.py
for path in paths.values():
subprocess.run(['python', CREATOR, '--api-base', BASE, '--token', TOKEN, '--config', path], check=True)
历史教训:查 orderNum → Write tool 写 JSON → 再调用 creator,拆成 3 步 tool call 浪费 ~10s。
强制规则: 编辑字段时,把 listByHeadId 查询、edit config 生成、写临时文件、调用 creator 全部写进一个 Python 脚本,在单次 shell tool call(Windows: PowerShell,Linux/macOS: Bash)里跑完。
# 模板:查询 → 生成 config → 写文件 → 调用 creator(一个脚本搞定)
import subprocess, json, os, tempfile
import urllib.request as u
# 1. 查询现有字段
fields = query_fields(head_id)
# 2. 在内存中构建 edit config(无需 Write tool)
cfg = {"action": "edit", "tableName": tname, "addFields": [...], "modifyFields": [...]}
path = os.path.join(tempfile.gettempdir(), f'onl_edit_{tname}.json')
with open(path, 'w', encoding='utf-8') as f:
json.dump(cfg, f, ensure_ascii=False)
# 3. 直接调用 creator
subprocess.run(['python', '-X', 'utf8', CREATOR, '--api-base', BASE, '--token', TOKEN, '--config', path], check=True)
tables: [] 并行 + 单表 add/modify 一次提交(节省 ~20s)历史教训:曾分两轮编辑 3 张表 = 6 次串行
subprocess.run(creator)。每次 creator 启动 ~1s + addAll/syncDb HTTP 各 ~1s ≈ 共 18-25s。三个症状叠加:
- 同一张表的
addFields(新增)和modifyFields(重排)拆成两轮调用 → HTTP/syncDb 翻倍- 多张表的 edit 串行 spawn 子进程 → 表数 × Python 冷启动
- 每次都重新 import 模块 → Windows 上每次 ~800ms 启动开销
强制规则(多表/复合编辑必须遵守):
同一张表的所有变更合并为一次 editAll:addFields + modifyFields + deleteFields 写在同一个 config,creator 内部一次提交即完成。
⚠️ addFields 的 orderNum 不被后端采纳——新字段始终追加到末尾(实测)。 同一次 editAll 里
modifyFields只对已存在字段生效,对同批addFields新建的字段无效。 教训来源:2026-05-09 给员工子表插入sex字段,指定orderNum=9并在modifyFields里后移id_card/phone,结果sex仍追加到末尾(orderNum=11),需第二次单独modifyFields才修正。
插入到中间位置的正确两步法:
addFields 创建字段(orderNum 随便填,后端忽略,字段追加到末尾);modifyFields 里只后移已有字段modifyFields,把新建字段的 orderNum 设到目标位置# 第一步:创建字段 + 后移已有字段(新字段 orderNum 会被忽略,先追加到末尾)
cfg1 = {"action":"edit","tableName":"t","addFields":[{"dbFieldName":"sex",...}],
"modifyFields":[{"dbFieldName":"id_card","orderNum":10},{"dbFieldName":"phone","orderNum":11}]}
# 第二步:修正新字段的 orderNum(必须在第一步完成后执行)
cfg2 = {"action":"edit","tableName":"t","modifyFields":[{"dbFieldName":"sex","orderNum":9}]}
多表编辑用顶层 tables: []:onlform_creator.py 的 action: "edit" 现在支持 tables 数组,会用线程池并发执行 N 张表的编辑。表数 ≥ 2 时必须用此格式,不要写 N 个单表 config 串行 spawn。
# ✅ 正确:一次 creator 调用,N 张表并行
cfg = {"action": "edit", "tables": [
{"tableName": "t1", "addFields": [...], "modifyFields": [...]},
{"tableName": "t2", "addFields": [...]},
{"tableName": "t3", "modifyFields": [...]}
]}
subprocess.run(['python', '-X', 'utf8', CREATOR, '--api-base', BASE, '--token', TOKEN, '--config', path], check=True)
# ❌ 错误:串行 spawn 3 次 creator
for cfg in [cfg1, cfg2, cfg3]:
subprocess.run([..., '--config', path], check=True) # 每次 ~3-5s
# ❌ 错误:同一张表 add 一轮、reorder 一轮
subprocess.run([..., '--config', add_only_config]) # 第一轮:add
subprocess.run([..., '--config', reorder_only_config]) # 第二轮:reorder(多余的 syncDb)
收益:3 张表的「add + reorder」从 6 次 creator 调用(~25s)降到 1 次(~5s)。
历史教训:早期用
dict/add+dictItem/add逐条写入,遇到result=null时还会 TypeError 崩溃触发重跑。实际上 JeecgBoot 有批量接口,1 次 HTTP 完成所有字典+items,且内置"已存在跳过"逻辑。
接口:POST /sys/dict/batchAddDictWithItems
{"dictList": [{...}, {...}]},每项含 sysDictItemList{"successCount": N, "failureCount": M, "failureList": [...]}def ensure_dicts_batch(base, token, dicts):
"""
dicts = [
{"dictName":"订单类型","dictCode":"order_type","description":"...","items":[("国内","1",1),...]},
...
]
"""
payload = {"dictList": [
{
"dictName": d["dictName"],
"dictCode": d["dictCode"],
"description": d.get("description", ""),
"sysDictItemList": [
{"itemText": t, "itemValue": v, "sortOrder": o, "status": 1}
for t, v, o in d["items"]
]
}
for d in dicts
]}
H = {'X-Access-Token': token, 'Content-Type': 'application/json'}
body = json.dumps(payload, ensure_ascii=False).encode('utf-8')
req = urllib.request.Request(base + '/sys/dict/batchAddDictWithItems', data=body, headers=H, method='POST')
r = json.loads(urllib.request.urlopen(req, timeout=15).read().decode('utf-8'))
# ⚠️ 全部已存在时接口返回 success=false + code 500,需判断 failList 区分"真失败"和"已存在跳过"
result = r.get('result') or {}
fail_list = result.get('failList') or []
real_fails = [f for f in fail_list if '已经存在' not in f.get('errorMsg', '')]
if real_fails:
raise RuntimeError(f'字典创建失败: {real_fails}')
skip_count = len([f for f in fail_list if '已经存在' in f.get('errorMsg', '')])
print(f' 字典批量: 新建={result.get("successCount",0)}, 已存在跳过={skip_count}')
return r
# 调用示例(N 个字典 1 次 HTTP)
ensure_dicts_batch(BASE, TOKEN, [
{"dictName":"订单类型", "dictCode":"order_type", "description":"国内/国际", "items":[("国内","1",1),("国际","2",2)]},
{"dictName":"运输方式", "dictCode":"transport_mode", "description":"运输方式(全量)","items":[("陆运","land",1),("铁运","rail",2),("空运","air",3),("海运","sea",4)]},
])
⚠️ 接口行为陷阱(实测):全部字典已存在时,接口返回
success=false+code=500,而不是静默跳过。 判断方式:遍历result.failList,errorMsg含"字典编码已经存在"的视为正常跳过,其余才是真失败。
收益:N 个字典 + M 个 items 从 1+N×M 次 HTTP → 1 次 HTTP,无 result=null 防御问题。
错误 数据库表[xxx]已存在,请从数据库导入表单 表示:物理 DB 表残留,但 Online 配置不存在(通常是之前创建后被手动删了 Online 头)。
onlform_creator.py和onlform_pipeline.py已内置自动加后缀重试,并自动更新关联子表的mainTable引用。主表冲突无需手动干预,脚本会自动完成。执行后在汇总输出中确认实际使用的表名即可。
GET /online/cgform/head/transTables/{tableName} 把物理表导入为 Online 配置,再用 action=edit 调整字段。主子表场景特别注意:主表建失败且脚本未自动重试时,子表如果已创建会成为"孤儿"。此时先
DELETE /online/cgform/head/delete?id={孤儿子表headId}删干净,再整体重建。
重要: 配置表单字段的字典、用户选择、部门选择等数据源时,必须遵循"先查后建"原则。 使用
jeecg-systemskill 的system_utils.py查询和管理主数据。
所有传给脚本的 --config <xxx.json> 必须写到系统临时目录,由 OS 自动清理;skill 和脚本都不主动 rm。
import tempfile, os, json
config_path = os.path.join(tempfile.gettempdir(), 'onl_<表名>_<步骤>.json')
with open(config_path, 'w', encoding='utf-8') as f:
json.dump(cfg, f, ensure_ascii=False, indent=2)
tempfile.gettempdir() 自动适配:Windows %TEMP% / Linux /tmp / macOS /var/folders/.../T(注意 macOS 不是 /tmp)。命名建议带表名+步骤前缀(如 onl_sk_audit_create.json)便于排错。
❌ 禁止:
<skill>/tmp/ 或当前工作目录(污染 skill / 用户项目)/tmp / C:\Temp(不跨平台)rm / Remove-Item(OS 会清,多余 tool call)os.path.exists() 检查(自身就是一次 tool call)临时文件可能被 OS 异步清理,但乐观调用 + 报错再补救:仅当脚本返回 FileNotFoundError / 配置文件不存在 时,用相同内容重写后重试,不要换路径或去 skill 目录找。
| 用户意图关键词 | 操作类型 | 使用脚本 |
|---|---|---|
| 创建/新建/做一个/生成 | 新增表单 → Step 1A | onlform_creator.py |
| 加字段/增加字段/修改字段/删除字段 | 编辑表单 → Step 1B | onlform_creator.py |
| 集成积木/关联打印/打印报表 | 积木报表集成 → Step 8 | onlform_jimureport.py |
| JS增强/按钮/增强功能 | 增强配置 → Step 9 | onlform_enhance.py |
| 权限/授权/数据规则 | 权限配置 → Step 10 | onlform_auth.py |
| 造数据/插入/查询/导出 | 数据操作 → Step 11 | onlform_data.py |
| 挂载菜单/加到菜单/预览地址/缓存路由 | 菜单挂载 → Step 12 | onlform_menu.py |
$N 后缀 = 视图,不是表单!)现象:用户说"视图"或表名含 $N(如 all_ctrl_demo_20260703$2),AI 却忽略 $N 后缀直接查原表名(去掉 $N),在原表上操作。
根因:$ 在 shell 中有特殊含义,AI 下意识去掉后缀去查"干净"的表名,但 $N 正是视图的标识。
规则:
$N 后缀(N=1,2,3...)→ 这是视图,不是原表单,必须用精确表名查询physicId 指向原表GET /online/cgform/head/list?copyType=1&physicId={原表headId} 或用精确 tableName 查正确流程:
1. 用户给的表名含 $N → 直接用精确表名查 head/list?tableName=xxx$N
2. 查不到 → 通过 copyType=1&physicId={原表headId} 列出所有视图
3. 找到视图 headId → 所有后续操作(字段编辑/权限/增强)都用视图的 headId
历史教训:用户说"online表单视图名称为 all_ctrl_demo_20260703$2 授权限",AI 搜索
all_ctrl_demo_20260703(去掉了$2)找到原表,在原表上配了权限。用户纠正后才发现$2是视图标识,视图 headId 完全不同,需重新在视图上操作。
从用户描述中提取:
| 信息 | 默认值 | 示例 |
|---|---|---|
| 表名 (tableName) | 自动生成 snake_case | leave_application |
| 表描述 (tableTxt) | 用户指定 | "请假申请" |
| 表类型 (tableType) | 1=单表 | 提到"主子表"→2/3,提到"树形"→1+isTree |
| 字段列表 | 从描述中解析 | 姓名(必填)、请假天数(数字)、日期(范围查询) |
判断表类型:
🔴 多数据源原则:用户不提多数据源 → 不设
dbSource→ 不追加enableMultiDataSource→ 默认主库。仅用户明确说"多数据源/数据源编码xxx/指定数据源"时才配置。
使用全控件模板(all_controls_master_sub.json)时: 读取模板作为结构参考,但表名(tableName/tableTxt)必须根据用户的业务描述重新命名,不能使用模板中的占位符表名。
创建前不要查重表名。
addAll接口本身会校验并返回明确的重复提示,提前查重是多余的网络开销。遇到重名错误(
数据库表[xxx]已存在)时的处理规则:
当前操作 处理方式 新建表单(用户意图是创建一个新表) 自动加后缀重试: xxx→xxx_1→xxx_2…,直到成功;执行成功后告知用户实际使用的表名修改/编辑表单(用户意图是改已有表) 切换到 editAll 流程 导入数据库已有表 调用 transTables/{tableName}导入❌ 绝对不能删除已有表(
DELETE /online/cgform/head/delete)来"让位"给新建——这会丢失已有数据和配置。删除只能在用户显式要求删除时执行。
字典字段配置易错点(必须注意):
dictField(字典编码,如 sex、education),dictTable 和 dictText 留空。绝对不能把 dictTable 设为 sys_dict_item。dictTable 填业务表名(如 sys_user,也可以填 Online 创建的表),dictField 填存储值字段,dictText 填显示文本字段。sys/dict/list?dictCode=xxx 查询,不存在则通过 sys/dict/add + sys/dictItem/add 创建(先查后建)。GET /online/cgform/head/list?tableName={表名}&pageNo=1&pageSize=1listByHeadId,见效率规则第6条):
GET /online/cgform/field/listByHeadId?headId={headId}
返回结果按 orderNum 排序,确认需要插入的前后字段的 orderNum 值多表编辑 / 增删改一次提交(见效率规则第10条):涉及多张表时用
tables: []一次配置并行执行;同一张表的addFields+modifyFields必须放在同一个 config(一次 editAll 内全做完,避免 add 一轮、reorder 一轮的双往返)。
单表编辑 + 重排同时进行的 JSON 示例(一次完成):
{
"action": "edit",
"tableName": "dept_employee",
"addFields": [
{"dbFieldName": "sex", "dbFieldTxt": "性别", "fieldShowType": "radio",
"dbType": "string", "dbLength": 2, "dictField": "sex", "orderNum": 9}
],
"modifyFields": [
{"dbFieldName": "id_card", "orderNum": 10},
{"dbFieldName": "phone", "orderNum": 11}
]
}
多表并行编辑 JSON 示例(creator 自动用线程池并发):
{
"action": "edit",
"tables": [
{"tableName": "dept_info", "addFields": [...], "modifyFields": [...]},
{"tableName": "dept_archive", "addFields": [...]},
{"tableName": "dept_employee", "addFields": [...], "modifyFields": [...]}
]
}
详细字段类型映射、字典配置、校验规则、默认值、扩展配置、特殊控件配置参见:
references/onlform-field-types.md
⚠️ link_table(关联记录)前置检查(必须执行): 配置
link_table字段时,dictTable只能引用 Online 管理的业务表,不能是系统表。 必须先调用GET /online/cgform/head/list?tableName={表名}&pageNo=1&pageSize=1验证被关联表是否存在。
- 若不存在 → 先创建该 Online 表并同步数据库,再插入至少 3 条示例数据,然后再配置 link_table 字段。
- 若已存在 → 继续执行列名验证(必须):调用
GET /online/cgform/api/getColumns/{headId}查出实际列名列表,用真实列名覆盖模板/假设中的dictText字段值,不能凭猜测或模板占位符直接使用。教训:仅检查表存在还不够——模板里的
dictText: "user_code,phone"与目标表实际列name/age/sex/birthday完全不同,导致运行时报Unknown column 'user_code' in 'field list'。 详细说明见references/onlform-field-types.md的link_table小节。
核心映射速查:
| 关键词 | fieldShowType | dbType |
|---|---|---|
| 文本 | text | string |
| 备注 | textarea | string |
| 日期 | date | Date |
| 下拉 | list | string |
| 单选 | radio | string |
| 多选 | checkbox | string |
| 开关 | switch | string |
| 图片 | image | string |
| 文件 | file | string |
| 用户选择 | sel_user | string |
| 部门选择 | sel_depart | string |
| 省市区 | pca | string |
| 富文本 | umeditor | Text |
必须展示配置摘要,等待用户确认后再执行。 这一步至关重要——字段配置一旦创建后修改成本较高(需要逐个编辑),提前确认能避免返工。摘要需包含:
使用 scripts/onlform_creator.py(推荐方式):
python <skill目录>/scripts/onlform_creator.py --api-base <URL> --token <TOKEN> --config <config.json>
单表创建 JSON 示例:
{
"action": "create",
"tables": [{
"tableName": "leave_application",
"tableTxt": "请假申请表",
"tableType": 1,
"dbSource": "",
"fields": [
{"dbFieldName": "name", "dbFieldTxt": "姓名", "fieldShowType": "text", "dbType": "string", "dbLength": 100, "fieldMustInput": "1", "isQuery": 1}
]
}]
}
多数据源配置:
dbSource是onl_cgform_head的直接字段(非 extConfigJson),填sys_data_source.code。配置dbSource后脚本自动在extConfigJson中设置enableMultiDataSource: 1,前端「开启多数据源」开关自动勾选、「选择数据源」下拉自动选中。子表不需要设置dbSource,自动跟随主表。
编辑表单 JSON 示例:
{
"action": "edit",
"tableName": "test_demo",
"addFields": [{"dbFieldName": "new_field", "dbFieldTxt": "新字段", "fieldShowType": "text", "dbType": "string", "dbLength": 100}],
"deleteFields": ["old_field"],
"modifyFields": [{"dbFieldName": "existing_field", "dbFieldTxt": "修改后标签", "dbLength": 200}]
}
主子表、树表的完整 JSON 配置示例参见:
references/onlform-misc.md
使用 scripts/onlform_jimureport.py:
python <skill目录>/scripts/onlform_jimureport.py --api-base <URL> --token <TOKEN> --config <config.json>
配置 JSON 示例:
{
"action": "create_report",
"tableName": "customer",
"reportName": "客户表打印",
"fields": [
{"fieldName": "customer_name", "fieldText": "客户名称"},
{"fieldName": "phone", "fieldText": "联系电话"}
]
}
脚本自动完成 8 步:创建报表 → 保存空模板 → 解析字段 → 检查编码 → 保存数据源 → 获取模板 → 写入引用 → 关联表单。
图片/日期字段自动处理:脚本按控件类型(fieldShowType + fieldExtendJson.picker)自动处理单元格——图片控件(
image)值单元格加display:"img"渲染为图片(否则只显示 URL 文字);普通日期控件(date且无 picker)值单元格引用样式format:"date"只显示日期yyyy/MM/dd去掉时分秒;年/月/周/季度 picker 变体的 date 控件保持原值(API 已返回显示值如2029、2026-34周,套格式会渲染成 1970/01/01)。datetime/time/file控件保持原值。 ⚠️ 日期禁止用=DATE_STR()表达式:字段值为空串时表达式抛异常,整表报「渲染失败」(已实测踩坑);必须用原生单元格格式format:"date"(空值安全)。
前提条件:Online 表中至少存在一条记录,否则字段解析不出来。 积木报表 API 详细说明参见:
references/onlform-jimureport.md
使用 scripts/onlform_enhance.py:
python <skill目录>/scripts/onlform_enhance.py --api-base <URL> --token <TOKEN> --config <config.json>
支持的操作:
create_buttons — 创建自定义按钮(button/link/form 样式)save_js — 保存 JS 增强(form/list 类型)save_java — 保存 Java 增强(spring-key/java-class/http-api)save_sql — 保存 SQL 增强query — 查询所有增强配置增强参考(按需读对应文件): JS增强 →
references/onlform-enhance-js.md;Java增强 →references/onlform-enhance-java.md;SQL/按钮 →references/onlform-enhance-misc.md⚠️ 保存增强前必须先 GET 现有内容再合并,禁止直接 PUT 覆盖! 历史教训:给
js_enhance_demo_20260702_1保存 list JS 增强时,直接 PUT 覆盖了整个cgJs字段,导致之前的 JS 代码丢失且无法恢复(API 无版本历史)。 正确流程:GET /online/cgform/head/enhanceJs/{headId}?type=list→ 取出result.cgJs→ 与新增代码拼接 → PUT 全量保存(含旧+新)。Java/SQL 增强同理。
使用 scripts/onlform_auth.py:
python <skill目录>/scripts/onlform_auth.py --api-base <URL> --token <TOKEN> --config <config.json>
支持的操作:
setup_field_auth — 配置字段权限(列表可见/表单可见/表单可编辑)setup_button_auth — 配置按钮权限setup_data_auth — 配置数据权限规则grant_role — 授权给角色/部门/用户query — 查询所有权限配置权限配置详细参考参见:
references/onlform-auth.md和references/onlform-misc.md🔴 启用字段权限前必须先过滤隐藏字段! 用户说"字段全部启用"时,不能无脑把所有字段都开权限。必须先用listByHeadId查出每个字段的isShowList和isShowForm,只对 列表或表单上实际可见的字段(isShowList=1或isShowForm=1)启用权限。反之,两个都是 0 的字段(isShowList=0 AND isShowForm=0)在页面上完全不显示,不应纳入字段权限控制。常见需要跳过的:id/create_by/create_time/update_by/update_time/sys_org_code/has_child。注意:pid(父节点)虽然列表不可见(list=0),但表单上可见(form=1),不能跳过——判断标准是 AND 不是 OR。 历史教训:给tree_ctrl_demo_20260706_1$1视图"字段全部启用"时,无脑开了全部 39 个字段;用户指出 id 等隐藏字段不该出现后,又错误地把pid(form=1)也关了,用户再次纠正"页面上是有父节点的"。正确做法:listByHeadId→ 只过滤isShowList=0 AND isShowForm=0→ 其余全部启用。🔴 "授权" ≠ "创建",数据权限必须先查已有规则再授权! 用户说"给 XX 授权数据权限"时,意思是把已有的数据规则授权给角色/部门/用户,不是创建新规则。必须先
GET /online/cgform/api/authData/{cgformId}查现有规则 → 找到目标规则 →POST roleDataAuth授权。只有当用户明确说"创建数据权限规则"或现有规则不满足需求时,才新建规则。否则会造成重复规则,且用户看到新增了规则会觉得 AI 多此一举。 历史教训:用户说"为 tree_ctrl_demo_20260706_1 授权数据权限,jeecg用户只能看单选框为男的数据",我直接POST authData新建了一条规则再授权,而基础表上已有规则"只能看单选框为男的数据"。正确做法:先GET authData→ 找到已有规则 → 直接POST roleDataAuth授权。
使用 scripts/onlform_data.py:
python <skill目录>/scripts/onlform_data.py --api-base <URL> --token <TOKEN> --config <config.json>
支持的操作:
insert — 插入数据(单表/主子表)insert_tree — 插入树表数据(自动父先子后)query — 查询数据列表(带过滤)query_tree — 查询树表数据get — 查询单条记录update — 更新记录(自动全量合并)delete — 删除记录export_csv — 导出 CSV数据 CRUD API 和存储格式详细参考参见:
references/onlform-data-crud.md
| 参数 | 说明 |
|---|---|
--api-base | JeecgBoot 后端地址(如 http://localhost:8080/jeecg-boot) |
--token | X-Access-Token |
--config | JSON 配置文件路径 — 必须写到系统临时目录,详见上文「临时配置文件规则」 |
所有脚本支持 tableName 自动解析 headId,无需手动查询。
| 文档 | 何时读取 |
|---|---|
| onlform-field-types.md | 需要确定控件类型(fieldShowType)、字典配置、校验规则、默认值表达式、扩展配置(fieldExtendJson)时——创建/编辑表单字段必读 |
| onlform-enhance-js.md | JS增强:onlChange联动、loaded初始化、beforeSubmit校验、列表拦截、显隐禁用时 |
| onlform-enhance-java.md | Java增强(spring/class/http)、导入增强时 |
| onlform-enhance-misc.md | SQL增强、自定义按钮、按钮表达式、fieldHref超链接时 |
| onlform-auth.md | 用户要求配置字段权限、按钮权限、数据权限、或给角色授权时 |
| onlform-data-crud.md | 需要插入/查询/更新/删除表单数据、导出CSV时——确认各控件的值格式必读 |
| onlform-jimureport.md | 用户要求关联积木报表或集成打印功能时 |
| onlform-master-detail-checklist.md | 创建主子表时必读——subTableStr / mainTable / mainField / 外键字段三项必填清单,缺任何一项关联失效 |
| onlform-misc.md | 处理head级配置(extConfigJson)、主子表/树表JSON结构、BPM集成、视图配置、表单布局(formTemplate)时 |
| onlform-api-reference.md | 需要查看addAll完整请求体模板、head字段枚举、系统默认字段时 |
| onlform-route-cache.md | 用户要求开启Online表单路由缓存(keepAlive)、配置菜单组件名称、或询问动态/静态路由配置时 |
本步骤非必须。 表单/流程创建完成后不要默认执行菜单挂载。先用一句话询问用户:"是否需要挂载到菜单?" 用户明确确认后再执行。 用户可以直接通过预览 URL 访问表单,或自行在系统设置中挂载。演示环境账号通常无菜单创建权限,强行执行会失败。
使用 scripts/onlform_menu.py:
python <skill目录>/scripts/onlform_menu.py --api-base <URL> --token <TOKEN> --config <config.json>
支持的操作:
mount — 挂载单个 Online 表单到菜单(自动推导预览地址和组件名称,可选开启缓存、授权角色)mount_batch — 批量挂载多个表enable_cache — 为已有菜单开启路由缓存挂载菜单 JSON 示例:
{
"action": "mount",
"tableName": "test_order_main",
"menuName": "测试订单主表",
"keepAlive": false,
"roleCode": "admin"
}
开启缓存 JSON 示例:
{
"action": "enable_cache",
"menuId": "xxx"
}
默认不开启缓存路由。仅当用户明确要求时才设置
keepAlive: true。 路由缓存详细参考参见:references/onlform-route-cache.md