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 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
- Single table:
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
- Master-child:
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`, MCP does not infer relations.
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
- exportUrl: '/{{API_PATH}}/export',
70
- exportFileName: '{{FUNCTION_NAME}}.xlsx',
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
- exportUrl: '/{{API_PATH}}/export',
70
- exportFileName: '{{FUNCTION_NAME}}.xlsx',
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
- exportUrl: '/{{API_PATH}}/export',
95
- exportFileName: '{{FUNCTION_NAME}}.xlsx',
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
- exportUrl: '/{{API_PATH}}/export',
89
- exportFileName: '{{FUNCTION_NAME}}.xlsx',
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
- exportUrl: '/{{API_PATH}}/export',
96
- exportFileName: '{{FUNCTION_NAME}}.xlsx',
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.85';
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: 'get',
210
- params: query,
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: 'Final style id from parseResult or translated mcpPayload. MCP validates it but does not infer it.' },
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: 'Structured main-table field metadata already translated by the caller. MCP only consumes these low-level generation parameters.',
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: ['tableName', 'style', 'frontendPath'],
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 < 0) {
1374
- throw new Error(`${label} must be a non-negative integer when provided, or empty when unspecified`);
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: 'get',",
2678
- ' params: query,',
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
- for (const key of TOOL_SCHEMA.required) {
4238
- if (input[key] === undefined || input[key] === null || input[key] === '') throw new Error(key + ' is required');
4239
- }
4240
-
4241
- const style = String(input.style);
4242
- const pageType = normalizePageTypeInput(input.pageType);
4243
- getStylePreset(style);
4244
- validatePageTypeAndStyle(pageType, style);
4245
- const isLevelStyle = isLevelBasedStyle(style);
4246
- const fields = isLevelStyle ? [] : normalizeStructuredFieldArray(input.fields, 'fields');
4247
- const levels = isLevelStyle ? normalizeLevelsInput(input.levels) : [];
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
- featureTitle: input.featureTitle ? String(input.featureTitle) : '',
4259
- billCode: input.billCode ? String(input.billCode).trim() : '',
4260
- tableName: String(input.tableName),
4261
- tableComment: input.tableComment ? String(input.tableComment) : '',
4262
- apiPath: normalizeApiPath(input.apiPath),
4263
- pageType,
4264
- style,
4265
- fields,
4266
- levels,
4267
- children: normalizeChildrenInput(input.children),
4268
- frontendPath: String(input.frontendPath),
4269
- moduleName: input.moduleName ? String(input.moduleName) : 'admin/test',
4270
- targetViewDir: input.targetViewDir ? String(input.targetViewDir) : '',
4271
- targetApiModule: input.targetApiModule ? String(input.targetApiModule) : '',
4272
- targetI18nKey: input.targetI18nKey ? String(input.targetI18nKey) : '',
4273
- extraApis: input.extraApis === undefined ? [] : input.extraApis,
4274
- writeToDisk: input.writeToDisk === undefined ? true : Boolean(input.writeToDisk),
4275
- overwrite: input.overwrite === undefined ? true : Boolean(input.overwrite),
4276
- writeSupportFiles: input.writeSupportFiles === undefined ? true : Boolean(input.writeSupportFiles),
4277
- mergeI18nZh: input.mergeI18nZh === undefined ? true : Boolean(input.mergeI18nZh),
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worsoft-frontend-codegen-local-mcp",
3
- "version": "0.1.85",
3
+ "version": "0.1.88",
4
4
  "description": "Worsoft frontend local-template code generation MCP server.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "worsoft <sw@worsoft.vip>",