worsoft-frontend-codegen-local-mcp 0.1.85 → 0.1.88
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/README.md +41 -14
- package/assets/templates/ledger_master_child_jump/api.tpl +2 -2
- package/assets/templates/ledger_master_child_jump/index.tpl +4 -3
- package/assets/templates/ledger_single_table_jump/api.tpl +2 -2
- package/assets/templates/ledger_single_table_jump/index.tpl +4 -3
- package/assets/templates/master_child_jump/index.tpl +4 -3
- package/assets/templates/single_table_dialog/index.tpl +4 -3
- package/assets/templates/single_table_jump/index.tpl +4 -3
- package/mcp_server.js +158 -75
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,18 @@ This MCP generates Worsoft frontend files from structured JSON metadata.
|
|
|
15
15
|
|
|
16
16
|
## Current Inputs
|
|
17
17
|
|
|
18
|
+
Preferred input:
|
|
19
|
+
|
|
20
|
+
- `parseResultPath`
|
|
21
|
+
- `writeToDisk`
|
|
22
|
+
- `overwrite`
|
|
23
|
+
- `writeSupportFiles`
|
|
24
|
+
- `mergeI18nZh`
|
|
25
|
+
|
|
26
|
+
When `parseResultPath` is provided, MCP reads `parseResult.json` locally and uses `parseResult.mcpPayload` as the generation input. The caller should not expand `mcpPayload` in the model context.
|
|
27
|
+
|
|
28
|
+
Legacy-compatible low-level inputs:
|
|
29
|
+
|
|
18
30
|
- `featureTitle`
|
|
19
31
|
- `tableName`
|
|
20
32
|
- `tableComment`
|
|
@@ -35,7 +47,28 @@ If the caller accidentally passes the `src` directory, MCP will normalize it bac
|
|
|
35
47
|
|
|
36
48
|
## Recommended MCP Arguments
|
|
37
49
|
|
|
38
|
-
|
|
50
|
+
Preferred:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"parseResultPath": "E:/own-worker-platform/trunk/docs/03-modules/.../parseResult.json",
|
|
55
|
+
"writeToDisk": true,
|
|
56
|
+
"overwrite": true
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
If `parseResult.mcpPayload` does not contain `frontendPath`, pass it as a non-semantic runtime argument:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"parseResultPath": "E:/own-worker-platform/trunk/docs/03-modules/.../parseResult.json",
|
|
65
|
+
"frontendPath": "E:/own-worker-platform/trunk/worsoft-ui",
|
|
66
|
+
"writeToDisk": true,
|
|
67
|
+
"overwrite": true
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Legacy low-level single table:
|
|
39
72
|
|
|
40
73
|
```json
|
|
41
74
|
{
|
|
@@ -56,7 +89,7 @@ Single table:
|
|
|
56
89
|
}
|
|
57
90
|
```
|
|
58
91
|
|
|
59
|
-
|
|
92
|
+
Legacy low-level master-child:
|
|
60
93
|
|
|
61
94
|
```json
|
|
62
95
|
{
|
|
@@ -105,6 +138,7 @@ Node:
|
|
|
105
138
|
|
|
106
139
|
```bash
|
|
107
140
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario single
|
|
141
|
+
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario parse_result_path
|
|
108
142
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario single_dialog
|
|
109
143
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario dict_single
|
|
110
144
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario master
|
|
@@ -134,9 +168,9 @@ Optional overrides:
|
|
|
134
168
|
- `mergeI18nZh=false`:
|
|
135
169
|
MCP renders `zh-cn.ts` from current metadata only and does not parse the existing file
|
|
136
170
|
|
|
137
|
-
## Master-Child Contract
|
|
171
|
+
## Legacy Low-Level Master-Child Contract
|
|
138
172
|
|
|
139
|
-
For `master_child_jump
|
|
173
|
+
For low-level `master_child_jump` calls, MCP does not infer relations. `parseResultPath` callers should let `doc-parse` populate `parseResult.mcpPayload.children`.
|
|
140
174
|
|
|
141
175
|
The caller must provide explicit `children[]`, and each child must include:
|
|
142
176
|
|
|
@@ -149,9 +183,9 @@ The caller must provide explicit `children[]`, and each child must include:
|
|
|
149
183
|
|
|
150
184
|
If these fields are missing, MCP returns a structured validation error.
|
|
151
185
|
|
|
152
|
-
## Multi-Level Dictionary Contract
|
|
186
|
+
## Legacy Low-Level Multi-Level Dictionary Contract
|
|
153
187
|
|
|
154
|
-
For `multi_level_dict`, MCP does not infer hierarchy.
|
|
188
|
+
For low-level `multi_level_dict`, MCP does not infer hierarchy. `parseResultPath` callers should let `doc-parse` populate `parseResult.mcpPayload.levels`.
|
|
155
189
|
|
|
156
190
|
The caller must provide explicit `levels[]`. Each level must include:
|
|
157
191
|
|
|
@@ -182,14 +216,7 @@ When the same physical table is split into different logical regions, pass `modu
|
|
|
182
216
|
- Human guide: `STYLE_TEMPLATE_GUIDE.md`
|
|
183
217
|
- Machine source: `assets/style-catalog.json`
|
|
184
218
|
|
|
185
|
-
Declared styles
|
|
186
|
-
|
|
187
|
-
- `single_table_jump`
|
|
188
|
-
- `single_table_dialog`
|
|
189
|
-
- `master_child_jump`
|
|
190
|
-
- `multi_level_dict`
|
|
191
|
-
- `single_tree_table`
|
|
192
|
-
- `tree_left_list`
|
|
219
|
+
Declared styles are defined by `assets/style-catalog.json`; README intentionally does not duplicate the full enum as a second source.
|
|
193
220
|
|
|
194
221
|
## Notes
|
|
195
222
|
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
import { createCrudApi } from '/@/api/common/crudFactory';
|
|
3
3
|
{{API_REQUEST_IMPORT}}
|
|
4
4
|
|
|
5
|
-
// {{FEATURE_TITLE}}
|
|
6
|
-
export const { fetchList, getObj } = createCrudApi('/{{API_PATH}}');
|
|
5
|
+
// {{FEATURE_TITLE}}主子表台账只读接口:分页查询、详情查询、导出
|
|
6
|
+
export const { fetchList, getObj, exportObj } = createCrudApi('/{{API_PATH}}');
|
|
7
7
|
{{EXTRA_API_FUNCTIONS}}
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
<script setup lang="ts" name="system{{CLASS_NAME}}">
|
|
38
38
|
// 列表接口:台账页只消费分页查询能力
|
|
39
|
-
import { fetchList } from '/@/api/{{API_MODULE_PATH}}';
|
|
39
|
+
import { fetchList, exportObj } from '/@/api/{{API_MODULE_PATH}}';
|
|
40
40
|
// 字典数据加载
|
|
41
41
|
import { useDict } from '/@/hooks/dict';
|
|
42
42
|
// 列表页字段元数据能力
|
|
@@ -66,8 +66,9 @@ const router = useRouter();
|
|
|
66
66
|
const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle, exportExcel: runExportExcel, resetQueryForm } = useSchemaListQuery({
|
|
67
67
|
schema: crudSchema,
|
|
68
68
|
pageList: fetchList,
|
|
69
|
-
|
|
70
|
-
exportFileName: '{{
|
|
69
|
+
exportApi: exportObj,
|
|
70
|
+
exportFileName: '{{FEATURE_TITLE}}.xlsx',
|
|
71
|
+
exportColumns: () => tableColumns.value,
|
|
71
72
|
});
|
|
72
73
|
|
|
73
74
|
// 统一处理台账列表加载后的首行选中,以及刷新后的当前行保持
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
import { createCrudApi } from '/@/api/common/crudFactory';
|
|
3
3
|
{{API_REQUEST_IMPORT}}
|
|
4
4
|
|
|
5
|
-
// {{FEATURE_TITLE}}
|
|
6
|
-
export const { fetchList, getObj } = createCrudApi('/{{API_PATH}}');
|
|
5
|
+
// {{FEATURE_TITLE}}台账只读接口:分页查询、详情查询、导出
|
|
6
|
+
export const { fetchList, getObj, exportObj } = createCrudApi('/{{API_PATH}}');
|
|
7
7
|
{{EXTRA_API_FUNCTIONS}}
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
<script setup lang="ts" name="system{{CLASS_NAME}}">
|
|
38
38
|
// 列表接口:台账页只消费分页查询能力
|
|
39
|
-
import { fetchList } from '/@/api/{{API_MODULE_PATH}}';
|
|
39
|
+
import { fetchList, exportObj } from '/@/api/{{API_MODULE_PATH}}';
|
|
40
40
|
// 字典数据加载
|
|
41
41
|
import { useDict } from '/@/hooks/dict';
|
|
42
42
|
// 列表页字段元数据能力
|
|
@@ -66,8 +66,9 @@ const router = useRouter();
|
|
|
66
66
|
const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle, exportExcel: runExportExcel, resetQueryForm } = useSchemaListQuery({
|
|
67
67
|
schema: crudSchema,
|
|
68
68
|
pageList: fetchList,
|
|
69
|
-
|
|
70
|
-
exportFileName: '{{
|
|
69
|
+
exportApi: exportObj,
|
|
70
|
+
exportFileName: '{{FEATURE_TITLE}}.xlsx',
|
|
71
|
+
exportColumns: () => tableColumns.value,
|
|
71
72
|
});
|
|
72
73
|
|
|
73
74
|
// 统一处理台账列表加载后的首行选中,以及刷新后的当前行保持
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
<script setup lang="ts" name="system{{CLASS_NAME}}">
|
|
52
52
|
// 列表接口
|
|
53
|
-
import { fetchList, delObjs } from '/@/api/{{API_MODULE_PATH}}';
|
|
53
|
+
import { fetchList, delObjs, exportObj } from '/@/api/{{API_MODULE_PATH}}';
|
|
54
54
|
// 通用消息与确认弹窗
|
|
55
55
|
import { useMessage, useMessageBox } from '/@/hooks/message';
|
|
56
56
|
// 字典数据加载
|
|
@@ -91,8 +91,9 @@ const multiple = ref(true);
|
|
|
91
91
|
const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle, exportExcel: runExportExcel, resetQueryForm } = useSchemaListQuery({
|
|
92
92
|
schema: crudSchema,
|
|
93
93
|
pageList: fetchList,
|
|
94
|
-
|
|
95
|
-
exportFileName: '{{
|
|
94
|
+
exportApi: exportObj,
|
|
95
|
+
exportFileName: '{{FEATURE_TITLE}}.xlsx',
|
|
96
|
+
exportColumns: () => tableColumns.value,
|
|
96
97
|
});
|
|
97
98
|
|
|
98
99
|
// 统一处理列表加载后的首行选中,以及刷新后的当前行保持
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
|
|
45
45
|
<script setup lang="ts" name="system{{CLASS_NAME}}">
|
|
46
46
|
// 列表接口
|
|
47
|
-
import { fetchList, delObjs{{DICT_API_IMPORTS}} } from '/@/api/{{API_MODULE_PATH}}';
|
|
47
|
+
import { fetchList, delObjs, exportObj{{DICT_API_IMPORTS}} } from '/@/api/{{API_MODULE_PATH}}';
|
|
48
48
|
// 通用消息与确认弹窗
|
|
49
49
|
import { useMessage, useMessageBox } from '/@/hooks/message';
|
|
50
50
|
// 字典数据加载
|
|
@@ -85,8 +85,9 @@ const multiple = ref(true);
|
|
|
85
85
|
const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle, exportExcel: runExportExcel, resetQueryForm } = useSchemaListQuery({
|
|
86
86
|
schema: crudSchema,
|
|
87
87
|
pageList: fetchList,
|
|
88
|
-
|
|
89
|
-
exportFileName: '{{
|
|
88
|
+
exportApi: exportObj,
|
|
89
|
+
exportFileName: '{{FEATURE_TITLE}}.xlsx',
|
|
90
|
+
exportColumns: () => tableColumns.value,
|
|
90
91
|
});
|
|
91
92
|
|
|
92
93
|
// 统一处理列表加载后的首行选中,以及刷新后的当前行保持
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
|
|
52
52
|
<script setup lang="ts" name="system{{CLASS_NAME}}">
|
|
53
53
|
// 列表接口
|
|
54
|
-
import { fetchList, delObjs } from '/@/api/{{API_MODULE_PATH}}';
|
|
54
|
+
import { fetchList, delObjs, exportObj } from '/@/api/{{API_MODULE_PATH}}';
|
|
55
55
|
// 通用消息与确认弹窗
|
|
56
56
|
import { useMessage, useMessageBox } from '/@/hooks/message';
|
|
57
57
|
// 字典数据加载
|
|
@@ -92,8 +92,9 @@ const multiple = ref(true);
|
|
|
92
92
|
const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle, exportExcel: runExportExcel, resetQueryForm } = useSchemaListQuery({
|
|
93
93
|
schema: crudSchema,
|
|
94
94
|
pageList: fetchList,
|
|
95
|
-
|
|
96
|
-
exportFileName: '{{
|
|
95
|
+
exportApi: exportObj,
|
|
96
|
+
exportFileName: '{{FEATURE_TITLE}}.xlsx',
|
|
97
|
+
exportColumns: () => tableColumns.value,
|
|
97
98
|
});
|
|
98
99
|
|
|
99
100
|
// 统一处理列表加载后的首行选中,以及刷新后的当前行保持
|
package/mcp_server.js
CHANGED
|
@@ -5,7 +5,7 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
7
|
const SERVER_NAME = 'worsoft-codegen-local';
|
|
8
|
-
const SERVER_VERSION = '0.1.
|
|
8
|
+
const SERVER_VERSION = '0.1.88';
|
|
9
9
|
const PROTOCOL_VERSION = '2024-11-05';
|
|
10
10
|
const TOOL_NAME = 'worsoft_codegen_local_generate_frontend';
|
|
11
11
|
const STYLE_CATALOG_PATH = path.join(__dirname, 'assets', 'style-catalog.json');
|
|
@@ -203,15 +203,15 @@ export const createCrudApi = (baseUrl: string, overrides: CrudApiPathOverrides =
|
|
|
203
203
|
data: obj,
|
|
204
204
|
}),
|
|
205
205
|
|
|
206
|
-
exportObj: (query?: object) =>
|
|
207
|
-
request({
|
|
208
|
-
url: overrides.export || joinApiPath(baseUrl, 'export'),
|
|
209
|
-
method: '
|
|
210
|
-
|
|
211
|
-
responseType: 'blob',
|
|
212
|
-
}),
|
|
213
|
-
});
|
|
214
|
-
`;
|
|
206
|
+
exportObj: (query?: object) =>
|
|
207
|
+
request({
|
|
208
|
+
url: overrides.export || joinApiPath(baseUrl, 'export'),
|
|
209
|
+
method: 'post',
|
|
210
|
+
data: query,
|
|
211
|
+
responseType: 'blob',
|
|
212
|
+
}),
|
|
213
|
+
});
|
|
214
|
+
`;
|
|
215
215
|
|
|
216
216
|
const DEFAULT_CLOSE_CURRENT_PAGE_TEMPLATE = `import type { RouteLocationNormalizedLoaded } from 'vue-router';
|
|
217
217
|
import { useRoute } from 'vue-router';
|
|
@@ -260,9 +260,13 @@ export const DictSemanticValues = {
|
|
|
260
260
|
} as const;
|
|
261
261
|
`;
|
|
262
262
|
|
|
263
|
-
const TOOL_SCHEMA = {
|
|
264
|
-
type: 'object',
|
|
265
|
-
properties: {
|
|
263
|
+
const TOOL_SCHEMA = {
|
|
264
|
+
type: 'object',
|
|
265
|
+
properties: {
|
|
266
|
+
parseResultPath: {
|
|
267
|
+
type: 'string',
|
|
268
|
+
description: 'Recommended local parseResult.json path. When provided, MCP reads parseResult.mcpPayload locally and uses it as generation input. Direct low-level arguments are legacy compatibility only.',
|
|
269
|
+
},
|
|
266
270
|
featureTitle: { type: 'string', description: 'Feature title from pre-parsed structured metadata.' },
|
|
267
271
|
billCode: { type: 'string', description: 'Stable menu bill_code generated from the Chinese menu title initials.' },
|
|
268
272
|
tableName: { type: 'string', description: 'Canonical main table name from PRD-aligned structured metadata.' },
|
|
@@ -273,10 +277,10 @@ const TOOL_SCHEMA = {
|
|
|
273
277
|
enum: ['business', 'dict', 'ledger', 'non_standard'],
|
|
274
278
|
description: 'Structured page type from parseResult. MCP consumes this value but does not derive it. Dict pages are restricted to dialog-based templates. Ledger pages are read-only jump-based templates.',
|
|
275
279
|
},
|
|
276
|
-
style: { type: 'string', enum: Object.keys(STYLE_CATALOG), description: '
|
|
280
|
+
style: { type: 'string', enum: Object.keys(STYLE_CATALOG), description: 'Legacy low-level final style id from parseResult.mcpPayload. MCP validates it but does not infer it.' },
|
|
277
281
|
fields: {
|
|
278
282
|
type: 'array',
|
|
279
|
-
description: '
|
|
283
|
+
description: 'Legacy low-level structured main-table field metadata. Prefer parseResultPath so MCP reads parseResult.mcpPayload locally.',
|
|
280
284
|
items: {
|
|
281
285
|
type: 'object',
|
|
282
286
|
properties: {
|
|
@@ -290,7 +294,7 @@ const TOOL_SCHEMA = {
|
|
|
290
294
|
show: { type: ['boolean', 'string'], description: 'Whether the field is shown on the page.' },
|
|
291
295
|
listShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the list page.' },
|
|
292
296
|
formShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the form page.' },
|
|
293
|
-
formOrder: { type: ['number', 'string'], description: 'Generation-only form field order from PRD detail/form table. This is not emitted to options.ts.' },
|
|
297
|
+
formOrder: { type: ['number', 'string'], description: 'Generation-only 1-based form field order from PRD detail/form table. This is not emitted to options.ts.' },
|
|
294
298
|
smart: { type: ['boolean', 'string'], description: 'Whether the field participates in smart keyword search. This must come from PRD, not inferred from type.' },
|
|
295
299
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
296
300
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
@@ -333,7 +337,7 @@ const TOOL_SCHEMA = {
|
|
|
333
337
|
show: { type: ['boolean', 'string'], description: 'Whether the field is shown on the page.' },
|
|
334
338
|
listShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the list page.' },
|
|
335
339
|
formShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the form page.' },
|
|
336
|
-
formOrder: { type: ['number', 'string'], description: 'Generation-only form field order from PRD detail/form table. This is not emitted to options.ts.' },
|
|
340
|
+
formOrder: { type: ['number', 'string'], description: 'Generation-only 1-based form field order from PRD detail/form table. This is not emitted to options.ts.' },
|
|
337
341
|
smart: { type: ['boolean', 'string'], description: 'Whether the field participates in smart keyword search. This must come from PRD, not inferred from type.' },
|
|
338
342
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
339
343
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
@@ -394,7 +398,7 @@ const TOOL_SCHEMA = {
|
|
|
394
398
|
show: { type: ['boolean', 'string'], description: 'Whether the field is shown on the page.' },
|
|
395
399
|
listShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the list page.' },
|
|
396
400
|
formShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the form page.' },
|
|
397
|
-
formOrder: { type: ['number', 'string'], description: 'Generation-only form field order from PRD detail/form table. This is not emitted to options.ts.' },
|
|
401
|
+
formOrder: { type: ['number', 'string'], description: 'Generation-only 1-based form field order from PRD detail/form table. This is not emitted to options.ts.' },
|
|
398
402
|
smart: { type: ['boolean', 'string'], description: 'Whether the field participates in smart keyword search. This must come from PRD, not inferred from type.' },
|
|
399
403
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
400
404
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
@@ -465,9 +469,9 @@ const TOOL_SCHEMA = {
|
|
|
465
469
|
description: 'Whether to merge generated zh-cn content into an existing i18n file. If false, MCP renders zh-cn.ts from current metadata only.'
|
|
466
470
|
},
|
|
467
471
|
},
|
|
468
|
-
required: [
|
|
469
|
-
additionalProperties: false,
|
|
470
|
-
};
|
|
472
|
+
required: [],
|
|
473
|
+
additionalProperties: false,
|
|
474
|
+
};
|
|
471
475
|
|
|
472
476
|
function loadStyleCatalog() {
|
|
473
477
|
return JSON.parse(fs.readFileSync(STYLE_CATALOG_PATH, 'utf8'));
|
|
@@ -1370,9 +1374,9 @@ function normalizeStructuredLengthAndScale(lengthValue, scaleValue) {
|
|
|
1370
1374
|
function parseOptionalOrder(value, label) {
|
|
1371
1375
|
if (value === undefined || value === null || value === '') return undefined;
|
|
1372
1376
|
const order = Number.parseInt(String(value), 10);
|
|
1373
|
-
if (Number.isNaN(order) || order <
|
|
1374
|
-
throw new Error(`${label} must be a
|
|
1375
|
-
}
|
|
1377
|
+
if (Number.isNaN(order) || order < 1) {
|
|
1378
|
+
throw new Error(`${label} must be a positive integer starting from 1 when provided, or empty when unspecified`);
|
|
1379
|
+
}
|
|
1376
1380
|
return order;
|
|
1377
1381
|
}
|
|
1378
1382
|
|
|
@@ -1659,7 +1663,7 @@ function normalizePageTypeInput(pageType) {
|
|
|
1659
1663
|
throw new Error(`Unsupported pageType: ${normalized}. Allowed values are dict, business, ledger, non_standard.`);
|
|
1660
1664
|
}
|
|
1661
1665
|
|
|
1662
|
-
function rejectSemanticStageInputs(input) {
|
|
1666
|
+
function rejectSemanticStageInputs(input) {
|
|
1663
1667
|
const forbiddenKeys = [
|
|
1664
1668
|
'parseResult',
|
|
1665
1669
|
'prdFile',
|
|
@@ -1670,10 +1674,86 @@ function rejectSemanticStageInputs(input) {
|
|
|
1670
1674
|
'fieldUiMeta',
|
|
1671
1675
|
];
|
|
1672
1676
|
const present = forbiddenKeys.filter((key) => Object.prototype.hasOwnProperty.call(input, key));
|
|
1673
|
-
if (present.length) {
|
|
1674
|
-
throw new Error(`worsoft_codegen_local_generate_frontend only accepts translated low-level generation parameters. Unsupported semantic-stage keys: ${present.join(', ')}`);
|
|
1675
|
-
}
|
|
1676
|
-
}
|
|
1677
|
+
if (present.length) {
|
|
1678
|
+
throw new Error(`worsoft_codegen_local_generate_frontend only accepts translated low-level generation parameters. Unsupported semantic-stage keys: ${present.join(', ')}`);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
function readJsonFile(filePath, label) {
|
|
1683
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
1684
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
1685
|
+
}
|
|
1686
|
+
const resolvedPath = path.resolve(filePath);
|
|
1687
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
1688
|
+
throw new Error(`${label} not found: ${resolvedPath}`);
|
|
1689
|
+
}
|
|
1690
|
+
try {
|
|
1691
|
+
return { path: resolvedPath, value: JSON.parse(fs.readFileSync(resolvedPath, 'utf8')) };
|
|
1692
|
+
} catch (error) {
|
|
1693
|
+
throw new Error(`${label} is not valid JSON: ${resolvedPath}. ${error.message}`);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
function copyIfPresent(target, source, keys) {
|
|
1698
|
+
if (!source || typeof source !== 'object') return;
|
|
1699
|
+
for (const key of keys) {
|
|
1700
|
+
if (target[key] === undefined && source[key] !== undefined && source[key] !== null && source[key] !== '') {
|
|
1701
|
+
target[key] = source[key];
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
function assertParseResultPayloadConsistency(parseResult, mcpPayload, parseResultPath) {
|
|
1707
|
+
const keys = ['pageType', 'style', 'targetViewDir', 'targetApiModule', 'targetI18nKey', 'billCode'];
|
|
1708
|
+
for (const key of keys) {
|
|
1709
|
+
if (parseResult[key] === undefined || parseResult[key] === null || parseResult[key] === '') continue;
|
|
1710
|
+
if (mcpPayload[key] === undefined || mcpPayload[key] === null || mcpPayload[key] === '') continue;
|
|
1711
|
+
if (String(parseResult[key]) !== String(mcpPayload[key])) {
|
|
1712
|
+
throw new Error(`parseResult.mcpPayload.${key} does not match parseResult.${key} in ${parseResultPath}`);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
function resolveGenerationInput(input) {
|
|
1718
|
+
if (!input.parseResultPath) {
|
|
1719
|
+
return { generationInput: input, parseResultPath: '' };
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
const parsed = readJsonFile(input.parseResultPath, 'parseResultPath');
|
|
1723
|
+
const parseResult = parsed.value;
|
|
1724
|
+
if (!parseResult || typeof parseResult !== 'object' || Array.isArray(parseResult)) {
|
|
1725
|
+
throw new Error(`parseResultPath must contain a JSON object: ${parsed.path}`);
|
|
1726
|
+
}
|
|
1727
|
+
if (!parseResult.mcpPayload || typeof parseResult.mcpPayload !== 'object' || Array.isArray(parseResult.mcpPayload)) {
|
|
1728
|
+
throw new Error(`parseResult.mcpPayload missing in ${parsed.path}`);
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
const generationInput = { ...parseResult.mcpPayload };
|
|
1732
|
+
copyIfPresent(generationInput, parseResult, [
|
|
1733
|
+
'featureTitle',
|
|
1734
|
+
'billCode',
|
|
1735
|
+
'tableName',
|
|
1736
|
+
'tableComment',
|
|
1737
|
+
'apiPath',
|
|
1738
|
+
'pageType',
|
|
1739
|
+
'style',
|
|
1740
|
+
'targetViewDir',
|
|
1741
|
+
'targetApiModule',
|
|
1742
|
+
'targetI18nKey',
|
|
1743
|
+
'frontendPath',
|
|
1744
|
+
'moduleName',
|
|
1745
|
+
]);
|
|
1746
|
+
assertParseResultPayloadConsistency(parseResult, generationInput, parsed.path);
|
|
1747
|
+
|
|
1748
|
+
for (const key of ['frontendPath', 'moduleName', 'writeToDisk', 'overwrite', 'writeSupportFiles', 'mergeI18nZh']) {
|
|
1749
|
+
if (Object.prototype.hasOwnProperty.call(input, key)) {
|
|
1750
|
+
generationInput[key] = input[key];
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
generationInput.parseResultPath = parsed.path;
|
|
1755
|
+
return { generationInput, parseResultPath: parsed.path };
|
|
1756
|
+
}
|
|
1677
1757
|
|
|
1678
1758
|
function validatePageTypeAndStyle(pageType, style) {
|
|
1679
1759
|
if (!pageType) return;
|
|
@@ -2670,15 +2750,15 @@ function renderMultiLevelApiFunctions(moduleModel) {
|
|
|
2670
2750
|
' });',
|
|
2671
2751
|
'}',
|
|
2672
2752
|
'',
|
|
2673
|
-
`// 导出${title}`,
|
|
2674
|
-
`export function export${moduleModel.className}Obj(query?: any) {`,
|
|
2675
|
-
' return request({',
|
|
2676
|
-
` url: '${basePath}/export',`,
|
|
2677
|
-
" method: '
|
|
2678
|
-
'
|
|
2679
|
-
" responseType: 'blob',",
|
|
2680
|
-
' });',
|
|
2681
|
-
'}',
|
|
2753
|
+
`// 导出${title}`,
|
|
2754
|
+
`export function export${moduleModel.className}Obj(query?: any) {`,
|
|
2755
|
+
' return request({',
|
|
2756
|
+
` url: '${basePath}/export',`,
|
|
2757
|
+
" method: 'post',",
|
|
2758
|
+
' data: query,',
|
|
2759
|
+
" responseType: 'blob',",
|
|
2760
|
+
' });',
|
|
2761
|
+
'}',
|
|
2682
2762
|
'',
|
|
2683
2763
|
`// 启用${title}`,
|
|
2684
2764
|
`export function enable${moduleModel.className}(id: string | number) {`,
|
|
@@ -4231,20 +4311,22 @@ function renderFiles(model, stylePreset, sharedSupport, localeZhSupport) {
|
|
|
4231
4311
|
return files;
|
|
4232
4312
|
}
|
|
4233
4313
|
|
|
4234
|
-
function ensureArguments(input) {
|
|
4235
|
-
if (!input || typeof input !== 'object') throw new Error('Arguments must be an object');
|
|
4236
|
-
rejectSemanticStageInputs(input);
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
const
|
|
4314
|
+
function ensureArguments(input) {
|
|
4315
|
+
if (!input || typeof input !== 'object') throw new Error('Arguments must be an object');
|
|
4316
|
+
rejectSemanticStageInputs(input);
|
|
4317
|
+
const resolved = resolveGenerationInput(input);
|
|
4318
|
+
const generationInput = resolved.generationInput;
|
|
4319
|
+
for (const key of ['tableName', 'style', 'frontendPath']) {
|
|
4320
|
+
if (generationInput[key] === undefined || generationInput[key] === null || generationInput[key] === '') throw new Error(key + ' is required');
|
|
4321
|
+
}
|
|
4322
|
+
|
|
4323
|
+
const style = String(generationInput.style);
|
|
4324
|
+
const pageType = normalizePageTypeInput(generationInput.pageType);
|
|
4325
|
+
getStylePreset(style);
|
|
4326
|
+
validatePageTypeAndStyle(pageType, style);
|
|
4327
|
+
const isLevelStyle = isLevelBasedStyle(style);
|
|
4328
|
+
const fields = isLevelStyle ? [] : normalizeStructuredFieldArray(generationInput.fields, 'fields');
|
|
4329
|
+
const levels = isLevelStyle ? normalizeLevelsInput(generationInput.levels) : [];
|
|
4248
4330
|
|
|
4249
4331
|
if (isLevelStyle && !levels.length) {
|
|
4250
4332
|
throw new Error(`${style} requires levels[]`);
|
|
@@ -4254,29 +4336,30 @@ function ensureArguments(input) {
|
|
|
4254
4336
|
throw new Error('fields must be a non-empty array');
|
|
4255
4337
|
}
|
|
4256
4338
|
|
|
4257
|
-
return {
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
}
|
|
4339
|
+
return {
|
|
4340
|
+
parseResultPath: resolved.parseResultPath,
|
|
4341
|
+
featureTitle: generationInput.featureTitle ? String(generationInput.featureTitle) : '',
|
|
4342
|
+
billCode: generationInput.billCode ? String(generationInput.billCode).trim() : '',
|
|
4343
|
+
tableName: String(generationInput.tableName),
|
|
4344
|
+
tableComment: generationInput.tableComment ? String(generationInput.tableComment) : '',
|
|
4345
|
+
apiPath: normalizeApiPath(generationInput.apiPath),
|
|
4346
|
+
pageType,
|
|
4347
|
+
style,
|
|
4348
|
+
fields,
|
|
4349
|
+
levels,
|
|
4350
|
+
children: normalizeChildrenInput(generationInput.children),
|
|
4351
|
+
frontendPath: String(generationInput.frontendPath),
|
|
4352
|
+
moduleName: generationInput.moduleName ? String(generationInput.moduleName) : 'admin/test',
|
|
4353
|
+
targetViewDir: generationInput.targetViewDir ? String(generationInput.targetViewDir) : '',
|
|
4354
|
+
targetApiModule: generationInput.targetApiModule ? String(generationInput.targetApiModule) : '',
|
|
4355
|
+
targetI18nKey: generationInput.targetI18nKey ? String(generationInput.targetI18nKey) : '',
|
|
4356
|
+
extraApis: generationInput.extraApis === undefined ? [] : generationInput.extraApis,
|
|
4357
|
+
writeToDisk: generationInput.writeToDisk === undefined ? true : Boolean(generationInput.writeToDisk),
|
|
4358
|
+
overwrite: generationInput.overwrite === undefined ? true : Boolean(generationInput.overwrite),
|
|
4359
|
+
writeSupportFiles: generationInput.writeSupportFiles === undefined ? true : Boolean(generationInput.writeSupportFiles),
|
|
4360
|
+
mergeI18nZh: generationInput.mergeI18nZh === undefined ? true : Boolean(generationInput.mergeI18nZh),
|
|
4361
|
+
};
|
|
4362
|
+
}
|
|
4280
4363
|
|
|
4281
4364
|
function maybeWriteFiles(files, writeToDisk, overwrite) {
|
|
4282
4365
|
if (!writeToDisk) return;
|
package/package.json
CHANGED