en un clic
menu-sync
// Use when: creating system menus for newly generated pages, batch registering menus, or syncing pages.ts entries to the backend menu table. Triggers on: 创建菜单, 注册菜单, 同步菜单, 补菜单, menu sync, create menu, register menu.
// Use when: creating system menus for newly generated pages, batch registering menus, or syncing pages.ts entries to the backend menu table. Triggers on: 创建菜单, 注册菜单, 同步菜单, 补菜单, menu sync, create menu, register menu.
Use when: auditing project source code against the 14 modular standards in .github/standards/. Outputs deviation report and component-extraction suggestions to reports/. Triggers on: 规范审计, 规范检查, 代码审计, 对齐规范, 规范偏差, 接手新项目, 存量代码分析, 项目体检, audit code, check conventions, onboard project.
Use when: analyzing Axure exported HTML prototype files to extract page inventory, classify interaction patterns, identify reusable components, and produce a structured page checklist for Vue development. Also supports NON-standard detailed design documents (free-form MD/Word) or natural language descriptions as input. DO NOT use for standard requirement-spec documents produced by wl-skills-design (path contains docs/spec/, has function codes like PMMB001, IPO tables, flow five-elements) — route those to spec-doc-parse instead. Triggers on: prototype analysis, axure scan, page inventory, 原型解析, 页面清单, axure转vue, 口述需求, 自然语言建页面, natural language page request, 建个页面, 写个页面, 口头描述页面.
Use when: parsing STANDARD requirement-design specification documents produced by wl-skills-design (path contains docs/spec/, files like ch1-3.md / 4.x-{module}.md / 4.N-data-report.md, with function codes like PMMB001, IPO tables containing 处理逻辑, and flow descriptions with five-elements) into the SAME page-spec JSON consumed by page-codegen. This is the 规范线 (spec line) counterpart to prototype-scan (原型线). Self-validating, self-correcting, report-producing. Triggers on: 解析说明书, 解析需求文档, 解析详设, 规范文档转页面, 根据说明书生成, spec doc parse, IPO 转页面, docs/spec, 功能编码, 需求设计说明书.
Use when: generating complete Vue 3 page code (index.vue + data.ts + modal components + api.md + pages.ts registration) from a prototype page inventory and API contract, strictly following the cx-ui-produce project conventions. Read SKILL.md first (rules+constraints), then read the matching TPL-*.md for the template code. Triggers on: generate page, create page, code generation, 生成页面, 页面代码, 代码生成, vue页面, 按原型生成, 口述需求, 建个页面, 写个页面, 帮我生成, natural language page generation.
Use when: fixing code convention issues found in convention-audit reports. Triggers on: 自动修复, 整改偏差, 修复报告, 规范整改, 修复偏差, code fix, 整改规范.
Use when: managing roles, authorizing menus to roles, attaching action buttons (type=A) to page menus, or wiring permission field in data.ts toolbar config. Triggers on: 创建角色, 角色管理, 角色授权, 给角色分配菜单, 挂动作, 添加动作按钮, 同步权限, 权限码注册, role assign, permission sync.
| name | menu-sync |
| description | Use when: creating system menus for newly generated pages, batch registering menus, or syncing pages.ts entries to the backend menu table. Triggers on: 创建菜单, 注册菜单, 同步菜单, 补菜单, menu sync, create menu, register menu. |
将 pages.ts 中注册的页面同步到后端菜单表,使系统能够路由到新页面。
背景:本项目是 Module Federation 子应用,页面在
pages.ts注册后, 还需要在后端菜单表中创建对应记录,系统才能路由到该页面。 设计文档:.github/guides/architecture.md
| 数据 | 来源 | 说明 |
|---|---|---|
| 菜单名称、路径、组件、权限、隐藏、排序、应用编码 | .github/reports/SYS_MENU_INFO*.md | 由 page-codegen 追加写入,AI 直接读取 |
parentMenuNameCode | wls_menu_query 查询菜单树 | 从父级节点获取,无需手填 |
| gatewayPath、parentMenuId、sysAppNo、token | env.local.json | 可通过 token/接口辅助提取 |
| domainId | 用户确认 / 菜单后台 Network | 当前权限下无法总是自动获取,需确认 |
优先读取 .github/skills/sync/env.local.json(v2.1.5+ 统一配置):
{
"gatewayPath": "http://网关地址:端口",
"sysAppNo": "应用编码(从已有菜单的sysAppNo字段获取,非明文)",
"token": "Bearer Token(不含bearer前缀)",
"menu": {
"parentMenuId": "父级菜单ID",
"domainId": "应用域ID"
}
}
向下兼容:如上述文件不存在,回落读取
menu-sync/env/env.local.json(旧版路径)。
字段说明及获取方式见env/guide.md
menu-sync 读取规则:parentMenuId 优先从 menu.parentMenuId 读取,回落到根级 parentMenuId。
env/guide.md 填写 skills/sync/env.local.json 的字段.github/reports/SYS_MENU_INFO*.md → 读 env.local.json → 优先调用 wls_menu_sync_from_report 一步完成确定性同步;如需手动拆分,则 wls_menu_query 查 domain 菜单树 → 先 upsert 一级目录 → 用返回 id upsert 二级页面菜单 → 输出 created/updated/skipped 结果表SYS_MENU_INFO.md 是 menu-sync Skill 的输入数据源:
wls_menu_sync_from_report 读取 SYS_MENU_INFO.md → 调 API 按目录与页面顺序创建/更新组件路径 字段与 pages.ts 注册的文件路径关联wls_menu_sync_from_report;手动拆分时必须先调用 wls_menu_query 获取当前 domain 菜单树,再 wls_menu_upsert 创建/更新一级目录(type=M),拿到目录 id 后再创建二级菜单(type=C)。不得把二级页面全部直接挂到根 parentMenuId。| 阶段 | 方案 | 状态 | 说明 |
|---|---|---|---|
| Phase 1 | AI 调用现有 API 逐条创建 | ✅ 当前可用 | 利用 /system/menu/save 接口,AI 充当自动化脚本 |
| Phase 2 | 前端推送脚本 pnpm run menu:push | ⏳ 待后端接口 | 需后端提供 POST /system/menu/batchPush upsert 接口 |
本质是 menu-sync-design.md 的方案 C(只增不删),由 AI 调 MCP 工具自动执行。
📖 必读公共护栏:
../_mcp-guardrail.md该文件定义了 MCP 调用纪律、错误分层判定、自愈闭环剧本。AI 首次执行 sync 类任务时必须先read_file加载它。本 Skill 使用的 MCP 工具:
wls_menu_sync_from_report/wls_menu_query/wls_menu_upsert。工具不可用或调用失败时,按 guardrail §2 剧本引导用户完善env.local.json后重试,不得用 curl/手拼 HTTP 绕开 MCP。
wls_menu_sync_from_report).github/skills/sync/env.local.json 已填写 token(纯 JWT,不含 bearer 前缀)、gatewayPath、sysAppNo、menu.parentMenuId、menu.domainId用户提供以下信息(或 AI 从 pages.ts 自动提取):
menuId(后端菜单树中的上级目录 ID)sysAppNo(如 produce、sale、system)菜单类型:
| type | 含义 | 必填字段 |
|---|---|---|
M | 目录 | menuName, path |
C | 菜单(页面) | menuName, path, permission, component |
A | 动作按钮 | menuName, path |
默认走 wls_menu_sync_from_report——它内部完成「读报告 → 查菜单树 → 一级目录 upsert → 二级菜单 upsert」全流程:
工具:wls_menu_sync_from_report
入参:{ dryRun?: boolean, reportPath?: string } // 不传 reportPath 自动用最新 SYS_MENU_INFO*.md
第一次执行:先传 dryRun: true 预览,确认无误后再正式执行(去掉 dryRun)
工具:wls_menu_query (无参,自动读 env.local.json → menu.domainId)
返回:当前应用域完整菜单树
menuName/path 在父级 parentMenuId 下去重;不存在则创建,存在则复用 idparentId 必须是上一步对应目录的 id,禁止全部挂到根 parentMenuId工具:wls_menu_upsert
入参:{ items: [<下面的对象>...] }
items[] 单条对象模板(仅作为 MCP 入参参考,禁止 AI 自行 fetch):
{
"useCache": 1,
"icon": "list",
"common": 2,
"hidden": false,
"type": "C", // "M"=目录, "C"=菜单, "A"=动作
"parentId": "{父级目录的 id}",
"sysAppNo": "{sysAppNo}",
"orderNum": 1,
"menuName": "客户档案",
"menuNameCode": "{parentMenuNameCode}:{pinyinName}",
"path": "mmwrCustomerArchive",
"permission": "mmwrCustomerArchive",
"component": "produce/production-mmwr/aiflow/mmwr-customer-archive/index.vue"
}
MCP 内部说明(AI 不可据此自行调接口):底层走
POST /system/menu/save,成功码code: 2000。
创建完成后输出结果表格:
| 菜单名 | path | type | 状态 | id |
|---|---|---|---|---|
| 客户档案 | mmwrCustomerArchive | C | ✅ created | 123456 |
| 客户详情 | mmwrCustomerDetail | C | ⏭️ skipped (已存在) | - |
| 字段 | 规则 |
|---|---|
menuName | 取 pages.ts 的 label |
path | 页面目录名转 camelCase(如 mmwr-customer-archive → mmwrCustomerArchive) |
component | 取 pages.ts 的 name(如 produce/production-mmwr/aiflow/mmwr-customer-archive/index.vue) |
permission | {域}:{path}:list(如 produce:mmwrCustomerArchive:list) |
menuNameCode | {父级menuNameCode}:{菜单名拼音}(小写连续拼接) |
hidden | 表单页/详情页等隐藏路由设为 true,菜单可见页面设为 false |
orderNum | 从父级已有菜单最大 orderNum + 1 开始递增 |
icon | 目录级 "list",菜单级 "list" |
以 aiflow 子模块为例:
pages.ts 条目:
["mmwr-customer-archive", "客户档案"]
→ 菜单数据:
{
type: "C",
menuName: "客户档案",
path: "mmwrCustomerArchive",
component: "produce/production-mmwr/aiflow/mmwr-customer-archive/index.vue",
permission: "produce:mmwrCustomerArchive:list",
hidden: false
}
隐藏页面示例:
["mmwr-customer-apply-add-form", "客户申请新增表单"]
→ 菜单数据:
{
type: "C",
menuName: "客户申请新增表单",
path: "mmwrCustomerApplyAddForm",
component: "produce/production-mmwr/aiflow/mmwr-customer-apply-add-form/index.vue",
permission: "produce:mmwrCustomerApplyAddForm:list",
hidden: true // ← 表单页隐藏
}
以下页面类型应设置 hidden: true:
-form(独立路由表单页)-detail(详情页)-history(历史查询页)menuName → 跳过path → 跳过如果 SYS_MENU_INFO 包含一级目录和二级菜单,必须按以下顺序处理:
type: "M" 创建目录data.idid 作为 parentId 继续创建子菜单对应 menu-sync-design.md 的 方案 D(推送覆盖),是最终目标方案。
| 维度 | Phase 1 (AI 调 API) | Phase 2 (推送脚本) |
|---|---|---|
| 执行者 | AI | pnpm run menu:push 脚本 |
| 更新能力 | 只增不删(方案 C) | upsert 覆盖(方案 D) |
| 改名支持 | ❌ 会产生重复 | ✅ 按 componentPath 匹配更新 |
| 权限影响 | 无 | 无(只写结构字段) |
| 依赖 | Token + 网关地址 | 后端 batchPush 接口 |
POST /system/menu/batchPush
componentPath 做 upsert(存在则更新结构字段,不存在则新增).github/guides/architecture.md在 SharedPageItem 中增加 menuMeta 字段:
export interface SharedPageItem {
name: string;
label: string;
menuMeta?: {
parentName: string;
menuPath: string;
sortOrder: number;
appCode: string;
hidden?: boolean;
icon?: string;
};
}
batchPush 接口menuMetascripts/menu-push.ts(vite-node 执行)pnpm run menu:push 命令menuMeta