jeecgboot/jeecg-codegen
Use when user asks to generate JeecgBoot CRUD code, create a new module, add/modify fields on existing module, or says "代码生成", "生成代码", "创建模块", "新增功能", "建表", "加字段", "加一个字段", "增加字段", "新增字段", "修改字段", "删除字段", "generate code", "new entity", "add field
npx skills add https://github.com/jeecgboot/skills --skill jeecg-codegen
将自然语言需求转换为 JeecgBoot 全套 CRUD 代码(后端 Java + 前端 Vue3 + 菜单权限 SQL),并支持对已生成模块的增量字段修改。
> 重要: 生成代码涉及的字典、角色、用户、部门等主数据,必须遵循"先查后建"原则。
> 使用 jeecg-system skill 的 system_utils.py 查询和管理主数据。
> 详见 ../jeecg-system/SKILL.md。
> 代码生成场景下,新建字典的"建"必须落到 Flyway SQL 文件,禁止调用 find_or_create_dict() / create_dict() 等 API 在远程服务器上直接创建。
>
> Why: 代码生成产物(Entity、前端、Flyway SQL)会通过 git 提交并部署到测试/预发/生产环境。如果字典只通过 API 在当前开发环境创建,部署到线上时线上数据库没有该字典,前端下拉框会空白、列表 _dictText 翻译失效。Flyway SQL 跟着代码走,所有环境拉到代码后执行迁移都会自动建上,是唯一能保证环境一致性的方式。
>
> How to apply:
> - 查询字典 → 走 API 或 MySQL(jeecg-system skill 的 query-dicts / query-dict),用于"先查后建"中的"查"
> - 创建字典 → 写入当次的 Flyway SQL 文件(sys_dict INSERT + sys_dict_item 批量 INSERT),用于"先查后建"中的"建"
> - 禁用:find_or_create_dict()、create_dict() 等 system_utils 中的字典创建函数(在代码生成 skill 中不能调用)
> - 同理适用于:分类字典 sys_category 节点新建 — 也必须写入 Flyway SQL,禁止走 /sys/category/add API
>
> 例外: 角色、审批角色、用户绑定关系等"运行时主数据",由于跨业务可复用,可走 API 创建(按 jeecg-system 原流程)。字典与分类字典是"配置数据",必须走 SQL。
> 严格禁止猜测任何 API 接口路径或参数。 AI 不得根据命名惯例、框架约定或已知路径拼凑接口地址后直接调用。
>
> 所有接口调用必须来源于以下之一:
> 1. 用户明确提供的接口文档或地址
> 2. jeecg-system skill 中已记录的接口
> 3. 通过 jeecg-system skill 查询后确认的接口
>
> 违反此规则即使偶然成功也视为错误操作,因为猜测成功不代表行为合规。
> 以下两条是 AI 凭"框架直觉"最常犯错的地方,文件写出去几乎必现 bug,调试成本极高。每次执行 Step 4 写后端/前端文件之前,必须逐条 self-check。
>
> ### 翻车点 1:每个文件的路径必须与 SKILL/reference 描述完全一致
>
> 写每一个文件之前,必须先在 codegen-reference.md 顶部"文件清单"章节中找到对应文件的路径模板,逐字符比对后再写入。禁止凭"Spring Boot/JeecgBoot 框架直觉"猜测路径。
>
> ### 翻车点 2:FormSchema 必含隐藏 id 字段
>
> 所有 FormSchema(主表 Modal 表单、一对一子表 Form、ERP 风格子表 Form、树表 Modal 表单)首位必须包含:
>
> `typescript
> { label: '', field: 'id', component: 'Input', show: false },
> `
>
> 为什么这是铁律: BasicForm 的 getFieldsValue() 只返回 schema 中声明过的字段。即使 Modal 打开时通过 setFieldsValue({ ...data.record }) 把 id 写入了表单状态,schema 没声明,提交时 getFieldsValue() 也会丢弃它。最终后端收到 entity.id == null,getById(null) 返回 null,Controller 返回 Result.error("未找到对应数据")。编辑功能直接报错。
>
> 位置统一规定:放在 FormSchema 数组首位(不要纠结"最后还是最前",统一首位)。
>
> 这两条规则不需要用户询问、不需要场景判断、不需要选项确认。100% 强制,100% 一致。
> 进入交互流程之前,必须先与用户确认本次使用的生成模式。 任何场景下都不要默默选择,必须显式告知用户当前模式;用户回复"确认"即采用默认。
本 skill 提供两种生成模式:
| 模式 | 状态 | 默认 | 说明 |
|------|------|------|------|
| 串行生成(Serial) | Stable | ✅ 默认 | 主 Agent 顺序生成后端 → 前端 → SQL,全程单线执行,稳定可靠 |
| 并行生成(Parallel) | ⚠️ Beta — 可能不稳定 | ❌ | 派发两个 SubAgent 并行生成前端 / 后端代码,主 Agent 负责契约冻结与跨端校验。详见同目录下 parallel-generation-mode.md |
进入 Step 0 之前,主 Agent 必须先输出类似以下消息,等用户回复后再继续:
本次代码生成将使用【串行生成模式】(默认,稳定)。
如需使用【并行生成模式(Beta)】以缩短耗时,请明确告知。
注意:并行模式当前为 Beta 版本,可能出现前后端字段命名漂移、API URL 不一致、
字典编码错位、FormSchema 隐藏 id 字段遗漏等问题,不确定时建议使用默认串行模式。
满足后,主 Agent 必须读取 parallel-generation-mode.md 并严格按其规范执行(契约冻结 → 派发 SubAgent → 跨端校验 → 输出清单)。任一环节失败 → 按该文档第 6 节"回退策略"切回串行从头来过。
> 铁律不变: 即使选择并行模式,本章上方的"⛔ 接口禁止猜测"、"⛔ 字典创建必须写入 Flyway SQL"、"⛔ 写文件前的强制自检清单(路径 + FormSchema id)"、以及"⛔ 铁律:Step 2 + Step 3 是不可跳过的硬性停止门" 全部仍然 100% 强制 —— 通过派发 prompt 传达给 SubAgent。
> ### ⛔ 铁律:Step 2 + Step 3 是不可跳过的硬性停止门
>
> 全量生成必须严格按顺序执行 Step 0 → Step 1 → Step 2 → 等用户回复 → Step 3 → 等用户确认 → Step 4。
> 在用户明确回复"确认"(或等价表述)之前,绝对禁止开始生成任何代码、创建任何文件、执行任何 SQL。
>
> 以下念头出现时立刻停下,它们都是合理化跳过确认的借口:
>
> | 借口 | 现实 |
> |------|------|
> | "需求描述足够清楚,可以直接推断" | 用户没有确认 ≠ 用户已认可。字段类型、路径、风格都可能偏差。 |
> | "选项都是默认值,不需要问" | 默认值是否适用由用户决定,不由 AI 决定。 |
> | "先生成再改很方便" | 用户不得不事后检查所有文件,浪费双方时间。 |
> | "用户说'Tab风格'已经隐含了风格选择" | 只说明了一个选项,其他9项仍需展示给用户确认。 |
> | "Skill 加载太慢,直接生成更高效" | 效率不是跳过确认的理由。 |
>
> 违反此铁律的代价: 用户发现问题后,所有已生成文件都需要重新生成或逐一修改。
> 此步骤必须在 Step 0 之前执行。前端目标直接决定 Step 2 中需要询问哪些选项。
识别移动端关键词: "移动端"、"手机端"、"UniApp"、"uniapp"、"APP端"、"小程序"、"H5"、"移动页面"、"APP页面"
根据用户描述判断:
| 用户意图 | 判定结果 | Step 2 调整 |
|---------|---------|------------|
| 明确只要移动端(含以上关键词,无 PC 相关词) | 仅移动端 | 跳过 PC 前端选项(选项2、3、6、8、9),改为询问 UniApp3 项目根路径 |
| 明确只要 PC 端(含 "vue3"、"PC端"、"web端" 等词,无移动端词) | 仅 PC 端 | 按原流程 |
| 两者都提到,或描述模糊(如"前后端代码"、"CRUD代码") | 不确定 | 在 Step 2 前先询问用户:"请问需要生成哪端的前端代码?① 仅 PC 端(Vue3) ② 仅移动端(UniApp3) ③ 两者都要" |
仅移动端时的 Step 2 选项调整:
两者都要时: 同时展示 PC 端和移动端的选项,分两组列出。
> ⚠️ 禁止默认跳过任何一端! 无法判断时必须询问用户,不得自行假设"用户可能只要 PC"或"用户只要移动端"。
识别增量修改的关键词: "加字段"、"增加字段"、"新增字段"、"加一个XX字段"、"删除字段"、"修改字段"、"改一下XX"、"给XX模块加"、"给XX表加"
如果是增量修改 → 进入 场景C
如果是全量生成 → 进入 场景A 或 场景B
场景A — 已有表(用户给了表名):
场景B — 新建表(用户用自然语言描述需求):
场景C — 增量修改(给已有模块加/改/删字段):
# <project_root>/<project_vue_root>:后端/前端项目根目录,使用前需向用户确认
# 搜索后端 Entity 文件
find <project_root> -name "{EntityName}.java" -path "*/entity/*"
# 搜索前端 data.ts 文件
find <project_vue_root>/src/views -name "{EntityName}.data.ts"
增量修改的操作类型:
判断表类型:
全控件生成模式("全控件"关键词触发):
当用户说"全控件"、"覆盖所有控件类型"时,触发全覆盖枚举模式,每张表都必须包含该场景支持的所有组件类型,不得只生成代表性字段:
全控件、覆盖所有 FormSchema 控件、覆盖所有 JVxeTypes> 生成全控件主子表,主表+一对一子表覆盖所有 FormSchema 控件,一对多子表覆盖所有 JVxeTypes(含pca),Tab-in-Modal 风格(radio-group 切换)
一对多表的前端布局风格:
> ⚠️ 严禁假设布局风格! 必须在 Step 2 询问用户,用户未回答前不得擅自选择非默认风格(如 Tab-in-Modal)。
> 过去曾犯错:用户未说明风格,却错误地选了 Tab-in-Modal (C9),导致用户反馈后需要重新生成 Modal.vue。
一对多表有三种前端布局风格,用户未指定时默认使用原始布局风格。
> 重要:vue3 封装风格和 vue3Native 原生风格的一对多架构完全不同! vue3 封装风格使用 useJvxeMethod,vue3Native 原生风格使用 useValidateAntFormAndTable。详见 codegen-reference.md 的 C9-C12(vue3)和 C13(vue3Native)。
vue3 封装风格布局选项:
| 风格 | 关键词 | 列表页 | Modal 布局 |
|------|--------|--------|-----------|
| 默认/原始布局 | "默认风格"、"默认"、未指定风格 | 标准列表(无 expandedRowRender) | 上面主表 BasicForm + 下面 a-tabs 子表 |
| Tab-in-Modal (C9) | "tab风格"、"tab切换"、"radio切换"、"标题栏切换" | 标准列表(同默认,无 expandedRowRender) | radio-group 标题栏切换主表/子表,wrapClassName="j-cgform-tab-modal" |
| 内嵌子表 (C12) | "内嵌子表"、"行展开"、"expandedRowRender" | 行展开显示子表(expandedRowRender) | 上面主表 BasicForm + 下面 a-tabs 子表(同默认) |
| ERP (C11) | "ERP风格"、"独立编辑" | 主表单选 + 子表独立 CRUD Tab | 仅主表 BasicForm(子表独立 Modal) |
> ⚠️ 子表外键字段名必须读实体确认,严禁猜测!
> 生成子表 FormSchema 的隐藏外键字段前,必须先 Read 子表 Entity.java,以实体中的 Java 字段名为准。
> 外键字段名因开发者习惯差异很大(companyId / bizCompanyId / mainId / headerId),
> 根据主表实体名推断必然出错,会导致 MySQL Field 'xxx' doesn't have a default value 异常。
> 同样,Modal 中 values.xxx = unref(mainId) 的 xxx 也必须与实体字段名一致。
vue3Native 原生风格(C13)— 架构完全不同:
formComponent.submitForm()/edit()/add()useValidateAntFormAndTable hook(不是 useJvxeMethod)saveOrUpdate 不用 isTransformResponse: falsea-form + Form.useForm,暴露 isForm = trueinitFormData(mainId) 直接传主表 ID(不传 URL 字符串)getFormData() 返回对象(不是数组)queryDataById API 函数useModal + openModal(true, {...}) 模式vue3 封装风格 — 默认/原始布局的关键特征:
<a-tabs> 包裹子表在下方wrapClassName="j-cgform-tab-modal",无 #title 插槽的 radio-grouprefKeys 只包含子表 key(不包含主表 key),如 ['subMany', 'subOne']<JVxeTable>,一对一子表抽成独立 Form.vue 组件(必须用 defineComponent,不能用 <script setup>)useJvxeMethod 的第6个参数 validateSubForm 用于校验一对一子表validateForm(index) 的 index 对应 refKeys 中的位置(0=第一个子表,1=第二个子表)tableRefs 只能包含 JVxeTable 的 ref,禁止包含 Form 组件 ref(否则 resetScrollTop 报错)内嵌子表 (C12) 的关键特征(Modal 与默认布局完全一致,仅 List 不同):
expandedRowRender 行展开显示 SubTable 组件,需额外创建 subTables/ 目录useJvxeMethod 6参数 + classifyIntoFormData + validateSubFormResult<IPage<T>>(不是 Result<List<T>>),SubTable 前端通过 res.result.records 获取数据isTransformResponse:false)BasicColumn[](SubTable 展示)+ JVxeColumn[](Modal 编辑)> 重要:必须直接向用户提问,禁止通过 Glob/Bash/Grep 等工具自动搜索 CLAUDE.md 或项目路径!
> Skill 加载完毕后,立刻将以下选项表格输出给用户,等待用户回复,所有路径/数据库名均通过问用户获取。
一次性展示所有选项及默认值,用户说"确认"即可全部采用默认值,或只说需要改的:
jeecg-module-system/jeecg-system-bizvue3(封装风格),可选 vue3Native(原生风格)是,读取后可自动为字段匹配已有字典编码(见"字典智能匹配"章节)D:/jeecgboot)D:/jeecgboot-vue3)原始布局(主表上方+子表 a-tabs),可选 Tab-in-Modal、内嵌子表、ERP单列(span:24)单列(span:24)单列(span:24)> ⚠️ 第8、9项绝对不能自行假设! 过去曾犯错:未询问直接生成 Tab-in-Modal 风格 + 双列补充信息,用户事后指出才改正。
> ⛔ 展示摘要后必须停止,等待用户明确回复"确认"(或"ok"、"可以"、"没问题"等等价表述)。收到确认前不得进入 Step 4。
> ✅ 只有用户明确确认后,才能进入 Step 4。 用户沉默、未回复、或继续追加需求,都不等于确认。
全量生成流程(根据前端目标选择执行路径):
> 前端目标由 Step 0 前置判断确定:仅 PC 端 / 仅移动端 / 两者都要。
codegen-reference.md 文件清单一致(譬如Mapper XML){ field: 'id', show: false }uniapp/SKILL.md 和 uniapp/references/code-templates.md{EntityName}List.vue + {EntityName}Form.vue + {EntityName}Data.ts(UniApp3 三件套)pages.json 注册路由references/ref-menu-sql.md 获取菜单权限 SQL 模板,禁止凭记忆生成 SQL增量修改流程:
references/ref-d-misc.md{ field: 'id', show: false },不要被新加的字段挤掉列出所有生成/修改的文件路径 + 后续操作说明(执行SQL、重启后端等)。
> ⚠️ 增量修改(场景C)跳过此步骤。
> ⚠️ "仅移动端"场景(Step 0 前置已判定)也跳过此步骤,移动端代码已在 Step 4 中一并生成,无需重复询问。
适用场景: 仅当前端目标为"仅 PC 端"时,文件清单输出完毕后,必须向用户询问:
> "是否同时生成对应的移动端(UniApp3)CRUD 代码?(回复"是"/"y"/"需要"确认,其他内容跳过)"
用户确认后的执行方式:
uniapp/SKILL.md,按其中定义的交互流程执行移动端代码生成uniapp/SKILL.md Step 0 中移动端特有的配置项(UniApp3 项目根目录)前置条件(必须):执行任何 SQL 之前,必须先询问用户要执行到哪个数据库。 不要自动假设目标数据库名称,即使配置文件中有默认值。用户本机可能有多个数据库实例。
判断条件: 数据库连接地址为 127.0.0.1 或 localhost(即本地开发环境)。
自动执行方式: 确认目标数据库后,生成 Flyway SQL 文件后,同时通过 Bash 工具直接执行菜单权限 SQL:
# 先询问用户目标数据库名,假设用户确认为 {dbname}
# 先检查菜单是否已存在,避免重复插入
mysql --no-defaults --default-character-set=utf8mb4 -h127.0.0.1 -P3306 -uroot -proot {dbname} -e "SELECT id FROM sys_permission WHERE id='{timestamp}01'"
# 不存在则执行全部菜单 + 角色授权 SQL
mysql --no-defaults --default-character-set=utf8mb4 -h127.0.0.1 -P3306 -uroot -proot {dbname} < {flyway_sql_file_path}
注意事项:
is_leaf 必须为 0(有按钮子级时),is_leaf=1 会导致按钮权限在权限管理树中不可见菜单 SQL:已自动执行 ✓已有表场景必须先查数据库! 通过以下方式获取精确 DDL:
重要:执行任何 SQL 之前,必须先询问用户要执行到哪个数据库。 不要自动假设数据库名称。先读取 application-dev.yml 获取配置中的数据库名,然后向用户确认是否使用该数据库。
场景A(已有表)— 一条命令取全部信息(DDL + 字段注释 + 字典列表 合并执行):
# 同一条 mysql 命令内完成三件事,减少连接次数
mysql --no-defaults --default-character-set=utf8mb4 -h127.0.0.1 -P3306 -uroot -proot {dbname} \
-e "SHOW CREATE TABLE 表名\G" \
-e "SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT, COLUMN_KEY, EXTRA FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='{dbname}' AND TABLE_NAME='表名' ORDER BY ORDINAL_POSITION" \
-e "SELECT d.dict_code, d.dict_name, GROUP_CONCAT(i.item_text,'=',i.item_value ORDER BY i.sort_order SEPARATOR ', ') AS items FROM sys_dict d LEFT JOIN sys_dict_item i ON d.id=i.dict_id AND i.status=1 WHERE d.del_flag=0 GROUP BY d.dict_code,d.dict_name ORDER BY d.dict_code"
> MySQL 连不上时(端口拒绝/账号错误/服务未启动),禁止直接降级到"跳过查询,全部新建"或"凭命名惯例猜测"。必须按以下优先级走 fallback:
>
> 1. 优先:用 jeecg-system skill 的 HTTP API 查询。 jeecg-system 通过 JeecgBoot 后端 REST 接口工作,不依赖数据库直连,只要后端服务在跑(本地或远程)就能用。
> - 调用方式:python <skill目录>/jeecg-system/scripts/system_creator.py --api-base <地址> --token <X-Access-Token> --action query-dicts
> - 需要的两项信息:后端 API 地址(如 http://localhost:8080/jeecg-boot)+ X-Access-Token(用户从浏览器 F12 → Network → Request Headers 复制)
> - 必须主动向用户索取这两项,不得跳过
> 2. 退而求其次:在项目 SQL 文件中搜索表定义(grep -r "CREATE TABLE.*表名" 在 docs/db/ 目录下)。仅适用于查 DDL,不能用于字典/角色/用户等主数据查询。
> 3. 最后才考虑跳过查询:上述两条都不可行(用户明确拒绝提供 token、后端服务也不可达),且用户书面确认后,方可在 Flyway SQL 中新建所需字典。
>
> 违反此回退顺序即视为违规,包括"MySQL 连不上 → 直接跳过字典查询 → 全部新建"这种降级方式。
> MySQL 命令报 Can't connect/10061/Access denied/ERROR 2002/ERROR 1045 等任何连接错误时,必须立即停止后续所有工作(包括但不限于:搜 SQL 文件、读 application-dev.yml、生成代码、派发 SubAgent、写 Flyway SQL),并向用户输出以下话术等待回复:
>
> `
> ⚠️ MySQL 连接失败({粘贴具体错误信息})。按 SKILL.md "⛔ 数据库不可达时的强制回退路径",
> 在继续之前必须先用 jeecg-system HTTP API 查询。请提供:
> 1. 后端 API 地址(例如 http://localhost:8080/jeecg-boot)
> 2. X-Access-Token(浏览器 F12 → Network → Request Headers 复制)
> 若两者都无法提供,请明确告知,我会再次确认是否接受"基于初始化 SQL 推断(可能与
> 真实库不一致)"作为兜底方案。
> `
>
> 在收到用户对 API 地址 + token 的明确回复之前,禁止执行下方任一动作:
> - 在项目目录下 grep / Grep 搜索字典编码、角色编码、用户、部门
> - 读取 db/jeecgboot-mysql-*.sql 等任何初始化 SQL 文件用于推断主数据存在状态
> - 读取 application-dev.yml / application-prod.yml 寻找其他数据库连接
> - 直接判定字典/角色不存在并准备新建
> - 进入摘要展示(Step 3)
> - 派发 SubAgent
> - 生成 Flyway SQL
以下行为在 MySQL 连接失败时全部视为违规,即使表面"看起来合理"或"看起来能完成任务":
| 错误行为 | 为什么是错的 | 正确做法 |
|---------|------------|---------|
| 在 db/jeecgboot-mysql-*.sql 初始化文件中 grep 字典 / 角色 / 用户的存在状态 | 初始化文件只代表系统初始状态。业务团队已通过 Flyway 增量 SQL、运行时 API、生产库迁移添加了新数据,初始化文件与真实数据库早已脱节。靠它判断"字典是否存在"会产出与真实环境矛盾的代码 | 走 STOP GATE 索要 API + token |
| 用初始化 SQL 中的 admin role ID(如 f6817f48af4fb3af11b9e8bf182f618b)直接写菜单授权 SQL | 用户生产库的 admin role ID 可能与初始化文件不同,菜单授权会打到错的 role 上或失败 | 走 STOP GATE 索要 API + token,然后用 jeecg-system 查 admin role |
| 在 flyway/sql/mysql/ 目录下 grep 字典编码看是否被引用过 | grep 命中只能说明"项目代码引用过这个字典",不能证明"运行时数据库当前确实存在该字典" | 同上 |
| 静默跳过字典查询,直接把所有字典都按"新建"写入本次 Flyway SQL | 与真实库已有的同名字典冲突,部署到非本地环境会主键/唯一约束报错 | 走 STOP GATE |
| 看到 Flyway 目录里有 V*_dict.sql 等历史文件就推断字典已建 | 文件存在 ≠ 字典已 INSERT 成功 ≠ 当前未被删除 / 修改 | 走 STOP GATE |
| 用项目其他 SQL 文件中出现的 dict_code(如 valid_status)就断言它"存在" | 仅 DDL 类信息允许从 SQL 文件回退查询;字典 / 角色 / 用户任何主数据状态都不允许靠 grep 推断 | 走 STOP GATE |
任何时候若发现自己即将执行上述清单中的动作,必须立刻停下,回到 STOP GATE 话术。
只有当用户明确回复"无法提供 token / 后端服务也不可用 / 接受基于初始化 SQL 推断的兜底方案"之后,才允许进入优先级 2 / 3。此时必须再次在摘要中显式标注:
⚠️ 本次字典 / 角色 / 菜单授权基于项目初始化 SQL 推断生成,与你的真实数据库状态可能不一致。
部署到非本地环境前,请手动核对 sys_dict / sys_role 是否已存在同名记录。
用户回复"确认"后才能继续派发 SubAgent / 生成 SQL。
生成 Flyway SQL 前必须检查已有版本号,并同时获取时间戳 — 两条命令并行发出:
# 并行执行(同一轮 response 发出两个 Bash 调用)
ls {后端根路径}/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/ | sort -V | tail -5
date +%s%3N
版本命名规则:V{YYYYMMDD}_{序号}__{描述}.sql
V20260311_1__xxx.sql)V20260311_2__xxx.sql)_1 开始时间戳在 Flyway 版本检查时已并行获取(见上方),直接使用,无需再单独执行 date 命令。
用这个时间戳作为基础 ID,依次拼接 01-14:
{timestamp}01{timestamp}02{timestamp}03> ⛔ MySQL 不可达时的强制回退:
> 本章节的 mysql 命令是默认方式,但 MySQL 连不上时必须按"数据库连接"章节的"⛔ 数据库不可达时的强制回退路径"执行:
> 1. 先尝试 jeecg-system skill 的 scripts/system_creator.py --action query-dicts(需用户提供 API 地址 + token)
> 2. 不能用"MySQL 连不上"作为跳过字典查询、直接新建字典的理由
>
> 详见 ## 数据库连接 章节末尾的"⛔ 数据库不可达时的强制回退路径"。
用户选择"读取系统字典"后,执行以下查询获取全部可用字典:
# 查询所有字典编码及其选项值({dbname} 需替换为用户确认的数据库名)
mysql --no-defaults --default-character-set=utf8mb4 -h127.0.0.1 -P3306 -uroot -proot {dbname} -e "
SELECT d.dict_code, d.dict_name, GROUP_CONCAT(i.item_text, '=', i.item_value ORDER BY i.sort_order SEPARATOR ', ') AS items
FROM sys_dict d
LEFT JOIN sys_dict_item i ON d.id = i.dict_id AND i.status = 1
WHERE d.del_flag = 0
GROUP BY d.dict_code, d.dict_name
ORDER BY d.dict_code
"
匹配规则: 拿到字典列表后,按以下优先级为字段匹配字典:
status)与 dict_code 完全一致匹配成功后的效果:
@Dict(dicCode = "matched_dict_code")dataIndex 使用 fieldName_dictText 后缀component 使用 JDictSelectTag,componentProps: { dictCode: 'matched_dict_code' }JDictSelectTag 组件展示格式: 在 Step 3 表结构摘要中,匹配到字典的字段标注字典编码和选项值,如:
| 字段名 | 类型 | 控件 | 字典 |
| status | varchar(10) | JDictSelectTag | order_status (待付款=0, 已付款=1, 已完成=2) |
JeecgBoot 支持三种字典类型,每种在后端 Entity、前端 data.ts 的 columns/formSchema/searchFormSchema/superQuerySchema 中的写法不同。
适用场景:固定枚举值(状态、类型、级别等),值存储在 sys_dict + sys_dict_item 表中。
查询可用字典:
mysql ... -e "
SELECT d.dict_code, d.dict_name, GROUP_CONCAT(i.item_text, '=', i.item_value ORDER BY i.sort_order SEPARATOR ', ') AS items
FROM sys_dict d LEFT JOIN sys_dict_item i ON d.id = i.dict_id AND i.status = 1
WHERE d.del_flag = 0 GROUP BY d.dict_code, d.dict_name ORDER BY d.dict_code"
后端 Entity:
@Excel(name = "学校状态", width = 15, dicCode = "valid_status")
@Dict(dicCode = "valid_status")
private String schoolStatus;
前端 data.ts — columns:
{ title: '学校状态', align: 'center', dataIndex: 'schoolStatus_dictText' }
// 注意:列表展示用 _dictText 后缀,后端自动翻译字典值为文本
前端 data.ts — formSchema:
{ label: '学校状态', field: 'schoolStatus', component: 'JDictSelectTag',
componentProps: {
dictCode: 'valid_status',
placeholder: '请选择学校状态',
getPopupContainer: () => document.body },
}
前端 data.ts — searchFormSchema:
{ label: '学校状态', field: 'schoolStatus', component: 'JDictSelectTag',
componentProps: { dictCode: 'valid_status' }, colProps: { span: 6 } }
前端 data.ts — superQuerySchema:
schoolStatus: { title: '学校状态', order: 0, view: 'list', dictCode: 'valid_status' }
Controller 查询规则(下拉/多选字段需添加):
Map<String, QueryRuleEnum> customeRuleMap = new HashMap<>();
customeRuleMap.put("schoolStatus", QueryRuleEnum.LIKE_WITH_OR);
QueryWrapper<EduSchool> queryWrapper = QueryGenerator.initQueryWrapper(eduSchool, req.getParameterMap(), customeRuleMap);
适用场景:树形分类数据(省市区、物料分类、部门分类等),数据存储在 sys_category 表中,通过 pid 构成树。
查询可用分类:
mysql ... -e "SELECT id, code, name, pid FROM sys_category WHERE pid = '0' OR pid IS NULL OR pid = '' ORDER BY code"
# 查看某分类的子项:
mysql ... -e "SELECT id, code, name, pid FROM sys_category WHERE code = 'B03' OR pid IN (SELECT id FROM sys_category WHERE code = 'B03') ORDER BY code"
后端 Entity:
// 分类字典不使用 @Dict 注解,由前端 JCategorySelect 组件和 renderCategoryTree 处理翻译
@Excel(name = "所在区域", width = 15)
private String schoolArea;
前端 data.ts — columns:
{ title: '所在区域', align: 'center', dataIndex: 'schoolArea',
customRender: ({ text }) => { return render.renderCategoryTree(text, 'B03'); } }
// 'B03' 是分类字典的顶级 code,renderCategoryTree 会自动翻译 id 为分类名称路径
前端 data.ts — formSchema:
{ label: '所在区域', field: 'schoolArea', component: 'JCategorySelect',
componentProps: {
pcode: 'B03',
getPopupContainer: () => document.body },
}
// pcode 指定分类字典的顶级 code,组件自动渲染树形选择
前端 data.ts — searchFormSchema:
// 分类字典一般不放在搜索栏,如需要则使用 JCategorySelect
{ label: '所在区域', field: 'schoolArea', component: 'JCategorySelect',
componentProps: { pcode: 'B03' }, colProps: { span: 6 } }
前端 data.ts — superQuerySchema:
schoolArea: { title: '所在区域', order: 0, view: 'cat_tree', code: 'B03' }
新增分类字典数据:
> 如果 AI 或用户需要向 sys_category 新增分类节点,必须调用 jeecg-system skill 获取正确的新增接口后再执行,禁止猜测接口路径或直接写库。
新增节点的 code 规则:
> ⛔ 禁止在 add 请求体中传入 code 字段,code 由后端自动生成,AI 不得自行填写。
>
> add 接口响应处理流程(需兼容新旧版本):
>
> 1. 调用 add 接口,请求体中不传 code,只传 pid、name 等字段
> 2. 检查响应的 result 中是否包含 code 字段:
> - 有 code(新版本):直接使用响应中的 code,继续后续操作
> - 无 code(旧版本兼容):主动调用查询接口,通过 id 或 name 查到该节点,取得 code 后再继续
> 3. 确认拿到 code 后,再进行子级节点的新增或其他依赖 code 的操作
生成 List.vue 时的 initDictConfig 规则:
> 生成 initDictConfig() 时,不得加 if (!allDictDate[cCode]) 短路判断,必须每次挂载都调用 loadCategoryData 覆盖 store,否则用户新增分类节点后页面翻译不会刷新。
>
> 正确写法:
> `ts
> function initDictConfig() {
> loadCategoryData({ code: cCode }).then((res) => {
> if (res) {
> userStore.setAllDictItems({ ...userStore.getAllDictItems, [cCode]: res });
> }
> });
> }
> initDictConfig();
> `
生成 handleSuccess 时的分类字典刷新规则:
> 含分类字典的 List.vue,handleSuccess 必须先刷新分类 store 再 reload,确保自己或他人新增分类节点后翻译立即生效:
>
> `ts
> function handleSuccess() {
> selectedRowKeys.value = [];
> loadCategoryData({ code: cCode }).then((res) => {
> if (res) userStore.setAllDictItems({ ...userStore.getAllDictItems, [cCode]: res });
> reload();
> });
> }
> `
>
> 禁止使用 (selectedRowKeys.value = []) && reload() 的简写形式(不刷新分类 store)。
适用场景:关联其他业务表的数据作为下拉选项(如从 sys_depart 表选部门、从 sys_user 表选用户等)。
后端 Entity:
@Excel(name = "归属部门", width = 15, dictTable = "sys_depart", dicText = "depart_name", dicCode = "id")
@Dict(dictTable = "sys_depart", dicText = "depart_name", dicCode = "id")
private String departId;
// dictTable: 关联的表名
// dicText: 作为显示文本的字段
// dicCode: 作为存储值的字段(通常是主键)
前端 data.ts — columns:
{ title: '归属部门', align: 'center', dataIndex: 'departId_dictText' }
// 与系统字典一样,列表展示用 _dictText 后缀
前端 data.ts — formSchema(下拉选择):
{ label: '归属部门', field: 'departId', component: 'JDictSelectTag',
componentProps: {
dictCode: 'sys_depart,
depart_name,
id',
placeholder: '请选择归属部门',
getPopupContainer: () => document.body },
}
// dictCode 格式: '表名,显示字段,值字段'
// 可追加条件: 'sys_depart,depart_name,id,status=1' (第四段为 WHERE 条件)
前端 data.ts — formSchema(搜索选择,大数据量推荐):
{ label: '归属部门', field: 'departId', component: 'JSearchSelect',
componentProps: {
dict: 'sys_depart,
depart_name,
id',
placeholder: '请选择归属部门',
getPopupContainer: () => document.body },
}
// JSearchSelect 支持远程搜索,适合数据量大的表
前端 data.ts — searchFormSchema:
{ label: '归属部门', field: 'departId', component: 'JDictSelectTag',
componentProps: {
dictCode: 'sys_depart,
depart_name,
id' }, colProps: { span: 6 },
}
前端 data.ts — superQuerySchema:
departId: { title: '归属部门', order: 0, view: 'sel_search',
dictTable: 'sys_depart', dictCode: 'id', dictText: 'depart_name' }
Controller 查询规则:
customeRuleMap.put("departId", QueryRuleEnum.LIKE_WITH_OR);
| 维度 | 系统字典 | 分类字典 | 表字典 |
|------|---------|---------|--------|
| 数据来源 | sys_dict + sys_dict_item | sys_category(树形) | 任意业务表 |
| 后端注解 | @Dict(dicCode = "xxx") | 无需 @Dict | @Dict(dictTable="t", dicText="text", dicCode="code") |
| Excel注解 | dicCode = "xxx" | 无 | dictTable="t", dicText="text", dicCode="code" |
| 列表 columns | dataIndex: 'field_dictText' | customRender: render.renderCategoryTree(text, 'pcode') | dataIndex: 'field_dictText' |
| 表单组件 | JDictSelectTag + dictCode: 'xxx' | JCategorySelect + pcode: 'xxx' | JDictSelectTag + dictCode: '表,text,code' |
| 搜索组件 | JDictSelectTag | JCategorySelect | JDictSelectTag 或 JSearchSelect |
| 高级查询 view | list + dictCode | cat_tree + code | sel_search + dictTable/dictCode/dictText |
| Controller 规则 | QueryRuleEnum.LIKE_WITH_OR | 无需特殊处理 | QueryRuleEnum.LIKE_WITH_OR |
| 适用场景 | 固定枚举(状态、类型) | 树形分类(地区、物料) | 关联其他表(部门、用户) |
除三种字典外,JeecgBoot 还支持以下所有组件类型。每种组件在后端 Entity、前端 data.ts 的 columns/formSchema/searchFormSchema/superQuerySchema、Controller 中各有固定写法。
// Entity
@Excel(name = "文本", width = 15)
@Schema(description = "文本")
private String name;
// columns
{ title: '文本', align: 'center', dataIndex: 'name' }
// formSchema
{ label: '文本', field: 'name', component: 'Input', componentProps: { placeholder: '请输入' } }
// superQuerySchema
name: { title: '文本', order: 0, view: 'text' }
@Excel(name = "密码", width = 15)
@Schema(description = "密码")
private String password;
// formSchema — 密码框一般不在 columns 和 searchFormSchema 中展示
{ label: '密码', field: 'password', component: 'InputPassword',
componentProps: { placeholder: '请输入密码' } }
@Excel(name = "备注", width = 15)
@Schema(description = "备注")
private String remark;
// formSchema
{ label: '备注', field: 'remark', component: 'InputTextArea',
componentProps: {
placeholder: '请输入备注',
rows: 4 },
}
// BigDecimal 类型(金额/单价)
@Excel(name = "单价", width = 15)
@Schema(description = "单价")
private BigDecimal amount;
// Integer 类型(整数/排序号)
@Excel(name = "排序", width = 15)
@Schema(description = "排序")
private Integer sortOrder;
// formSchema
{ label: '单价', field: 'amount', component: 'InputNumber',
componentProps: {
placeholder: '请输入',
style: 'width:100%' },
}
// superQuerySchema
amount: { title: '单价', order: 0, view: 'number' }
> 基本的 JDictSelectTag 下拉见"三种字典控件"章节,此处仅列出扩展用法。
// formSchema
{ label: '性别', field: 'sex', component: 'JDictSelectTag',
componentProps: {
dictCode: 'sex',
type: 'radio',
getPopupContainer: () => document.body },
}
// Entity — 存储多个值用逗号分隔
@Excel(name = "颜色多选", width = 15, dicCode = "demo_color")
@Dict(dicCode = "demo_color")
private String demoColor;
// formSchema
{ label: '颜色多选', field: 'demoColor', component: 'JCheckbox',
componentProps: { dictCode: 'demo_color' } }
// superQuerySchema — view 使用 list_multi
demoColor: { title: '颜色多选', order: 0, view: 'list_multi', dictCode: 'demo_color' }
@Excel(name = "字典下拉多选", width = 15, dicCode = "urgent_level")
@Dict(dicCode = "urgent_level")
private String dictMultiSelect;
// formSchema
{ label: '字典下拉多选', field: 'dictMultiSelect', component: 'JSelectMultiple',
componentProps: {
dictCode: 'urgent_level',
triggerChange: true,
getPopupContainer: () => document.body },
}
// superQuerySchema
dictMultiSelect: { title: '字典下拉多选', order: 0, view: 'list_multi', dictCode: 'urgent_level' }
// Controller
customeRuleMap.put("dictMultiSelect", QueryRuleEnum.LIKE_WITH_OR);
@Excel(name = "开关", width = 15, replace = {"是_1", "否_0"})
private String isEnabled;
// columns — 使用 renderSwitch 自定义渲染
{ title: '开关', align: 'center', dataIndex: 'isEnabled',
customRender: ({ text }) => render.renderSwitch(text, [{ text: '是', value: '1' }, { text: '否', value: '0' }]) }
// formSchema(vue3 封装风格)
{ label: '开关', field: 'isEnabled', component: 'JSwitch',
componentProps: { options: ['1', '0'] } }
// superQuerySchema
isEnabled: { title: '开关', order: 0, view: 'radio', dictCode: 'yn' }
vue3Native 原生风格:
// 导入(script setup 中)
import JSwitch from '/@/components/Form/src/jeecg/components/JSwitch.vue';
<!-- 模板中使用,:options 第一个值为 checked,第二个为 unchecked -->
<JSwitch v-model:value="formData.isEnabled" :options="['1', '0']" />
> 注意: vue3Native 中不要使用 <a-switch checkedValue="1">,要用 <JSwitch> 组件,否则值类型不一致导致保存后回显错误。
> 基本的表字典下拉和搜索见"三种字典控件"章节,此处仅列出扩展用法。
@Excel(name = "表字典单选", width = 15, dictTable = "sys_depart", dicText = "depart_name", dicCode = "id")
@Dict(dictTable = "sys_depart", dicText = "depart_name", dicCode = "id")
private String tableDictRadio;
// formSchema
{ label: '表字典单选', field: 'tableDictRadio', component: 'JDictSelectTag',
componentProps: {
dictCode: 'sys_depart,
depart_name,
id',
type: 'radio',
getPopupContainer: () => document.body },
}
@Dict(dictTable = "sys_depart", dicText = "depart_name", dicCode = "id")
private String tableDictCheckbox;
// formSchema
{ label: '表字典多选', field: 'tableDictCheckbox', component: 'JCheckbox',
componentProps: {
dictCode: 'sys_depart,
depart_name,
id' },
}
@Dict(dictTable = "sys_depart", dicText = "depart_name", dicCode = "id")
private String tableDictMultiSelect;
// formSchema
{ label: '表字典下拉多选', field: 'tableDictMultiSelect', component: 'JSelectMultiple',
componentProps: {
dictCode: 'sys_depart,
depart_name,
id',
triggerChange: true,
getPopupContainer: () => document.body },
}
// Entity — dictTable 中直接拼 WHERE 条件
@Excel(name = "表字典带条件", width = 15, dictTable = "sys_user where username like '%a%'", dicText = "realname", dicCode = "username")
@Dict(dictTable = "sys_user where username like '%a%'", dicText = "realname", dicCode = "username")
private String tableDictCondition;
// formSchema — dictCode 字符串中拼条件
{ label: '表字典带条件', field: 'tableDictCondition', component: 'JDictSelectTag',
componentProps: {
dictCode: "sys_user where username like '%a%',
realname,
username",
placeholder: '请选择',
getPopupContainer: () => document.body },
}
> ⚠️ 重要:代码生成器已在 2024-01-02(issue/#5711)将用户选择组件从 JSelectUserByDept 修正为 JSelectUser。生成代码必须使用 JSelectUser,不要再使用旧的 JSelectUserByDept。
@Excel(name = "用户选择", width = 15, dictTable = "sys_user", dicText = "realname", dicCode = "username")
@Dict(dictTable = "sys_user", dicText = "realname", dicCode = "username")
private String userId;
// columns
{ title: '用户选择', align: 'center', dataIndex: 'userId_dictText' }
// formSchema(vue3 封装风格)
{ label: '用户选择', field: 'userId', component: 'JSelectUser',
componentProps: { labelKey: 'realname' } }
// superQuerySchema
userId: { title: '用户选择', order: 0, view: 'sel_user' }
vue3Native 原生风格:
// 导入(script setup 中)
import JSelectUser from '/@/components/Form/src/jeecg/components/JSelectUser.vue';
<!-- 模板中使用 -->
<JSelectUser v-model:value="formData.userId" placeholder="请选择用户" />
@Excel(name = "部门选择", width = 15, dictTable = "sys_depart", dicText = "depart_name", dicCode = "id")
@Dict(dictTable = "sys_depart", dicText = "depart_name", dicCode = "id")
private String deptId;
// columns
{ title: '部门选择', align: 'center', dataIndex: 'deptId_dictText' }
// formSchema
{ label: '部门选择', field: 'deptId', component: 'JSelectDept' }
// superQuerySchema
deptId: { title: '部门选择', order: 0, view: 'sel_depart' }
@Excel(name = "自定义树", width = 15, dictTable = "sys_category", dicText = "name", dicCode = "id")
@Dict(dictTable = "sys_category", dicText = "name", dicCode = "id")
private String treeSelect;
// columns
{ title: '自定义树', align: 'center', dataIndex: 'treeSelect_dictText' }
// formSchema
{ label: '自定义树', field: 'treeSelect', component: 'JTreeSelect',
componentProps: {
dict: 'sys_category,
name,
id',
pidField: 'pid',
pidValue: '0',
multiple: false,
getPopupContainer: () => document.body },
}
// superQuerySchema
treeSelect: { title: '自定义树', order: 0, view: 'sel_search',
dictTable: 'sys_category', dictCode: 'id', dictText: 'name' }
// Controller
customeRuleMap.put("treeSelect", QueryRuleEnum.LIKE_WITH_OR);
@Excel(name = "日期", width = 15, format = "yyyy-MM-dd")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
// columns — 截取前10位防止时间部分显示
{ title: '日期', align: 'center', dataIndex: 'birthday',
customRender: ({ text }) => (!text ? '' : (text.length > 10 ? text.substr(0, 10) : text)) }
// formSchema
{ label: '日期', field: 'birthday', component: 'DatePicker',
componentProps: {
showTime: false,
valueFormat: 'YYYY-MM-DD',
placeholder: '请选择日期',
style: 'width:100%',
getPopupContainer: () => document.body },
}
// superQuerySchema
birthday: { title: '日期', order: 0, view: 'date' }
@Excel(name = "年月日时分秒", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date workTime;
// formSchema
{ label: '年月日时分秒', field: 'workTime', component: 'DatePicker',
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
placeholder: '请选择',
style: 'width:100%',
getPopupContainer: () => document.body },
}
// superQuerySchema
workTime: { title: '年月日时分秒', order: 0, view: 'datetime' }
// 时间类型用 String 存储(如 "14:30:00")
@Excel(name = "时间", width = 15)
private String timeVal;
// formSchema(vue3 封装风格)
{ label: '时间', field: 'timeVal', component: 'TimePicker',
componentProps: {
valueFormat: 'HH:mm:ss',
placeholder: '请选择时间',
style: 'width:100%',
getPopupContainer: () => document.body },
}
vue3Native 原生风格:
// 导入 — 从 ant-design-vue 解构,与 Form 合并在同一行
import { TimePicker, Form } from 'ant-design-vue';
// 如果当前行已有其他 ant-design-vue 导入,直接追加 TimePicker:
// import { TimePicker, RangePicker, Form } from 'ant-design-vue';
<!-- 模板中使用,必须带 value-format 否则返回 dayjs 对象而非字符串 -->
<TimePicker v-model:value="formData.timeVal" value-format="HH:mm:ss"
placeholder="请选择时间" style="width:100%" />
> 注意: vue3Native 中不要使用 <a-time-picker>(全局注册版),要显式导入 TimePicker 并以大驼峰标签使用,与 Form.useForm 的 resetFields 配合才能正确还原默认值。
共同规则: DB 类型均为 date,Java 类型均为 Date,注解统一用 yyyy-MM-dd 格式。前端通过 picker 属性区分。
// Entity — 季度/年/月/周写法完全一致,只改注释
@Excel(name = "季度", width = 15, format = "yyyy-MM-dd")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date quarterVal;
// formSchema — 通过 picker 区分
{ label: '季度', field: 'quarterVal', component: 'DatePicker',
componentProps: {
picker: 'quarter',
valueFormat: 'YYYY-MM-DD',
placeholder: '请选择季度',
style: 'width:100%',
getPopupContainer: () => document.body },
}
{ label: '年', field: 'yearVal', component: 'DatePicker',
componentProps: {
picker: 'year',
valueFormat: 'YYYY-MM-DD',
placeholder: '请选择年',
style: 'width:100%',
getPopupContainer: () => document.body },
}
{ label: '月', field: 'monthVal', component: 'DatePicker',
componentProps: {
picker: 'month',
valueFormat: 'YYYY-MM-DD',
placeholder: '请选择月',
style: 'width:100%',
getPopupContainer: () => document.body },
}
{ label: '周', field: 'weekVal', component: 'DatePicker',
componentProps: {
picker: 'week',
valueFormat: 'YYYY-MM-DD',
placeholder: '请选择周',
style: 'width:100%',
getPopupContainer: () => document.body },
}
// columns — 必须使用 getWeekMonthQuarterYear 翻译(需 import { getWeekMonthQuarterYear } from '/@/utils')
{ title: '季度', dataIndex: 'quarterVal',
customRender: ({ text }) => {
text = !text ? '' : (text.length > 10 ? text.substr(0, 10) : text);
return text ? getWeekMonthQuarterYear(text)['quarter'] : text;
} }
{ title: '年', dataIndex: 'yearVal',
customRender: ({ text }) => {
text = !text ? '' : (text.length > 10 ? text.substr(0, 10) : text);
return text ? getWeekMonthQuarterYear(text)['year'] : text;
} }
// 月用 ['month'],周用 ['week'],写法相同
// ✅ 季度/年/月/周可以作为查询条件,需配合 List.vue 中的 fieldPickers + getDateByPicker(见 searchFormSchema 生成规则)
JPopup、JPopupDict、关联记录三个组件都依赖在线报表 code,code 来源于 onl_cgreport_head 表。生成代码时必须先查询可用报表,再配置到组件中。
查询可用报表 code:
mysql ... -e "SELECT code, name, cgr_sql FROM onl_cgreport_head ORDER BY create_time DESC"
查询报表的字段列表(用于配置 fieldConfig 的 source):
mysql ... -e "SELECT field_name, field_txt FROM onl_cgreport_item WHERE cgrhead_id = (SELECT id FROM onl_cgreport_head WHERE code='报表code') ORDER BY order_num"
配置流程:
onl_cgreport_head 获取所有可用报表 code 和 SQLtestbigdata,需用户数据选 report_user)onl_cgreport_item 获取可用字段名(field_name)code 和 field_name 配置到前端组件的 code、fieldConfig.source、dictCode 中常用报表 code 示例:
| code | 名称 | SQL数据源 | 典型字段 |
|------|------|----------|---------|
| report_user | 统计在线用户 | sys_user | id, username, realname, phone, email |
| testbigdata | 测试大数据 | sys_log | id, log_content, userid, username, ip |
| demo | Report Demo | demo | * |
JPopup 用于弹窗选择记录并回填多个字段值。code 对应 onl_cgreport_head 表中的报表编码。
// Entity — popup 存储选中值,popback 存储回填值
@Excel(name = "popup弹窗", width = 15)
private String popup;
@Excel(name = "popup回填", width = 15)
private String popback;
// ===== vue3 封装风格 formSchema — 通过 formActionType 获取 setFieldsValue =====
{ label: 'Popup弹窗', field: 'popup', component: 'JPopup',
componentProps: ({ formActionType }) => {
const { setFieldsValue } = formActionType;
return {
setFieldsValue,
code: 'report_user', // 默认使用 report_user;日志场景用 testbigdata
fieldConfig: [
{ source: 'username', target: 'popup' }, // 报表字段 → 当前表字段(source=报表列名,target=表单字段名)
{ source: 'realname', target: 'popback' }, // 回填其他字段
],
multi: false,
};
} }
// 他表字段(回填字段)— disabled Input,不可手动编辑
{ label: 'Popup回填', field: 'popback', component: 'Input',
componentProps: {
disabled: true,
placeholder: '由Popup自动回填' },
}
<!-- ===== vue3Native 原生风格 Form.vue 模板写法 ===== -->
<!-- import JPopup from '/@/components/Form/src/jeecg/components/JPopup.vue'; -->
<!-- script 中定义 setFormFieldValue -->
<!-- function setFormFieldValue(values) { Object.assign(formData, values); } -->
<JPopup v-model:value="formData.popup" code="report_user"
:fieldConfig="[{source:'username',target:'popup'},{source:'realname',target:'popback'}]"
:setFieldsValue="setFormFieldValue" :multi="false" />
<a-input v-model:value="formData.popback" :disabled="true" placeholder="由Popup自动回填" />
fieldConfig 配置规则:
Take jeecgboot/jeecg-codegen from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.