@agile-team/wl-skills-kit 1.2.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +120 -0
- package/README.md +169 -366
- package/bin/wl-skills.js +149 -43
- package/files/.github/copilot-instructions.md +104 -43
- package/files/.github/guides/README.md +13 -0
- package/files/.github/guides/architecture.md +555 -0
- package/files/.github/guides/usage.md +166 -0
- package/files/.github/reports/README.md +65 -0
- package/files/.github/reports/SYS_DICT_INFO.md +19 -0
- package/files/.github/reports/SYS_PERMISSION_INFO.md +20 -0
- package/files/.github/reports//347/273/204/344/273/266/346/217/220/345/217/226/345/273/272/350/256/256.md +33 -0
- package/files/.github/reports//350/247/204/350/214/203/345/256/241/346/237/245/346/212/245/345/221/212.md +44 -0
- package/files/.github/skills/_compat/README.md +108 -0
- package/files/.github/skills/_compat/editors.json +61 -0
- package/files/.github/skills/_compat/headers/agents.txt +8 -0
- package/files/.github/skills/_compat/headers/claude-code.txt +7 -0
- package/files/.github/skills/_compat/headers/cline.txt +7 -0
- package/files/.github/skills/_compat/headers/cursor-mdc.txt +16 -0
- package/files/.github/skills/_compat/headers/cursor-rules.txt +7 -0
- package/files/.github/skills/_compat/headers/github-copilot.txt +1 -0
- package/files/.github/skills/_compat/headers/kiro.txt +10 -0
- package/files/.github/skills/_compat/headers/trae.txt +11 -0
- package/files/.github/skills/_compat/headers/windsurf.txt +7 -0
- package/files/.github/skills/_registry.md +81 -0
- package/files/.github/skills/{api-contract → core/api-contract}/SKILL.md +126 -29
- package/files/.github/skills/core/api-contract/USAGE.md +110 -0
- package/files/.github/skills/core/convention-audit/SKILL.md +189 -0
- package/files/.github/skills/core/convention-audit/USAGE.md +99 -0
- package/files/.github/skills/{page-codegen → core/page-codegen}/SKILL.md +64 -21
- package/files/.github/skills/core/page-codegen/USAGE.md +102 -0
- package/files/.github/skills/core/page-codegen/templates/_index.md +46 -0
- package/files/.github/skills/core/page-codegen/templates/domains/_CONTRIBUTING.md +107 -0
- package/files/.github/skills/{page-codegen → core/page-codegen/templates/domains/produce}/TPL-OPERATION-STATION.md +442 -442
- package/files/.github/skills/core/page-codegen/templates/domains/sale/README.md +26 -0
- package/files/.github/skills/{page-codegen → core/page-codegen/templates/universal}/TPL-DETAIL-TABS.md +94 -39
- package/files/.github/skills/{page-codegen → core/page-codegen/templates/universal}/TPL-DRIVEN.md +124 -124
- package/files/.github/skills/core/prototype-scan/USAGE.md +95 -0
- package/files/.github/skills/core/template-extract/SKILL.md +139 -0
- package/files/.github/skills/core/template-extract/USAGE.md +93 -0
- package/files/.github/skills/domain/README.md +51 -0
- package/files/.github/skills/ops/code-fix/SKILL.draft.md +108 -0
- package/files/.github/skills/sync/dict-sync/SKILL.draft.md +100 -0
- package/files/.github/skills/{menu-sync → sync/menu-sync}/SKILL.md +258 -258
- package/files/.github/skills/sync/menu-sync/USAGE.md +104 -0
- package/files/.github/skills/{menu-sync → sync/menu-sync}/env/guide.md +83 -83
- package/files/.github/skills/sync/permission-sync/SKILL.draft.md +91 -0
- package/files/.github/standards/01-toolchain.md +57 -0
- package/files/.github/standards/02-code-structure.md +111 -0
- package/files/.github/standards/03-comments.md +53 -0
- package/files/.github/standards/04-coding-basics.md +33 -0
- package/files/.github/standards/05-logging.md +38 -0
- package/files/.github/standards/06-security.md +44 -0
- package/files/.github/standards/07-config.md +52 -0
- package/files/.github/standards/08-git.md +60 -0
- package/files/.github/standards/09-typescript.md +71 -0
- package/files/.github/standards/10-pinia.md +57 -0
- package/files/.github/standards/11-form-validation.md +81 -0
- package/files/.github/standards/12-base-table.md +116 -0
- package/files/.github/standards/13-platform-components.md +123 -0
- package/files/.github/standards/index.md +89 -0
- package/files/demo/produce/aiflow/mmwr-customer-apply-change-history/data.ts +196 -196
- package/files/demo/produce/aiflow/mmwr-customer-apply-change-history/index.scss +150 -150
- package/files/demo/produce/aiflow/mmwr-customer-apply-change-history/index.vue +79 -79
- package/files/docs/jh-date-range.md +257 -257
- package/files/docs/jh-date.md +222 -222
- package/files/docs/jh-dept-picker.md +190 -190
- package/files/docs/jh-drag-row.md +590 -590
- package/files/docs/jh-file-upload.md +216 -216
- package/files/docs/jh-picker.md +218 -218
- package/files/docs/jh-select.md +148 -148
- package/files/docs/jh-text.md +248 -248
- package/files/docs/jh-user-picker.md +197 -197
- package/package.json +4 -1
- package/files/.github/docs/menu-sync-design.md +0 -264
- package/files/.github/docs/use-skill.md +0 -382
- package/files/.github/docs/wl-skills-kit.md +0 -266
- package/files/.github/skills/convention-extract/SKILL.md +0 -236
- /package/files/.github/{docs → reports}/SYS_MENU_INFO.md +0 -0
- /package/files/.github/skills/{page-codegen → core/page-codegen/templates/universal}/TPL-CHANGE-HISTORY.md +0 -0
- /package/files/.github/skills/{page-codegen → core/page-codegen/templates/universal}/TPL-FORM-ROUTE.md +0 -0
- /package/files/.github/skills/{page-codegen → core/page-codegen/templates/universal}/TPL-LIST.md +0 -0
- /package/files/.github/skills/{page-codegen → core/page-codegen/templates/universal}/TPL-MASTER-DETAIL.md +0 -0
- /package/files/.github/skills/{page-codegen → core/page-codegen/templates/universal}/TPL-RECORD-FORM.md +0 -0
- /package/files/.github/skills/{page-codegen → core/page-codegen/templates/universal}/TPL-TREE-LIST.md +0 -0
- /package/files/.github/skills/{prototype-scan → core/prototype-scan}/SKILL.md +0 -0
- /package/files/.github/skills/{menu-sync → sync/menu-sync}/env/env.local.json +0 -0
|
@@ -1,258 +1,258 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: menu-sync
|
|
3
|
-
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."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Skill: 菜单同步(menu-sync)
|
|
7
|
-
|
|
8
|
-
将 pages.ts 中注册的页面同步到后端菜单表,使系统能够路由到新页面。
|
|
9
|
-
|
|
10
|
-
> **背景**:本项目是 Module Federation 子应用,页面在 `pages.ts` 注册后,
|
|
11
|
-
> 还需要在后端菜单表中创建对应记录,系统才能路由到该页面。
|
|
12
|
-
> 设计文档:`.github/
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 配置与使用方式(一次配置,无需反复填写)
|
|
17
|
-
|
|
18
|
-
### 数据来源分工
|
|
19
|
-
|
|
20
|
-
| 数据 | 来源
|
|
21
|
-
| ------------------------------------------------ |
|
|
22
|
-
| 菜单名称、路径、组件、权限、隐藏、排序、应用编码 | `SYS_MENU_INFO.md` |
|
|
23
|
-
| `parentMenuNameCode` | API 自动查询
|
|
24
|
-
| **gatewayPath、parentMenuId、sysAppNo、token**
|
|
25
|
-
|
|
26
|
-
### 配置文件(只需填 4 个字段)
|
|
27
|
-
|
|
28
|
-
`.github/skills/menu-sync/env/env.local.json`(已加入 `.gitignore`,本地维护,不提交)
|
|
29
|
-
|
|
30
|
-
```json
|
|
31
|
-
{
|
|
32
|
-
"gatewayPath": "http://网关地址:端口",
|
|
33
|
-
"parentMenuId": "父级菜单ID",
|
|
34
|
-
"sysAppNo": "应用编码(从已有菜单的sysAppNo字段获取,非明文)",
|
|
35
|
-
"token": "Bearer Token(不含bearer前缀)"
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
> 字段说明及获取方式见 `env/guide.md`
|
|
40
|
-
|
|
41
|
-
### 使用步骤
|
|
42
|
-
|
|
43
|
-
1. **首次**:按 `env/guide.md` 填写 `env.local.json` 的 4 个字段
|
|
44
|
-
2. **之后**:直接对 AI 说「帮我创建菜单」/「同步菜单」/「补菜单」
|
|
45
|
-
3. AI 自动执行:读 `SYS_MENU_INFO.md` → 读 `env.local.json` → 查父级已有子节点 → 逐条对比去重 → 调 `/system/menu/save` → 输出 created/skipped 结果表
|
|
46
|
-
4. **全程无需手动执行任何命令**
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## 方案演进路线
|
|
51
|
-
|
|
52
|
-
| 阶段 | 方案 | 状态 | 说明 |
|
|
53
|
-
| ----------- | --------------------------------- | ------------- | ---------------------------------------------------- |
|
|
54
|
-
| **Phase 1** | AI 调用现有 API 逐条创建 | ✅ 当前可用 | 利用 `/system/menu/save` 接口,AI 充当自动化脚本 |
|
|
55
|
-
| **Phase 2** | 前端推送脚本 `pnpm run menu:push` | ⏳ 待后端接口 | 需后端提供 `POST /system/menu/batchPush` upsert 接口 |
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## Phase 1:AI 调用 API 创建菜单(当前方案)
|
|
60
|
-
|
|
61
|
-
> 此方案参考平台伙伴的 jh4j-cloud skill,适配 cx-ui-produce 项目。
|
|
62
|
-
> 本质是 **menu-sync-design.md 的方案 C(只增不删)**,由 AI 自动执行。
|
|
63
|
-
|
|
64
|
-
### 前置条件
|
|
65
|
-
|
|
66
|
-
1. 用户提供后端系统管理地址(如 `http://localhost:9000` 或实际网关地址)
|
|
67
|
-
2. 用户提供有效的 Bearer Token(从浏览器开发者工具 Network 面板复制)
|
|
68
|
-
3. 用户提供父级菜单 ID(`menuId`),可通过查询接口获取
|
|
69
|
-
|
|
70
|
-
### 输入
|
|
71
|
-
|
|
72
|
-
用户提供以下信息(或 AI 从 pages.ts 自动提取):
|
|
73
|
-
|
|
74
|
-
- **父级菜单 ID**:`menuId`(后端菜单树中的上级目录 ID)
|
|
75
|
-
- **应用编码**:`sysAppNo`(如 `produce`、`sale`、`system`)
|
|
76
|
-
- **菜单数据**:可以是 pages.ts 中的条目,或用户手动指定
|
|
77
|
-
|
|
78
|
-
菜单类型:
|
|
79
|
-
|
|
80
|
-
| type | 含义 | 必填字段 |
|
|
81
|
-
| ---- | ------------ | --------------------------------------------- |
|
|
82
|
-
| `M` | 目录 | `menuName`, `path` |
|
|
83
|
-
| `C` | 菜单(页面) | `menuName`, `path`, `permission`, `component` |
|
|
84
|
-
| `A` | 动作按钮 | `menuName`, `path` |
|
|
85
|
-
|
|
86
|
-
### 执行流程
|
|
87
|
-
|
|
88
|
-
#### Step 1: 查询父级下已有菜单(防重复)
|
|
89
|
-
|
|
90
|
-
```
|
|
91
|
-
GET {gatewayPath}/system/menu/children?current=1&size=100&menuId={parentMenuId}
|
|
92
|
-
Headers:
|
|
93
|
-
authorization: bearer {token}
|
|
94
|
-
Sysappno: {sysAppNo}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
#### Step 2: 逐条创建菜单
|
|
98
|
-
|
|
99
|
-
对于每条待创建的菜单,先检查是否与已有菜单重名(`menuName` 或 `path` 相同),重复则跳过。
|
|
100
|
-
|
|
101
|
-
> **响应码说明**:后端成功响应为 `code: 2000`(非标准 HTTP 200),判断成功应检查 `response.body.code === 2000` 或 `message` 包含"成功"。
|
|
102
|
-
|
|
103
|
-
```
|
|
104
|
-
POST {gatewayPath}/system/menu/save
|
|
105
|
-
Headers:
|
|
106
|
-
authorization: bearer {token}
|
|
107
|
-
Sysappno: {sysAppNo}
|
|
108
|
-
Content-Type: application/json
|
|
109
|
-
|
|
110
|
-
Body:
|
|
111
|
-
{
|
|
112
|
-
"useCache": 1,
|
|
113
|
-
"icon": "list",
|
|
114
|
-
"common": 2,
|
|
115
|
-
"hidden": false,
|
|
116
|
-
"type": "C",
|
|
117
|
-
"parentId": "{parentMenuId}",
|
|
118
|
-
"sysAppNo": "{sysAppNo}",
|
|
119
|
-
"orderNum": {nextOrder},
|
|
120
|
-
"menuName": "客户档案",
|
|
121
|
-
"menuNameCode": "{parentMenuNameCode}:{pinyinName}",
|
|
122
|
-
"path": "mmwrCustomerArchive",
|
|
123
|
-
"permission": "mmwrCustomerArchive",
|
|
124
|
-
"component": "produce/production-mmwr/aiflow/mmwr-customer-archive/index.vue"
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
#### Step 3: 记录结果
|
|
129
|
-
|
|
130
|
-
创建完成后输出结果表格:
|
|
131
|
-
|
|
132
|
-
| 菜单名 | path | type | 状态 | id |
|
|
133
|
-
| -------- | ------------------- | ---- | ------------------- | ------ |
|
|
134
|
-
| 客户档案 | mmwrCustomerArchive | C | ✅ created | 123456 |
|
|
135
|
-
| 客户详情 | mmwrCustomerDetail | C | ⏭️ skipped (已存在) | - |
|
|
136
|
-
|
|
137
|
-
### 字段生成规则
|
|
138
|
-
|
|
139
|
-
| 字段 | 规则 |
|
|
140
|
-
| -------------- | -------------------------------------------------------------------------------------------- |
|
|
141
|
-
| `menuName` | 取 pages.ts 的 `label` |
|
|
142
|
-
| `path` | 页面目录名转 camelCase(如 `mmwr-customer-archive` → `mmwrCustomerArchive`) |
|
|
143
|
-
| `component` | 取 pages.ts 的 `name`(如 `produce/production-mmwr/aiflow/mmwr-customer-archive/index.vue`) |
|
|
144
|
-
| `permission` | `{域}:{path}:list`(如 `produce:mmwrCustomerArchive:list`) |
|
|
145
|
-
| `menuNameCode` | `{父级menuNameCode}:{菜单名拼音}`(小写连续拼接) |
|
|
146
|
-
| `hidden` | 表单页/详情页等隐藏路由设为 `true`,菜单可见页面设为 `false` |
|
|
147
|
-
| `orderNum` | 从父级已有菜单最大 orderNum + 1 开始递增 |
|
|
148
|
-
| `icon` | 目录级 `"list"`,菜单级 `"list"` |
|
|
149
|
-
|
|
150
|
-
### pages.ts → 菜单数据映射示例
|
|
151
|
-
|
|
152
|
-
以 `aiflow` 子模块为例:
|
|
153
|
-
|
|
154
|
-
```
|
|
155
|
-
pages.ts 条目:
|
|
156
|
-
["mmwr-customer-archive", "客户档案"]
|
|
157
|
-
|
|
158
|
-
→ 菜单数据:
|
|
159
|
-
{
|
|
160
|
-
type: "C",
|
|
161
|
-
menuName: "客户档案",
|
|
162
|
-
path: "mmwrCustomerArchive",
|
|
163
|
-
component: "produce/production-mmwr/aiflow/mmwr-customer-archive/index.vue",
|
|
164
|
-
permission: "produce:mmwrCustomerArchive:list",
|
|
165
|
-
hidden: false
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
隐藏页面示例:
|
|
169
|
-
["mmwr-customer-apply-add-form", "客户申请新增表单"]
|
|
170
|
-
|
|
171
|
-
→ 菜单数据:
|
|
172
|
-
{
|
|
173
|
-
type: "C",
|
|
174
|
-
menuName: "客户申请新增表单",
|
|
175
|
-
path: "mmwrCustomerApplyAddForm",
|
|
176
|
-
component: "produce/production-mmwr/aiflow/mmwr-customer-apply-add-form/index.vue",
|
|
177
|
-
permission: "produce:mmwrCustomerApplyAddForm:list",
|
|
178
|
-
hidden: true // ← 表单页隐藏
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### 隐藏页面判断规则
|
|
183
|
-
|
|
184
|
-
以下页面类型应设置 `hidden: true`:
|
|
185
|
-
|
|
186
|
-
- 目录名含 `-form`(独立路由表单页)
|
|
187
|
-
- 目录名含 `-detail`(详情页)
|
|
188
|
-
- 目录名含 `-history`(历史查询页)
|
|
189
|
-
- SYS_MENU_INFO.md 中标注为"隐藏菜单"的页面
|
|
190
|
-
|
|
191
|
-
### 跳过规则
|
|
192
|
-
|
|
193
|
-
- 当前父级下已有同名 `menuName` → 跳过
|
|
194
|
-
- 当前父级下已有相同 `path` → 跳过
|
|
195
|
-
- 跳过时不做更新、不做覆盖,只记录"已跳过"
|
|
196
|
-
|
|
197
|
-
### 树形菜单处理
|
|
198
|
-
|
|
199
|
-
如果需要先创建目录再创建子菜单:
|
|
200
|
-
|
|
201
|
-
1. 先以 `type: "M"` 创建目录
|
|
202
|
-
2. 保存成功后取返回的 `data.id`
|
|
203
|
-
3. 以新 `id` 作为 `parentId` 继续创建子菜单
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
|
|
207
|
-
## Phase 2:推送脚本方案(待后端接口就绪后启用)
|
|
208
|
-
|
|
209
|
-
> 对应 menu-sync-design.md 的 **方案 D(推送覆盖)**,是最终目标方案。
|
|
210
|
-
|
|
211
|
-
### 与 Phase 1 的差异
|
|
212
|
-
|
|
213
|
-
| 维度 | Phase 1 (AI 调 API) | Phase 2 (推送脚本) |
|
|
214
|
-
| -------- | ------------------- | ---------------------------- |
|
|
215
|
-
| 执行者 | AI | `pnpm run menu:push` 脚本 |
|
|
216
|
-
| 更新能力 | 只增不删(方案 C) | upsert 覆盖(方案 D) |
|
|
217
|
-
| 改名支持 | ❌ 会产生重复 | ✅ 按 componentPath 匹配更新 |
|
|
218
|
-
| 权限影响 | 无 | 无(只写结构字段) |
|
|
219
|
-
| 依赖 | Token + 网关地址 | 后端 `batchPush` 接口 |
|
|
220
|
-
|
|
221
|
-
### 所需后端接口
|
|
222
|
-
|
|
223
|
-
```
|
|
224
|
-
POST /system/menu/batchPush
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
- 按 `componentPath` 做 upsert(存在则更新结构字段,不存在则新增)
|
|
228
|
-
- **不删除**后端多余的菜单
|
|
229
|
-
- **不碰**权限/角色绑定字段
|
|
230
|
-
- 详见 `.github/
|
|
231
|
-
|
|
232
|
-
### pages.ts 扩展
|
|
233
|
-
|
|
234
|
-
在 `SharedPageItem` 中增加 `menuMeta` 字段:
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
export interface SharedPageItem {
|
|
238
|
-
name: string;
|
|
239
|
-
label: string;
|
|
240
|
-
menuMeta?: {
|
|
241
|
-
parentName: string;
|
|
242
|
-
menuPath: string;
|
|
243
|
-
sortOrder: number;
|
|
244
|
-
appCode: string;
|
|
245
|
-
hidden?: boolean;
|
|
246
|
-
icon?: string;
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### 切换步骤
|
|
252
|
-
|
|
253
|
-
1. 后端提供 `batchPush` 接口
|
|
254
|
-
2. 在 pages.ts 的页面条目中补充 `menuMeta`
|
|
255
|
-
3. 创建 `scripts/menu-push.ts`(vite-node 执行)
|
|
256
|
-
4. 注册 `pnpm run menu:push` 命令
|
|
257
|
-
5. page-codegen Skill 生成页面时自动填充 `menuMeta`
|
|
258
|
-
6. 废弃 Phase 1 的 AI 手动调 API 流程
|
|
1
|
+
---
|
|
2
|
+
name: menu-sync
|
|
3
|
+
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."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: 菜单同步(menu-sync)
|
|
7
|
+
|
|
8
|
+
将 pages.ts 中注册的页面同步到后端菜单表,使系统能够路由到新页面。
|
|
9
|
+
|
|
10
|
+
> **背景**:本项目是 Module Federation 子应用,页面在 `pages.ts` 注册后,
|
|
11
|
+
> 还需要在后端菜单表中创建对应记录,系统才能路由到该页面。
|
|
12
|
+
> 设计文档:`.github/guides/architecture.md`
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 配置与使用方式(一次配置,无需反复填写)
|
|
17
|
+
|
|
18
|
+
### 数据来源分工
|
|
19
|
+
|
|
20
|
+
| 数据 | 来源 | 说明 |
|
|
21
|
+
| ------------------------------------------------ | -------------------------- | --------------------------------------- |
|
|
22
|
+
| 菜单名称、路径、组件、权限、隐藏、排序、应用编码 | `reports/SYS_MENU_INFO.md` | 由 page-codegen 追加写入,AI 直接读取 |
|
|
23
|
+
| `parentMenuNameCode` | API 自动查询 | AI 调 children 接口获取,无需手填 |
|
|
24
|
+
| **gatewayPath、parentMenuId、sysAppNo、token** | `env.local.json` | 每套环境不同,唯一需要手动维护的 4 个值 |
|
|
25
|
+
|
|
26
|
+
### 配置文件(只需填 4 个字段)
|
|
27
|
+
|
|
28
|
+
`.github/skills/sync/menu-sync/env/env.local.json`(已加入 `.gitignore`,本地维护,不提交)
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"gatewayPath": "http://网关地址:端口",
|
|
33
|
+
"parentMenuId": "父级菜单ID",
|
|
34
|
+
"sysAppNo": "应用编码(从已有菜单的sysAppNo字段获取,非明文)",
|
|
35
|
+
"token": "Bearer Token(不含bearer前缀)"
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> 字段说明及获取方式见 `env/guide.md`
|
|
40
|
+
|
|
41
|
+
### 使用步骤
|
|
42
|
+
|
|
43
|
+
1. **首次**:按 `env/guide.md` 填写 `env.local.json` 的 4 个字段
|
|
44
|
+
2. **之后**:直接对 AI 说「帮我创建菜单」/「同步菜单」/「补菜单」
|
|
45
|
+
3. AI 自动执行:读 `reports/SYS_MENU_INFO.md` → 读 `env.local.json` → 查父级已有子节点 → 逐条对比去重 → 调 `/system/menu/save` → 输出 created/skipped 结果表
|
|
46
|
+
4. **全程无需手动执行任何命令**
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 方案演进路线
|
|
51
|
+
|
|
52
|
+
| 阶段 | 方案 | 状态 | 说明 |
|
|
53
|
+
| ----------- | --------------------------------- | ------------- | ---------------------------------------------------- |
|
|
54
|
+
| **Phase 1** | AI 调用现有 API 逐条创建 | ✅ 当前可用 | 利用 `/system/menu/save` 接口,AI 充当自动化脚本 |
|
|
55
|
+
| **Phase 2** | 前端推送脚本 `pnpm run menu:push` | ⏳ 待后端接口 | 需后端提供 `POST /system/menu/batchPush` upsert 接口 |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Phase 1:AI 调用 API 创建菜单(当前方案)
|
|
60
|
+
|
|
61
|
+
> 此方案参考平台伙伴的 jh4j-cloud skill,适配 cx-ui-produce 项目。
|
|
62
|
+
> 本质是 **menu-sync-design.md 的方案 C(只增不删)**,由 AI 自动执行。
|
|
63
|
+
|
|
64
|
+
### 前置条件
|
|
65
|
+
|
|
66
|
+
1. 用户提供后端系统管理地址(如 `http://localhost:9000` 或实际网关地址)
|
|
67
|
+
2. 用户提供有效的 Bearer Token(从浏览器开发者工具 Network 面板复制)
|
|
68
|
+
3. 用户提供父级菜单 ID(`menuId`),可通过查询接口获取
|
|
69
|
+
|
|
70
|
+
### 输入
|
|
71
|
+
|
|
72
|
+
用户提供以下信息(或 AI 从 pages.ts 自动提取):
|
|
73
|
+
|
|
74
|
+
- **父级菜单 ID**:`menuId`(后端菜单树中的上级目录 ID)
|
|
75
|
+
- **应用编码**:`sysAppNo`(如 `produce`、`sale`、`system`)
|
|
76
|
+
- **菜单数据**:可以是 pages.ts 中的条目,或用户手动指定
|
|
77
|
+
|
|
78
|
+
菜单类型:
|
|
79
|
+
|
|
80
|
+
| type | 含义 | 必填字段 |
|
|
81
|
+
| ---- | ------------ | --------------------------------------------- |
|
|
82
|
+
| `M` | 目录 | `menuName`, `path` |
|
|
83
|
+
| `C` | 菜单(页面) | `menuName`, `path`, `permission`, `component` |
|
|
84
|
+
| `A` | 动作按钮 | `menuName`, `path` |
|
|
85
|
+
|
|
86
|
+
### 执行流程
|
|
87
|
+
|
|
88
|
+
#### Step 1: 查询父级下已有菜单(防重复)
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
GET {gatewayPath}/system/menu/children?current=1&size=100&menuId={parentMenuId}
|
|
92
|
+
Headers:
|
|
93
|
+
authorization: bearer {token}
|
|
94
|
+
Sysappno: {sysAppNo}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Step 2: 逐条创建菜单
|
|
98
|
+
|
|
99
|
+
对于每条待创建的菜单,先检查是否与已有菜单重名(`menuName` 或 `path` 相同),重复则跳过。
|
|
100
|
+
|
|
101
|
+
> **响应码说明**:后端成功响应为 `code: 2000`(非标准 HTTP 200),判断成功应检查 `response.body.code === 2000` 或 `message` 包含"成功"。
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
POST {gatewayPath}/system/menu/save
|
|
105
|
+
Headers:
|
|
106
|
+
authorization: bearer {token}
|
|
107
|
+
Sysappno: {sysAppNo}
|
|
108
|
+
Content-Type: application/json
|
|
109
|
+
|
|
110
|
+
Body:
|
|
111
|
+
{
|
|
112
|
+
"useCache": 1,
|
|
113
|
+
"icon": "list",
|
|
114
|
+
"common": 2,
|
|
115
|
+
"hidden": false,
|
|
116
|
+
"type": "C",
|
|
117
|
+
"parentId": "{parentMenuId}",
|
|
118
|
+
"sysAppNo": "{sysAppNo}",
|
|
119
|
+
"orderNum": {nextOrder},
|
|
120
|
+
"menuName": "客户档案",
|
|
121
|
+
"menuNameCode": "{parentMenuNameCode}:{pinyinName}",
|
|
122
|
+
"path": "mmwrCustomerArchive",
|
|
123
|
+
"permission": "mmwrCustomerArchive",
|
|
124
|
+
"component": "produce/production-mmwr/aiflow/mmwr-customer-archive/index.vue"
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Step 3: 记录结果
|
|
129
|
+
|
|
130
|
+
创建完成后输出结果表格:
|
|
131
|
+
|
|
132
|
+
| 菜单名 | path | type | 状态 | id |
|
|
133
|
+
| -------- | ------------------- | ---- | ------------------- | ------ |
|
|
134
|
+
| 客户档案 | mmwrCustomerArchive | C | ✅ created | 123456 |
|
|
135
|
+
| 客户详情 | mmwrCustomerDetail | C | ⏭️ skipped (已存在) | - |
|
|
136
|
+
|
|
137
|
+
### 字段生成规则
|
|
138
|
+
|
|
139
|
+
| 字段 | 规则 |
|
|
140
|
+
| -------------- | -------------------------------------------------------------------------------------------- |
|
|
141
|
+
| `menuName` | 取 pages.ts 的 `label` |
|
|
142
|
+
| `path` | 页面目录名转 camelCase(如 `mmwr-customer-archive` → `mmwrCustomerArchive`) |
|
|
143
|
+
| `component` | 取 pages.ts 的 `name`(如 `produce/production-mmwr/aiflow/mmwr-customer-archive/index.vue`) |
|
|
144
|
+
| `permission` | `{域}:{path}:list`(如 `produce:mmwrCustomerArchive:list`) |
|
|
145
|
+
| `menuNameCode` | `{父级menuNameCode}:{菜单名拼音}`(小写连续拼接) |
|
|
146
|
+
| `hidden` | 表单页/详情页等隐藏路由设为 `true`,菜单可见页面设为 `false` |
|
|
147
|
+
| `orderNum` | 从父级已有菜单最大 orderNum + 1 开始递增 |
|
|
148
|
+
| `icon` | 目录级 `"list"`,菜单级 `"list"` |
|
|
149
|
+
|
|
150
|
+
### pages.ts → 菜单数据映射示例
|
|
151
|
+
|
|
152
|
+
以 `aiflow` 子模块为例:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
pages.ts 条目:
|
|
156
|
+
["mmwr-customer-archive", "客户档案"]
|
|
157
|
+
|
|
158
|
+
→ 菜单数据:
|
|
159
|
+
{
|
|
160
|
+
type: "C",
|
|
161
|
+
menuName: "客户档案",
|
|
162
|
+
path: "mmwrCustomerArchive",
|
|
163
|
+
component: "produce/production-mmwr/aiflow/mmwr-customer-archive/index.vue",
|
|
164
|
+
permission: "produce:mmwrCustomerArchive:list",
|
|
165
|
+
hidden: false
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
隐藏页面示例:
|
|
169
|
+
["mmwr-customer-apply-add-form", "客户申请新增表单"]
|
|
170
|
+
|
|
171
|
+
→ 菜单数据:
|
|
172
|
+
{
|
|
173
|
+
type: "C",
|
|
174
|
+
menuName: "客户申请新增表单",
|
|
175
|
+
path: "mmwrCustomerApplyAddForm",
|
|
176
|
+
component: "produce/production-mmwr/aiflow/mmwr-customer-apply-add-form/index.vue",
|
|
177
|
+
permission: "produce:mmwrCustomerApplyAddForm:list",
|
|
178
|
+
hidden: true // ← 表单页隐藏
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 隐藏页面判断规则
|
|
183
|
+
|
|
184
|
+
以下页面类型应设置 `hidden: true`:
|
|
185
|
+
|
|
186
|
+
- 目录名含 `-form`(独立路由表单页)
|
|
187
|
+
- 目录名含 `-detail`(详情页)
|
|
188
|
+
- 目录名含 `-history`(历史查询页)
|
|
189
|
+
- reports/SYS_MENU_INFO.md 中标注为"隐藏菜单"的页面
|
|
190
|
+
|
|
191
|
+
### 跳过规则
|
|
192
|
+
|
|
193
|
+
- 当前父级下已有同名 `menuName` → 跳过
|
|
194
|
+
- 当前父级下已有相同 `path` → 跳过
|
|
195
|
+
- 跳过时不做更新、不做覆盖,只记录"已跳过"
|
|
196
|
+
|
|
197
|
+
### 树形菜单处理
|
|
198
|
+
|
|
199
|
+
如果需要先创建目录再创建子菜单:
|
|
200
|
+
|
|
201
|
+
1. 先以 `type: "M"` 创建目录
|
|
202
|
+
2. 保存成功后取返回的 `data.id`
|
|
203
|
+
3. 以新 `id` 作为 `parentId` 继续创建子菜单
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Phase 2:推送脚本方案(待后端接口就绪后启用)
|
|
208
|
+
|
|
209
|
+
> 对应 menu-sync-design.md 的 **方案 D(推送覆盖)**,是最终目标方案。
|
|
210
|
+
|
|
211
|
+
### 与 Phase 1 的差异
|
|
212
|
+
|
|
213
|
+
| 维度 | Phase 1 (AI 调 API) | Phase 2 (推送脚本) |
|
|
214
|
+
| -------- | ------------------- | ---------------------------- |
|
|
215
|
+
| 执行者 | AI | `pnpm run menu:push` 脚本 |
|
|
216
|
+
| 更新能力 | 只增不删(方案 C) | upsert 覆盖(方案 D) |
|
|
217
|
+
| 改名支持 | ❌ 会产生重复 | ✅ 按 componentPath 匹配更新 |
|
|
218
|
+
| 权限影响 | 无 | 无(只写结构字段) |
|
|
219
|
+
| 依赖 | Token + 网关地址 | 后端 `batchPush` 接口 |
|
|
220
|
+
|
|
221
|
+
### 所需后端接口
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
POST /system/menu/batchPush
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
- 按 `componentPath` 做 upsert(存在则更新结构字段,不存在则新增)
|
|
228
|
+
- **不删除**后端多余的菜单
|
|
229
|
+
- **不碰**权限/角色绑定字段
|
|
230
|
+
- 详见 `.github/guides/architecture.md`
|
|
231
|
+
|
|
232
|
+
### pages.ts 扩展
|
|
233
|
+
|
|
234
|
+
在 `SharedPageItem` 中增加 `menuMeta` 字段:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
export interface SharedPageItem {
|
|
238
|
+
name: string;
|
|
239
|
+
label: string;
|
|
240
|
+
menuMeta?: {
|
|
241
|
+
parentName: string;
|
|
242
|
+
menuPath: string;
|
|
243
|
+
sortOrder: number;
|
|
244
|
+
appCode: string;
|
|
245
|
+
hidden?: boolean;
|
|
246
|
+
icon?: string;
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### 切换步骤
|
|
252
|
+
|
|
253
|
+
1. 后端提供 `batchPush` 接口
|
|
254
|
+
2. 在 pages.ts 的页面条目中补充 `menuMeta`
|
|
255
|
+
3. 创建 `scripts/menu-push.ts`(vite-node 执行)
|
|
256
|
+
4. 注册 `pnpm run menu:push` 命令
|
|
257
|
+
5. page-codegen Skill 生成页面时自动填充 `menuMeta`
|
|
258
|
+
6. 废弃 Phase 1 的 AI 手动调 API 流程
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# 使用指南:menu-sync(菜单同步)
|
|
2
|
+
|
|
3
|
+
> **谁读这个文档**:团队成员(前端 + 后端联调时)
|
|
4
|
+
> **AI 触发文件**:同目录 `SKILL.md`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 这个 Skill 解决什么问题
|
|
9
|
+
|
|
10
|
+
新页面写完后,需要在系统菜单中**注册一条菜单记录**才能从 UI 访问。这个 Skill:
|
|
11
|
+
|
|
12
|
+
1. 拉取**线上菜单数据**到 `reports/SYS_MENU_INFO.md`(团队基线)
|
|
13
|
+
2. 对比已生成页面 vs 线上菜单,**自动补齐缺失菜单项**
|
|
14
|
+
3. 调用菜单注册接口(或生成 SQL 让后端执行)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 何时使用
|
|
19
|
+
|
|
20
|
+
- 新增页面后,从 UI 访问报"菜单不存在"
|
|
21
|
+
- 一次性创建一批页面,需要批量注册菜单
|
|
22
|
+
- 同步线上菜单基线(PROD/UAT/DEV 拉齐)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 触发关键词
|
|
27
|
+
|
|
28
|
+
`创建菜单` / `注册菜单` / `同步菜单` / `补菜单`
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 前置条件
|
|
33
|
+
|
|
34
|
+
`.github/skills/sync/menu-sync/env/env.local.json`(首次使用先填写):
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"gatewayPath": "https://uat-api.example.com",
|
|
39
|
+
"token": "Bearer xxx",
|
|
40
|
+
"tenantId": "10001",
|
|
41
|
+
"rootMenuId": "1234567890"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
> **`env.local.json` 不入 git**(已加 .gitignore)。每个开发者本地填自己的 token。
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 标准对话示例
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
你:把 src/views/produce/aiflow/ 下新建的 7 个页面同步到菜单
|
|
53
|
+
AI:[Pre-flight]
|
|
54
|
+
步骤 1:读取线上菜单基线 reports/SYS_MENU_INFO.md(最新更新:2025-04-20)
|
|
55
|
+
步骤 2:扫描 src/views/produce/aiflow/ 共 7 个页面
|
|
56
|
+
步骤 3:对比缺失:5 项缺失,2 项已存在
|
|
57
|
+
步骤 4:将调用 /sys/menu/save 创建 5 项
|
|
58
|
+
[需你确认]:
|
|
59
|
+
- 父菜单:精整作业 → 客户管理(rootMenuId: xxx)
|
|
60
|
+
- 排序:sortNo 自动递增
|
|
61
|
+
- 权限标识:mmwr:customer:list 等
|
|
62
|
+
确认 yes/no?
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 输出物
|
|
68
|
+
|
|
69
|
+
1. **基线文件**:`.github/reports/SYS_MENU_INFO.md` —— 线上菜单的本地基线(团队 review 用)
|
|
70
|
+
2. **同步报告**:`.github/reports/MENU_SYNC_<YYYYMMDD>.md` —— 本次新增/更新了哪些菜单
|
|
71
|
+
3. **回滚 SQL**:报告里附带 DELETE SQL,用于回退
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 常见踩坑
|
|
76
|
+
|
|
77
|
+
| 现象 | 原因 | 解法 |
|
|
78
|
+
| -------------------- | ---------------------------- | ---------------------------------------------- |
|
|
79
|
+
| 同步成功但 UI 看不到 | 用户角色没分配新菜单 | 跑 permission-sync(PLANNED)或后台手工分配 |
|
|
80
|
+
| 401/403 报错 | env.local.json 的 token 过期 | 重新登录系统,从 Network 抓 Authorization 替换 |
|
|
81
|
+
| 同名菜单重复创建 | 没读 SYS_MENU_INFO.md 基线 | 先跑一遍"刷新基线"再 sync |
|
|
82
|
+
| 父菜单 ID 不对 | rootMenuId 配错 | 从浏览器开发者工具看父菜单的 dom data-id |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 团队协作流程
|
|
87
|
+
|
|
88
|
+
1. 新人入职,让他先把 `env.local.json` 填一份(参考 env.example.json)
|
|
89
|
+
2. **每周一**由 lead 跑一次"刷新基线",确保 reports/SYS_MENU_INFO.md 最新
|
|
90
|
+
3. PR 提交前自查"我加的菜单有没有提交进基线"
|
|
91
|
+
4. 上线前再 sync 一次到生产环境(切换 env 文件中的 gatewayPath)
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## FAQ
|
|
96
|
+
|
|
97
|
+
**Q:env.local.json 泄露 token 危险吗?**
|
|
98
|
+
A:危险。token 视同密码。已加 .gitignore + .npmignore。**不要 commit**。
|
|
99
|
+
|
|
100
|
+
**Q:能不能直接给 SQL 让后端跑?**
|
|
101
|
+
A:能。在指令里加"只生成 SQL,不调用接口"即可。
|
|
102
|
+
|
|
103
|
+
**Q:和 dict-sync / permission-sync 关系?**
|
|
104
|
+
A:菜单是入口,字典是值域,权限是访问控制。三者独立但配合。建议顺序:menu-sync → permission-sync → dict-sync。
|