worsoft-frontend-codegen-local-mcp 0.1.16 → 0.1.18

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.
@@ -10,7 +10,7 @@ export function fetchList(query?: object) {
10
10
 
11
11
  export function addObj(obj?: object) {
12
12
  return request({
13
- url: '/{{API_PATH}}',
13
+ url: '/{{API_PATH}}/save',
14
14
  method: 'post',
15
15
  data: obj,
16
16
  });
@@ -18,7 +18,7 @@ export function addObj(obj?: object) {
18
18
 
19
19
  export function getObj(obj?: object) {
20
20
  return request({
21
- url: '/{{API_PATH}}/details',
21
+ url: '/{{API_PATH}}/getById',
22
22
  method: 'get',
23
23
  params: obj,
24
24
  });
@@ -26,24 +26,16 @@ export function getObj(obj?: object) {
26
26
 
27
27
  export function delObjs(ids?: object) {
28
28
  return request({
29
- url: '/{{API_PATH}}',
30
- method: 'delete',
29
+ url: '/{{API_PATH}}/removeByIds',
30
+ method: 'post',
31
31
  data: ids,
32
32
  });
33
33
  }
34
34
 
35
35
  export function putObj(obj?: object) {
36
36
  return request({
37
- url: '/{{API_PATH}}',
38
- method: 'put',
37
+ url: '/{{API_PATH}}/updateById',
38
+ method: 'post',
39
39
  data: obj,
40
40
  });
41
41
  }
42
-
43
- export function delChildObj(ids?: object, childTableName?: string) {
44
- return request({
45
- url: '/{{API_PATH}}/child',
46
- method: 'delete',
47
- data: childTableName ? { ids, childTableName } : ids,
48
- });
49
- }
@@ -31,7 +31,7 @@
31
31
  <script setup lang="ts" name="{{CLASS_NAME}}Form">
32
32
  import mittBus from '/@/utils/mitt';
33
33
  import { useMessage } from '/@/hooks/message';
34
- import { getObj, addObj, putObj, delChildObj } from '/@/api/{{API_MODULE_PATH}}';
34
+ import { getObj, addObj, putObj } from '/@/api/{{API_MODULE_PATH}}';
35
35
  import { useDict } from '/@/hooks/dict';
36
36
  import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
37
37
  import { useI18n } from 'vue-i18n';
@@ -49,36 +49,30 @@ const dataFormRef = ref();
49
49
  const loading = ref(false);
50
50
  const detail = ref(false);
51
51
 
52
- // 主表字段的双语、字典和校验提示统一由公共 hook 提供
53
- const getChildFieldMeta = (groupName: string, prop: string) => childFieldGroups[groupName]?.[prop];
54
- const resolveLabel = (labelKey?: string, fallback = '') => {
55
- if (!labelKey) return fallback;
56
- const translated = t(labelKey);
57
- return translated === labelKey ? fallback : translated;
58
- };
59
- const getChildFieldLabel = (groupName: string, prop: string) => {
60
- const config = getChildFieldMeta(groupName, prop);
61
- return resolveLabel(config?.labelKey, config?.label || prop);
62
- };
52
+ // 主表和子表字段的双语、字典和校验提示统一由公共 hook 提供
63
53
  const {
64
54
  getFieldMeta: getMasterFieldMeta,
65
55
  getFieldLabel: getMasterFieldLabel,
56
+ getChildFieldLabel,
66
57
  getDictOptions,
67
58
  inputPlaceholder,
68
59
  selectPlaceholder,
69
60
  fieldRequiredMessage,
70
61
  commonActionLabel,
71
- } = useCrudPageMeta(dataMasterEntity, dictRefs);
62
+ } = useCrudPageMeta(dataMasterEntity, dictRefs, childFieldGroups);
72
63
 
73
64
  // 子表分组标题从功能级词条中读取
74
65
  const childSectionTitle = (groupName: string) => t(`${pageI18nKey}.children.${groupName}.title`);
75
66
 
76
- // 表单数据模型,包含主表字段和多个子表列表
77
- const form = reactive({
67
+ // 统一维护表单默认值,初始化和重置都复用这一份
68
+ const createDefaultFormState = () => ({
78
69
  {{FORM_DEFAULTS}}
79
70
  {{CHILD_FORM_LIST_DEFAULTS}}
80
71
  });
81
72
 
73
+ // 表单数据模型,包含主表字段和多个子表列表
74
+ const form = reactive(createDefaultFormState());
75
+
82
76
  // 各子表新增行时使用的临时对象
83
77
  {{CHILD_TEMP_DECLARATIONS}}
84
78
 
@@ -92,7 +86,7 @@ const get{{CLASS_NAME}}Data = async (id: string) => {
92
86
  try {
93
87
  loading.value = true;
94
88
  const { data } = await getObj({ {{PK_ATTR}}: id });
95
- Object.assign(form, data[0] || {});
89
+ Object.assign(form, Array.isArray(data) ? data[0] || {} : data || {});
96
90
  } catch (error) {
97
91
  useMessage().error(t('common.messages.fetchError'));
98
92
  } finally {
@@ -102,10 +96,7 @@ const get{{CLASS_NAME}}Data = async (id: string) => {
102
96
 
103
97
  // 回到初始表单状态,供新增和页面切换时复用
104
98
  const resetFormState = () => {
105
- Object.assign(form, {
106
- {{FORM_DEFAULTS}}
107
- {{CHILD_FORM_LIST_DEFAULTS}}
108
- });
99
+ Object.assign(form, createDefaultFormState());
109
100
  nextTick(() => {
110
101
  dataFormRef.value?.resetFields();
111
102
  {{CHILD_RESET_LISTS}}
@@ -162,16 +153,9 @@ const onSubmit = async (actionType?: string) => {
162
153
  }
163
154
  };
164
155
 
165
- // 删除子表行,已落库的数据先调用后端删除
166
- const deleteChild = async (obj: Record<string, any>, childPkAttr: string, childTableName?: string) => {
167
- if (obj[childPkAttr]) {
168
- try {
169
- await delChildObj([obj[childPkAttr]], childTableName);
170
- useMessage().success(t('common.delSuccessText'));
171
- } catch (err: any) {
172
- useMessage().error(err.msg || t('common.delBtn'));
173
- }
174
- }
156
+ // 子表删除只处理前端行状态,统一随主表保存时提交
157
+ const deleteChild = (_obj: Record<string, any>, _childPkAttr: string) => {
158
+ return true;
175
159
  };
176
160
 
177
161
  onMounted(() => {
@@ -41,10 +41,12 @@ const {
41
41
  } = useCrudPageMeta(dataMasterEntity, dictRefs);
42
42
 
43
43
  // 表单数据模型
44
- const form = reactive({
44
+ const createDefaultFormState = () => ({
45
45
  {{FORM_DEFAULTS}}
46
46
  });
47
47
 
48
+ const form = reactive(createDefaultFormState());
49
+
48
50
  // 表单校验规则,仅保留当前需求中明确必填的字段
49
51
  const dataRules = ref({
50
52
  {{FORM_RULES}}
@@ -55,7 +57,7 @@ const get{{CLASS_NAME}}Data = async (id: string) => {
55
57
  try {
56
58
  loading.value = true;
57
59
  const { data } = await getObj({ {{PK_ATTR}}: id });
58
- Object.assign(form, data[0] || {});
60
+ Object.assign(form, Array.isArray(data) ? data[0] || {} : data || {});
59
61
  } catch (error) {
60
62
  useMessage().error(t('common.messages.fetchError'));
61
63
  } finally {
@@ -65,9 +67,7 @@ const get{{CLASS_NAME}}Data = async (id: string) => {
65
67
 
66
68
  // 回到初始表单状态,供新增和弹窗切换时复用
67
69
  const resetFormState = () => {
68
- Object.assign(form, {
69
- {{FORM_DEFAULTS}}
70
- });
70
+ Object.assign(form, createDefaultFormState());
71
71
  nextTick(() => {
72
72
  dataFormRef.value?.resetFields();
73
73
  });
@@ -55,10 +55,12 @@ const {
55
55
  } = useCrudPageMeta(dataMasterEntity, dictRefs);
56
56
 
57
57
  // 表单数据模型
58
- const form = reactive({
58
+ const createDefaultFormState = () => ({
59
59
  {{FORM_DEFAULTS}}
60
60
  });
61
61
 
62
+ const form = reactive(createDefaultFormState());
63
+
62
64
  // 表单校验规则,仅保留当前需求中明确必填的字段
63
65
  const dataRules = ref({
64
66
  {{FORM_RULES}}
@@ -69,7 +71,7 @@ const get{{CLASS_NAME}}Data = async (id: string) => {
69
71
  try {
70
72
  loading.value = true;
71
73
  const { data } = await getObj({ {{PK_ATTR}}: id });
72
- Object.assign(form, data[0] || {});
74
+ Object.assign(form, Array.isArray(data) ? data[0] || {} : data || {});
73
75
  } catch (error) {
74
76
  useMessage().error(t('common.messages.fetchError'));
75
77
  } finally {
@@ -79,9 +81,7 @@ const get{{CLASS_NAME}}Data = async (id: string) => {
79
81
 
80
82
  // 回到初始表单状态,供新增和页面切换时复用
81
83
  const resetFormState = () => {
82
- Object.assign(form, {
83
- {{FORM_DEFAULTS}}
84
- });
84
+ Object.assign(form, createDefaultFormState());
85
85
  nextTick(() => {
86
86
  dataFormRef.value?.resetFields();
87
87
  });
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.16';
8
+ const SERVER_VERSION = '0.1.18';
9
9
  const PROTOCOL_VERSION = '2024-11-05';
10
10
  const TOOL_NAME = 'worsoft_codegen_local_generate_frontend';
11
11
  const TEMPLATE_LIBRARY_ROOT = path.resolve(__dirname, '..', 'template');
@@ -130,25 +130,27 @@ const TOOL_SCHEMA = {
130
130
  designFile: { type: 'string', description: 'Absolute or relative Markdown design file path. Defaults to ../sql/SQL 设计说明.md when omitted.' },
131
131
  tableName: { type: 'string', description: 'Target main table name from the design file.' },
132
132
  style: { type: 'string', enum: Object.keys(STYLE_CATALOG), description: 'Style id from assets/style-catalog.json.' },
133
- children: {
134
- type: 'array',
135
- description: 'Optional direct child-table relations for master_child_jump. When provided, MCP renders all listed direct child tables on the same main form.',
136
- items: {
137
- type: 'object',
138
- properties: {
139
- childTableName: { type: 'string', description: 'Child table name.' },
140
- mainField: { type: 'string', description: 'Main table relation field.' },
141
- childField: { type: 'string', description: 'Child table relation field.' },
142
- relationType: { type: 'string', description: 'Optional relation type label, for example 1:N.' },
143
- },
144
- required: ['childTableName', 'mainField', 'childField'],
145
- additionalProperties: false,
146
- },
147
- },
148
- childTableName: { type: 'string', description: 'Child table name. Required when style=master_child_jump.' },
149
- mainField: { type: 'string', description: 'Main table relation field. Required when style=master_child_jump.' },
150
- childField: { type: 'string', description: 'Child table relation field. Required when style=master_child_jump.' },
151
- relationType: { type: 'string', description: 'Optional relation type label, for example 1:N.' },
133
+ children: {
134
+ type: 'array',
135
+ description: 'Optional direct child-table relations for master_child_jump. When provided, MCP renders all listed direct child tables on the same main form.',
136
+ items: {
137
+ type: 'object',
138
+ properties: {
139
+ childTableName: { type: 'string', description: 'Child table name.' },
140
+ mainField: { type: 'string', description: 'Main table relation field.' },
141
+ childField: { type: 'string', description: 'Child table relation field.' },
142
+ payloadField: { type: 'string', description: 'Optional backend payload list field name from the API document, for example certificateList.' },
143
+ relationType: { type: 'string', description: 'Optional relation type label, for example 1:N.' },
144
+ },
145
+ required: ['childTableName', 'mainField', 'childField'],
146
+ additionalProperties: false,
147
+ },
148
+ },
149
+ childTableName: { type: 'string', description: 'Child table name. Required when style=master_child_jump.' },
150
+ mainField: { type: 'string', description: 'Main table relation field. Required when style=master_child_jump.' },
151
+ childField: { type: 'string', description: 'Child table relation field. Required when style=master_child_jump.' },
152
+ childPayloadField: { type: 'string', description: 'Optional backend payload list field name for the legacy single-child mode, for example certificateList.' },
153
+ relationType: { type: 'string', description: 'Optional relation type label, for example 1:N.' },
152
154
  frontendPath: { type: 'string', description: 'Absolute frontend output root path.' },
153
155
  moduleName: { type: 'string', description: 'Relative frontend module path, for example admin/test.' },
154
156
  writeToDisk: { type: 'boolean', default: true, description: 'Whether to write generated files.' },
@@ -870,14 +872,18 @@ function resolveSourcePath(inputPath) {
870
872
  throw new Error('Source file does not exist: ' + resolvedPath);
871
873
  }
872
874
 
873
- function formatRelationCandidate(relation) {
874
- return {
875
- childTableName: relation.childTableName,
876
- mainField: relation.mainField,
877
- childField: relation.childField,
878
- relationType: relation.relationType || '',
879
- };
880
- }
875
+ function formatRelationCandidate(relation) {
876
+ const candidate = {
877
+ childTableName: relation.childTableName,
878
+ mainField: relation.mainField,
879
+ childField: relation.childField,
880
+ relationType: relation.relationType || '',
881
+ };
882
+ if (relation.payloadField) {
883
+ candidate.payloadField = relation.payloadField;
884
+ }
885
+ return candidate;
886
+ }
881
887
 
882
888
  function getRelationCandidates(designDoc, tableName) {
883
889
  return designDoc.relations
@@ -899,14 +905,15 @@ function buildRetryArguments(safeArgs, sourceFile, childTableName) {
899
905
  retryArguments.designFile = sourceFile;
900
906
  }
901
907
 
902
- if (safeArgs.children && safeArgs.children.length) {
903
- retryArguments.children = safeArgs.children.map((relation) => ({
904
- childTableName: relation.childTableName,
905
- mainField: relation.mainField,
906
- childField: relation.childField,
907
- relationType: relation.relationType || '',
908
- }));
909
- }
908
+ if (safeArgs.children && safeArgs.children.length) {
909
+ retryArguments.children = safeArgs.children.map((relation) => ({
910
+ childTableName: relation.childTableName,
911
+ mainField: relation.mainField,
912
+ childField: relation.childField,
913
+ ...(relation.payloadField ? { payloadField: relation.payloadField } : {}),
914
+ relationType: relation.relationType || '',
915
+ }));
916
+ }
910
917
 
911
918
  if (childTableName) {
912
919
  retryArguments.childTableName = childTableName;
@@ -916,9 +923,13 @@ function buildRetryArguments(safeArgs, sourceFile, childTableName) {
916
923
  retryArguments.mainField = safeArgs.mainField;
917
924
  }
918
925
 
919
- if (safeArgs.childField) {
920
- retryArguments.childField = safeArgs.childField;
921
- }
926
+ if (safeArgs.childField) {
927
+ retryArguments.childField = safeArgs.childField;
928
+ }
929
+
930
+ if (safeArgs.childPayloadField) {
931
+ retryArguments.childPayloadField = safeArgs.childPayloadField;
932
+ }
922
933
 
923
934
  if (safeArgs.relationType) {
924
935
  retryArguments.relationType = safeArgs.relationType;
@@ -970,7 +981,7 @@ function loadSourceDocument(safeArgs) {
970
981
  };
971
982
  }
972
983
 
973
- function normalizeChildrenInput(inputChildren) {
984
+ function normalizeChildrenInput(inputChildren) {
974
985
  if (inputChildren === undefined || inputChildren === null) {
975
986
  return [];
976
987
  }
@@ -981,28 +992,30 @@ function normalizeChildrenInput(inputChildren) {
981
992
  if (!item || typeof item !== 'object') {
982
993
  throw new Error('children[' + index + '] must be an object');
983
994
  }
984
- const childTableName = item.childTableName ? String(item.childTableName) : '';
985
- const mainField = item.mainField ? String(item.mainField) : '';
986
- const childField = item.childField ? String(item.childField) : '';
987
- const relationType = item.relationType ? String(item.relationType) : '';
988
- const missingFields = ['childTableName', 'mainField', 'childField'].filter((field) => !({ childTableName, mainField, childField })[field]);
989
- if (missingFields.length) {
990
- throw new Error('children[' + index + '] is missing required fields: ' + missingFields.join(', '));
991
- }
992
- return { childTableName, mainField, childField, relationType };
993
- });
994
- }
995
+ const childTableName = item.childTableName ? String(item.childTableName) : '';
996
+ const mainField = item.mainField ? String(item.mainField) : '';
997
+ const childField = item.childField ? String(item.childField) : '';
998
+ const payloadField = item.payloadField ? String(item.payloadField) : '';
999
+ const relationType = item.relationType ? String(item.relationType) : '';
1000
+ const missingFields = ['childTableName', 'mainField', 'childField'].filter((field) => !({ childTableName, mainField, childField })[field]);
1001
+ if (missingFields.length) {
1002
+ throw new Error('children[' + index + '] is missing required fields: ' + missingFields.join(', '));
1003
+ }
1004
+ return { childTableName, mainField, childField, payloadField, relationType };
1005
+ });
1006
+ }
995
1007
 
996
1008
  function resolveMarkdownChildRelations(sourceDocument, safeArgs) {
997
1009
  if (safeArgs.children && safeArgs.children.length) {
998
1010
  return safeArgs.children.map((relation) => ({
999
1011
  mainTableName: safeArgs.tableName,
1000
- childTableName: relation.childTableName,
1001
- mainField: relation.mainField,
1002
- childField: relation.childField,
1003
- relationType: relation.relationType || '',
1004
- }));
1005
- }
1012
+ childTableName: relation.childTableName,
1013
+ mainField: relation.mainField,
1014
+ childField: relation.childField,
1015
+ payloadField: relation.payloadField || '',
1016
+ relationType: relation.relationType || '',
1017
+ }));
1018
+ }
1006
1019
 
1007
1020
  const missingFields = ['childTableName', 'mainField', 'childField'].filter((field) => !safeArgs[field]);
1008
1021
  if (missingFields.length) {
@@ -1020,31 +1033,34 @@ function resolveMarkdownChildRelations(sourceDocument, safeArgs) {
1020
1033
  example: {
1021
1034
  ...buildRetryArguments(safeArgs, sourceDocument.path, safeArgs.childTableName || '<child_table_name>'),
1022
1035
  children: [
1023
- {
1024
- childTableName: safeArgs.childTableName || '<child_table_name>',
1025
- mainField: safeArgs.mainField || '<main_field>',
1026
- childField: safeArgs.childField || '<child_field>',
1027
- relationType: safeArgs.relationType || '1:N',
1028
- },
1029
- ],
1030
- mainField: safeArgs.mainField || '<main_field>',
1031
- childField: safeArgs.childField || '<child_field>',
1032
- },
1033
- },
1034
- };
1036
+ {
1037
+ childTableName: safeArgs.childTableName || '<child_table_name>',
1038
+ mainField: safeArgs.mainField || '<main_field>',
1039
+ childField: safeArgs.childField || '<child_field>',
1040
+ ...(safeArgs.childPayloadField ? { payloadField: safeArgs.childPayloadField } : {}),
1041
+ relationType: safeArgs.relationType || '1:N',
1042
+ },
1043
+ ],
1044
+ mainField: safeArgs.mainField || '<main_field>',
1045
+ childField: safeArgs.childField || '<child_field>',
1046
+ ...(safeArgs.childPayloadField ? { childPayloadField: safeArgs.childPayloadField } : {}),
1047
+ },
1048
+ },
1049
+ };
1035
1050
  throw error;
1036
1051
  }
1037
1052
 
1038
1053
  return [
1039
1054
  {
1040
1055
  mainTableName: safeArgs.tableName,
1041
- childTableName: safeArgs.childTableName,
1042
- mainField: safeArgs.mainField,
1043
- childField: safeArgs.childField,
1044
- relationType: safeArgs.relationType || '',
1045
- },
1046
- ];
1047
- }
1056
+ childTableName: safeArgs.childTableName,
1057
+ mainField: safeArgs.mainField,
1058
+ childField: safeArgs.childField,
1059
+ payloadField: safeArgs.childPayloadField || '',
1060
+ relationType: safeArgs.relationType || '',
1061
+ },
1062
+ ];
1063
+ }
1048
1064
 
1049
1065
  function getStylePreset(styleId) {
1050
1066
  const preset = STYLE_CATALOG[styleId];
@@ -1075,28 +1091,33 @@ function ensureFieldExists(fields, fieldName, tableName, role) {
1075
1091
  return field;
1076
1092
  }
1077
1093
 
1078
- function buildChildModels(sourceDocument, safeArgs, mainParsed) {
1079
- if (safeArgs.style !== 'master_child_jump') return [];
1080
-
1081
- const relations = resolveMarkdownChildRelations(sourceDocument, safeArgs);
1082
- return relations.map((relation) => {
1094
+ function buildChildModels(sourceDocument, safeArgs, mainParsed) {
1095
+ if (safeArgs.style !== 'master_child_jump') return [];
1096
+
1097
+ const relations = resolveMarkdownChildRelations(sourceDocument, safeArgs);
1098
+ return relations.map((relation) => {
1083
1099
  const childParsed = parseTableFromMarkdownDesign(sourceDocument.designDoc, relation.childTableName);
1084
1100
  const childFields = normalizeFields(childParsed);
1085
1101
  const mainRelationField = ensureFieldExists(mainParsed.fields, relation.mainField, safeArgs.tableName, 'Main relation');
1086
1102
  const childRelationField = ensureFieldExists(childParsed.fields, relation.childField, relation.childTableName, 'Child relation');
1087
- const childVisibleFields = childFields.filter(
1088
- (field) => field.fieldName !== childParsed.pkField.fieldName && !field.isAudit && field.fieldName !== relation.childField
1089
- );
1090
-
1091
- return {
1092
- tableName: relation.childTableName,
1093
- tableComment: childParsed.tableComment,
1094
- className: toPascalCase(relation.childTableName),
1095
- functionName: toCamelCase(relation.childTableName),
1096
- listName: toCamelCase(relation.childTableName) + 'List',
1097
- pk: childParsed.pkField,
1098
- fields: childFields,
1099
- visibleFields: childVisibleFields,
1103
+ const childVisibleFields = childFields.filter(
1104
+ (field) => field.fieldName !== childParsed.pkField.fieldName && !field.isAudit && field.fieldName !== relation.childField
1105
+ );
1106
+ const payloadField =
1107
+ relation.payloadField || (relations.length === 1 && safeArgs.childPayloadField ? safeArgs.childPayloadField : '') || '';
1108
+ const listName = payloadField || toCamelCase(relation.childTableName) + 'List';
1109
+
1110
+ return {
1111
+ tableName: relation.childTableName,
1112
+ tableComment: childParsed.tableComment,
1113
+ className: toPascalCase(relation.childTableName),
1114
+ functionName: toCamelCase(relation.childTableName),
1115
+ listName,
1116
+ payloadField,
1117
+ payloadFieldSource: payloadField ? 'arguments' : 'derived',
1118
+ pk: childParsed.pkField,
1119
+ fields: childFields,
1120
+ visibleFields: childVisibleFields,
1100
1121
  mainField: mainRelationField,
1101
1122
  childField: childRelationField,
1102
1123
  relationType: relation.relationType,
@@ -1285,12 +1306,9 @@ function renderChildTempDeclaration(childModel) {
1285
1306
  ].join('\n');
1286
1307
  }
1287
1308
 
1288
- function renderChildSection(childModel, childCount) {
1289
- const title = childModel.tableComment.replace(/'/g, "\\'");
1290
- const deleteExpression =
1291
- childCount > 1
1292
- ? `deleteChild(obj, '${childModel.pk.attrName}', '${childModel.tableName}')`
1293
- : `deleteChild(obj, '${childModel.pk.attrName}')`;
1309
+ function renderChildSection(childModel, childCount) {
1310
+ const title = childModel.tableComment.replace(/'/g, "\\'");
1311
+ const deleteExpression = `deleteChild(obj, '${childModel.pk.attrName}')`;
1294
1312
 
1295
1313
  return [
1296
1314
  ' <el-col :span="24" class="mb20">',
@@ -1541,11 +1559,8 @@ function renderChildTableColumnV2(field, childListName) {
1541
1559
  ].join('\n');
1542
1560
  }
1543
1561
 
1544
- function renderChildSectionV2(childModel, childCount) {
1545
- const deleteExpression =
1546
- childCount > 1
1547
- ? `deleteChild(obj, '${childModel.pk.attrName}', '${childModel.tableName}')`
1548
- : `deleteChild(obj, '${childModel.pk.attrName}')`;
1562
+ function renderChildSectionV2(childModel, childCount) {
1563
+ const deleteExpression = `deleteChild(obj, '${childModel.pk.attrName}')`;
1549
1564
 
1550
1565
  return [
1551
1566
  ' <el-col :span="24" class="mb20">',
@@ -1666,11 +1681,12 @@ function ensureArguments(input) {
1666
1681
  designFile: input.designFile ? String(input.designFile) : null,
1667
1682
  tableName: String(input.tableName),
1668
1683
  style,
1669
- children: normalizeChildrenInput(input.children),
1670
- childTableName: input.childTableName ? String(input.childTableName) : null,
1671
- mainField: input.mainField ? String(input.mainField) : null,
1672
- childField: input.childField ? String(input.childField) : null,
1673
- relationType: input.relationType ? String(input.relationType) : '',
1684
+ children: normalizeChildrenInput(input.children),
1685
+ childTableName: input.childTableName ? String(input.childTableName) : null,
1686
+ mainField: input.mainField ? String(input.mainField) : null,
1687
+ childField: input.childField ? String(input.childField) : null,
1688
+ childPayloadField: input.childPayloadField ? String(input.childPayloadField) : null,
1689
+ relationType: input.relationType ? String(input.relationType) : '',
1674
1690
  frontendPath: String(input.frontendPath),
1675
1691
  moduleName: input.moduleName ? String(input.moduleName) : 'admin/test',
1676
1692
  writeToDisk: input.writeToDisk === undefined ? true : Boolean(input.writeToDisk),
@@ -1700,14 +1716,16 @@ function maybeWriteFiles(files, writeToDisk, overwrite) {
1700
1716
  }
1701
1717
 
1702
1718
  function buildManifest(model, safeArgs, stylePreset, sharedTemplates, files, note) {
1703
- const relations = model.children.map((childModel) => ({
1704
- childTableName: childModel.tableName,
1705
- childTableComment: childModel.tableComment,
1706
- mainField: childModel.mainField.fieldName,
1707
- childField: childModel.childField.fieldName,
1708
- childListName: childModel.listName,
1709
- relationType: childModel.relationType || '',
1710
- }));
1719
+ const relations = model.children.map((childModel) => ({
1720
+ childTableName: childModel.tableName,
1721
+ childTableComment: childModel.tableComment,
1722
+ mainField: childModel.mainField.fieldName,
1723
+ childField: childModel.childField.fieldName,
1724
+ childListName: childModel.listName,
1725
+ payloadField: childModel.payloadField || '',
1726
+ payloadFieldSource: childModel.payloadFieldSource || 'derived',
1727
+ relationType: childModel.relationType || '',
1728
+ }));
1711
1729
  const selectedList = relations.map((relation) => formatRelationCandidate(relation));
1712
1730
 
1713
1731
  return {
@@ -1743,18 +1761,31 @@ function buildManifest(model, safeArgs, stylePreset, sharedTemplates, files, not
1743
1761
  },
1744
1762
  }
1745
1763
  : null,
1746
- summary: {
1747
- totalFields: model.fields.length,
1748
- visibleFields: model.visibleFields.length,
1749
- dictFields: model.visibleFields.filter((field) => field.dictType).map((field) => field.attrName),
1750
- skippedAuditFields: model.fields.filter((field) => field.isAudit).map((field) => field.fieldName),
1751
- childCount: model.children.length,
1752
- childTables: model.children.map((childModel) => childModel.tableName),
1753
- childVisibleFields: model.children.reduce((sum, childModel) => sum + childModel.visibleFields.length, 0),
1754
- },
1755
- note,
1756
- };
1757
- }
1764
+ summary: {
1765
+ totalFields: model.fields.length,
1766
+ visibleFields: model.visibleFields.length,
1767
+ dictFields: model.visibleFields.filter((field) => field.dictType).map((field) => field.attrName),
1768
+ skippedAuditFields: model.fields.filter((field) => field.isAudit).map((field) => field.fieldName),
1769
+ childCount: model.children.length,
1770
+ childTables: model.children.map((childModel) => childModel.tableName),
1771
+ childPayloadFields: model.children.map((childModel) => ({ childTableName: childModel.tableName, payloadField: childModel.payloadField || childModel.listName })),
1772
+ childVisibleFields: model.children.reduce((sum, childModel) => sum + childModel.visibleFields.length, 0),
1773
+ },
1774
+ structureValidation: model.children.length
1775
+ ? {
1776
+ source: 'arguments',
1777
+ children: model.children.map((childModel) => ({
1778
+ childTableName: childModel.tableName,
1779
+ childListName: childModel.listName,
1780
+ payloadField: childModel.payloadField || '',
1781
+ payloadFieldSource: childModel.payloadFieldSource || 'derived',
1782
+ matchesExplicitPayloadField: Boolean(childModel.payloadField),
1783
+ })),
1784
+ }
1785
+ : null,
1786
+ note,
1787
+ };
1788
+ }
1758
1789
 
1759
1790
  async function handleToolCall(argumentsObject) {
1760
1791
  const safeArgs = ensureArguments(argumentsObject);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worsoft-frontend-codegen-local-mcp",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "Worsoft frontend local-template code generation MCP server.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "worsoft <sw@worsoft.vip>",