@agile-team/wl-skills-kit 2.4.2 → 2.5.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 +37 -1
- package/README.md +65 -44
- package/bin/wl-skills.js +196 -12
- package/files/.github/copilot-instructions.md +361 -322
- package/files/.github/guides/architecture.md +1 -1
- package/files/.github/guides/usage.md +32 -10
- package/files/.github/skills/_registry.md +18 -16
- package/files/.github/skills/core/page-codegen/SKILL.md +149 -74
- package/files/.github/skills/core/page-codegen/USAGE.md +33 -12
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-DETAIL-TABS.md +80 -48
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-FORM-ROUTE.md +183 -55
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-LIST.md +110 -21
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-MASTER-DETAIL.md +29 -9
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-RECORD-FORM.md +93 -48
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-TREE-LIST.md +49 -29
- package/files/.github/skills/sync/menu-sync/SKILL.md +27 -13
- package/mcp/server.js +279 -195
- package/mcp/tools/menuSync.js +416 -96
- package/mcp/tools/projectTools.js +336 -124
- package/package.json +2 -2
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
> 见 SKILL.md 主文件(约束 + 按钮规则 + Mock 规范等共用规则)。
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
#### index.vue
|
|
7
6
|
|
|
8
7
|
```vue
|
|
@@ -24,7 +23,14 @@
|
|
|
24
23
|
@reset="select"
|
|
25
24
|
/>
|
|
26
25
|
<BaseToolbar :items="toolbars" />
|
|
27
|
-
<BaseTable
|
|
26
|
+
<BaseTable
|
|
27
|
+
ref="tableRef"
|
|
28
|
+
render-type="agGrid"
|
|
29
|
+
:cid="TABLE_CID"
|
|
30
|
+
:data="list"
|
|
31
|
+
:columns="columns"
|
|
32
|
+
showToolbar
|
|
33
|
+
/>
|
|
28
34
|
<jh-pagination
|
|
29
35
|
v-show="page.total && page.total > 0"
|
|
30
36
|
:total="page.total || 0"
|
|
@@ -39,7 +45,7 @@
|
|
|
39
45
|
</template>
|
|
40
46
|
|
|
41
47
|
<script setup lang="ts">
|
|
42
|
-
import { createPage, loadTree } from "./data";
|
|
48
|
+
import { createPage, loadTree, TABLE_CID } from "./data";
|
|
43
49
|
|
|
44
50
|
const Page = createPage();
|
|
45
51
|
const {
|
|
@@ -52,7 +58,7 @@ const {
|
|
|
52
58
|
columns,
|
|
53
59
|
toolbars,
|
|
54
60
|
select,
|
|
55
|
-
handleNodeClick
|
|
61
|
+
handleNodeClick,
|
|
56
62
|
} = Page;
|
|
57
63
|
|
|
58
64
|
onMounted(() => {
|
|
@@ -74,9 +80,13 @@ import {
|
|
|
74
80
|
BaseQueryItemDesc,
|
|
75
81
|
ActionButtonDesc,
|
|
76
82
|
TableColumnDesc,
|
|
77
|
-
BusLogicDataType
|
|
83
|
+
BusLogicDataType,
|
|
78
84
|
} from "@/types/page";
|
|
79
85
|
import { getAction, postAction } from "@jhlc/common-core/src/api/action";
|
|
86
|
+
import { ElMessage } from "element-plus";
|
|
87
|
+
import { defineColumns, renderOps } from "@agile-team/wk-skills-ui/runtime";
|
|
88
|
+
|
|
89
|
+
export const TABLE_CID = "[pageAbbr]-[base36Timestamp]";
|
|
80
90
|
|
|
81
91
|
export const API_CONFIG = {
|
|
82
92
|
tree: "/[服务缩写]/[树资源]/tree",
|
|
@@ -84,7 +94,7 @@ export const API_CONFIG = {
|
|
|
84
94
|
remove: "/[服务缩写]/[主资源]/remove",
|
|
85
95
|
getById: "/[服务缩写]/[主资源]/getById",
|
|
86
96
|
save: "/[服务缩写]/[主资源]/save",
|
|
87
|
-
update: "/[服务缩写]/[主资源]/update"
|
|
97
|
+
update: "/[服务缩写]/[主资源]/update",
|
|
88
98
|
} as const;
|
|
89
99
|
|
|
90
100
|
// ===== 树形数据 =====
|
|
@@ -106,7 +116,7 @@ export function createPage(editModalRef?: any) {
|
|
|
106
116
|
|
|
107
117
|
queryDef(): BaseQueryItemDesc<any>[] {
|
|
108
118
|
return [
|
|
109
|
-
{ name: "[fieldName]", label: "[中文名]", placeholder: "请输入" }
|
|
119
|
+
{ name: "[fieldName]", label: "[中文名]", placeholder: "请输入" },
|
|
110
120
|
];
|
|
111
121
|
}
|
|
112
122
|
|
|
@@ -115,7 +125,7 @@ export function createPage(editModalRef?: any) {
|
|
|
115
125
|
{
|
|
116
126
|
name: "primary",
|
|
117
127
|
label: "新增",
|
|
118
|
-
onClick: () => _editModalRef?.value?.open()
|
|
128
|
+
onClick: () => _editModalRef?.value?.open(),
|
|
119
129
|
},
|
|
120
130
|
{
|
|
121
131
|
name: "danger",
|
|
@@ -124,34 +134,44 @@ export function createPage(editModalRef?: any) {
|
|
|
124
134
|
const rows = this.tableRef.value?.getSelectionRows();
|
|
125
135
|
if (!rows?.length) return ElMessage.warning("请先选择数据");
|
|
126
136
|
this.removeBatch();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
137
|
+
},
|
|
138
|
+
},
|
|
129
139
|
];
|
|
130
140
|
}
|
|
131
141
|
|
|
132
142
|
columnsDef(): TableColumnDesc<any>[] {
|
|
133
|
-
return [
|
|
134
|
-
{
|
|
135
|
-
|
|
136
|
-
|
|
143
|
+
return defineColumns([
|
|
144
|
+
{
|
|
145
|
+
type: "selection",
|
|
146
|
+
width: 55,
|
|
147
|
+
fixed: "left",
|
|
148
|
+
align: "center",
|
|
149
|
+
headerAlign: "center",
|
|
150
|
+
},
|
|
151
|
+
{ type: "index", label: "序号", width: 60, align: "center" },
|
|
152
|
+
{
|
|
153
|
+
label: "[字段名]",
|
|
154
|
+
name: "[fieldName]",
|
|
155
|
+
cid: `${TABLE_CID}-[fieldName]`,
|
|
156
|
+
minWidth: 120,
|
|
157
|
+
},
|
|
137
158
|
{
|
|
138
159
|
label: "操作",
|
|
160
|
+
name: "_action",
|
|
161
|
+
cid: `${TABLE_CID}-action`,
|
|
139
162
|
width: 140,
|
|
140
163
|
fixed: "right",
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
]
|
|
153
|
-
}
|
|
154
|
-
];
|
|
164
|
+
align: "center",
|
|
165
|
+
defaultSlot: ({ row }: any) =>
|
|
166
|
+
renderOps([
|
|
167
|
+
{
|
|
168
|
+
type: "edit",
|
|
169
|
+
onClick: () => _editModalRef?.value?.edit(row.id),
|
|
170
|
+
},
|
|
171
|
+
{ type: "del", onClick: () => this.remove(row.id) },
|
|
172
|
+
]),
|
|
173
|
+
},
|
|
174
|
+
] as any) as TableColumnDesc<any>[];
|
|
155
175
|
}
|
|
156
176
|
})();
|
|
157
177
|
|
|
@@ -168,7 +188,7 @@ export function createPage(editModalRef?: any) {
|
|
|
168
188
|
return {
|
|
169
189
|
...created,
|
|
170
190
|
treeData,
|
|
171
|
-
handleNodeClick
|
|
191
|
+
handleNodeClick,
|
|
172
192
|
};
|
|
173
193
|
}
|
|
174
194
|
```
|
|
@@ -19,9 +19,10 @@ description: "Use when: creating system menus for newly generated pages, batch r
|
|
|
19
19
|
|
|
20
20
|
| 数据 | 来源 | 说明 |
|
|
21
21
|
| ------------------------------------------------ | -------------------------- | --------------------------------------- |
|
|
22
|
-
| 菜单名称、路径、组件、权限、隐藏、排序、应用编码 |
|
|
23
|
-
| `parentMenuNameCode` |
|
|
24
|
-
| **gatewayPath、parentMenuId、sysAppNo、token** | `env.local.json` |
|
|
22
|
+
| 菜单名称、路径、组件、权限、隐藏、排序、应用编码 | `.github/reports/SYS_MENU_INFO*.md` | 由 page-codegen 追加写入,AI 直接读取 |
|
|
23
|
+
| `parentMenuNameCode` | `wls_menu_query` 查询菜单树 | 从父级节点获取,无需手填 |
|
|
24
|
+
| **gatewayPath、parentMenuId、sysAppNo、token** | `env.local.json` | 可通过 token/接口辅助提取 |
|
|
25
|
+
| **domainId** | 用户确认 / 菜单后台 Network | 当前权限下无法总是自动获取,需确认 |
|
|
25
26
|
|
|
26
27
|
### 配置文件(统一维护,菜单/字典/权限共用)
|
|
27
28
|
|
|
@@ -33,7 +34,8 @@ description: "Use when: creating system menus for newly generated pages, batch r
|
|
|
33
34
|
"sysAppNo": "应用编码(从已有菜单的sysAppNo字段获取,非明文)",
|
|
34
35
|
"token": "Bearer Token(不含bearer前缀)",
|
|
35
36
|
"menu": {
|
|
36
|
-
"parentMenuId": "父级菜单ID"
|
|
37
|
+
"parentMenuId": "父级菜单ID",
|
|
38
|
+
"domainId": "应用域ID"
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
```
|
|
@@ -47,9 +49,15 @@ menu-sync 读取规则:`parentMenuId` 优先从 `menu.parentMenuId` 读取,
|
|
|
47
49
|
|
|
48
50
|
1. **首次**:按 `env/guide.md` 填写 `skills/sync/env.local.json` 的字段
|
|
49
51
|
2. **之后**:直接对 AI 说「帮我创建菜单」/「同步菜单」/「补菜单」
|
|
50
|
-
3. AI 自动执行:读
|
|
52
|
+
3. AI 自动执行:读 `.github/reports/SYS_MENU_INFO*.md` → 读 `env.local.json` → 优先调用 `wls_menu_sync_from_report` 一步完成确定性同步;如需手动拆分,则 `wls_menu_query` 查 domain 菜单树 → 先 upsert 一级目录 → 用返回 id upsert 二级页面菜单 → 输出 created/updated/skipped 结果表
|
|
51
53
|
4. **全程无需手动执行任何命令**
|
|
52
54
|
|
|
55
|
+
SYS_MENU_INFO.md 是 menu-sync Skill 的输入数据源:
|
|
56
|
+
- **自动创建**:用户说"帮我创建菜单" → menu-sync 调用 `wls_menu_sync_from_report` 读取 SYS_MENU_INFO.md → 调 API 按目录与页面顺序创建/更新
|
|
57
|
+
- **手动创建**:用户也可直接按 SYS_MENU_INFO.md 的表格在系统管理后台手动创建菜单
|
|
58
|
+
- 两种方式等价,菜单创建后通过 `组件路径` 字段与 pages.ts 注册的文件路径关联
|
|
59
|
+
- **自动创建顺序**:优先使用 `wls_menu_sync_from_report`;手动拆分时必须先调用 `wls_menu_query` 获取当前 domain 菜单树,再 `wls_menu_upsert` 创建/更新一级目录(type=M),拿到目录 id 后再创建二级菜单(type=C)。不得把二级页面全部直接挂到根 `parentMenuId`。
|
|
60
|
+
|
|
53
61
|
---
|
|
54
62
|
|
|
55
63
|
## 方案演进路线
|
|
@@ -90,18 +98,23 @@ menu-sync 读取规则:`parentMenuId` 优先从 `menu.parentMenuId` 读取,
|
|
|
90
98
|
|
|
91
99
|
### 执行流程
|
|
92
100
|
|
|
93
|
-
#### Step 1:
|
|
101
|
+
#### Step 1: 查询当前 domain 菜单树(防重复 + 取父级信息)
|
|
94
102
|
|
|
95
103
|
```
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
Sysappno: {sysAppNo}
|
|
104
|
+
工具:wls_menu_query
|
|
105
|
+
读取:env.local.json → menu.domainId
|
|
106
|
+
返回:当前应用域完整菜单树
|
|
100
107
|
```
|
|
101
108
|
|
|
102
|
-
|
|
109
|
+
> 推荐:具备 MCP 时优先调用 `wls_menu_sync_from_report`,它会自动读取最新 `SYS_MENU_INFO*.md`、查询菜单树、复用/更新目录,再把二级菜单挂到对应目录下。可先传 `dryRun: true` 做预览。
|
|
110
|
+
|
|
111
|
+
#### Step 2: 先创建一级目录(type=M)
|
|
112
|
+
|
|
113
|
+
对于 `SYS_MENU_INFO` 中的一级目录,按 `menuName/path` 在父级 `parentMenuId` 下去重;不存在则创建,存在则复用 id。
|
|
114
|
+
|
|
115
|
+
#### Step 3: 再创建二级页面菜单(type=C)
|
|
103
116
|
|
|
104
|
-
|
|
117
|
+
对于每条页面菜单,`parentId` 必须使用上一步对应目录的 id,禁止全部挂到根 `parentMenuId`。
|
|
105
118
|
|
|
106
119
|
> **响应码说明**:后端成功响应为 `code: 2000`(非标准 HTTP 200),判断成功应检查 `response.body.code === 2000` 或 `message` 包含"成功"。
|
|
107
120
|
|
|
@@ -201,11 +214,12 @@ pages.ts 条目:
|
|
|
201
214
|
|
|
202
215
|
### 树形菜单处理
|
|
203
216
|
|
|
204
|
-
|
|
217
|
+
如果 `SYS_MENU_INFO` 包含一级目录和二级菜单,必须按以下顺序处理:
|
|
205
218
|
|
|
206
219
|
1. 先以 `type: "M"` 创建目录
|
|
207
220
|
2. 保存成功后取返回的 `data.id`
|
|
208
221
|
3. 以新 `id` 作为 `parentId` 继续创建子菜单
|
|
222
|
+
4. 若目录已存在,复用已存在目录 id,不重复创建
|
|
209
223
|
|
|
210
224
|
---
|
|
211
225
|
|