worsoft-frontend-codegen-local-mcp 0.1.2 → 0.1.4
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 +32 -3
- package/assets/templates/master_child_jump/api.tpl +2 -2
- package/assets/templates/master_child_jump/form.tpl +15 -19
- package/mcp_server.js +201 -73
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ This MCP generates Worsoft frontend files from a local Markdown design document.
|
|
|
24
24
|
- `mainField` when `style=master_child_jump`: required
|
|
25
25
|
- `childField` when `style=master_child_jump`: required
|
|
26
26
|
- `relationType` when `style=master_child_jump`: optional
|
|
27
|
+
- `children` when `style=master_child_jump`: optional array of direct child relations; when provided it overrides the legacy single-child fields
|
|
27
28
|
|
|
28
29
|
## Recommended MCP Arguments
|
|
29
30
|
|
|
@@ -57,6 +58,33 @@ Master-child:
|
|
|
57
58
|
}
|
|
58
59
|
```
|
|
59
60
|
|
|
61
|
+
Multi direct children on one main form:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"tableName": "iwm_sys_trade_level",
|
|
66
|
+
"style": "master_child_jump",
|
|
67
|
+
"children": [
|
|
68
|
+
{
|
|
69
|
+
"childTableName": "iwm_sys_trade_level_standard",
|
|
70
|
+
"mainField": "id",
|
|
71
|
+
"childField": "trade_level_id",
|
|
72
|
+
"relationType": "1:N"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"childTableName": "iwm_sys_trade_level_competency",
|
|
76
|
+
"mainField": "id",
|
|
77
|
+
"childField": "trade_level_id",
|
|
78
|
+
"relationType": "1:N"
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"designFile": "plugins/sql/SQL 璁捐璇存槑.md",
|
|
82
|
+
"frontendPath": "E:/own-worker-platform/trunk/worsoft-ui",
|
|
83
|
+
"moduleName": "admin/test",
|
|
84
|
+
"writeToDisk": false
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
60
88
|
## Smoke Test
|
|
61
89
|
|
|
62
90
|
PowerShell:
|
|
@@ -65,6 +93,7 @@ PowerShell:
|
|
|
65
93
|
powershell -ExecutionPolicy Bypass -File plugins/worsoft-codegen-local/smoke_test_mcp.ps1 -Scenario single
|
|
66
94
|
powershell -ExecutionPolicy Bypass -File plugins/worsoft-codegen-local/smoke_test_mcp.ps1 -Scenario master
|
|
67
95
|
powershell -ExecutionPolicy Bypass -File plugins/worsoft-codegen-local/smoke_test_mcp.ps1 -Scenario child
|
|
96
|
+
powershell -ExecutionPolicy Bypass -File plugins/worsoft-codegen-local/smoke_test_mcp.ps1 -Scenario multi
|
|
68
97
|
powershell -ExecutionPolicy Bypass -File plugins/worsoft-codegen-local/smoke_test_mcp.ps1 -Scenario missing_relation
|
|
69
98
|
```
|
|
70
99
|
|
|
@@ -74,6 +103,7 @@ Node:
|
|
|
74
103
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario single
|
|
75
104
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario master
|
|
76
105
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario child
|
|
106
|
+
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario multi
|
|
77
107
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario missing_relation
|
|
78
108
|
```
|
|
79
109
|
|
|
@@ -95,9 +125,8 @@ For `master_child_jump`, MCP does not infer relations from the design file anymo
|
|
|
95
125
|
|
|
96
126
|
The caller must provide:
|
|
97
127
|
|
|
98
|
-
- `
|
|
99
|
-
- `mainField`
|
|
100
|
-
- `childField`
|
|
128
|
+
- either `children[]`
|
|
129
|
+
- or the legacy single-child fields: `childTableName`, `mainField`, `childField`
|
|
101
130
|
|
|
102
131
|
If these fields are missing, MCP returns `relation_input_required`.
|
|
103
132
|
|
|
@@ -40,10 +40,10 @@ export function putObj(obj?: object) {
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export function delChildObj(ids?: object) {
|
|
43
|
+
export function delChildObj(ids?: object, childTableName?: string) {
|
|
44
44
|
return request({
|
|
45
45
|
url: '/{{API_PATH}}/child',
|
|
46
46
|
method: 'delete',
|
|
47
|
-
data: ids,
|
|
47
|
+
data: childTableName ? { ids, childTableName } : ids,
|
|
48
48
|
});
|
|
49
49
|
}
|
|
@@ -9,14 +9,12 @@
|
|
|
9
9
|
{{FORM_FIELDS}}
|
|
10
10
|
</el-row>
|
|
11
11
|
<el-row :gutter="24">
|
|
12
|
-
|
|
13
|
-
{{CHILD_TABLE_COLUMNS}}
|
|
14
|
-
</sc-form-table>
|
|
12
|
+
{{CHILD_SECTIONS}}
|
|
15
13
|
</el-row>
|
|
16
14
|
</el-form>
|
|
17
15
|
<div class="dialog-footer" style="text-align: right; margin-top: 18px;">
|
|
18
|
-
<el-button @click="handleBack"
|
|
19
|
-
<el-button type="primary" @click="onSubmit" :disabled="loading"
|
|
16
|
+
<el-button @click="handleBack">取消</el-button>
|
|
17
|
+
<el-button type="primary" @click="onSubmit" :disabled="loading">确认</el-button>
|
|
20
18
|
</div>
|
|
21
19
|
</el-card>
|
|
22
20
|
</div>
|
|
@@ -38,12 +36,10 @@ const detail = ref(false);
|
|
|
38
36
|
|
|
39
37
|
const form = reactive({
|
|
40
38
|
{{FORM_DEFAULTS}}
|
|
41
|
-
|
|
39
|
+
{{CHILD_FORM_LIST_DEFAULTS}}
|
|
42
40
|
});
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
{{CHILD_TEMP_DEFAULTS}}
|
|
46
|
-
});
|
|
42
|
+
{{CHILD_TEMP_DECLARATIONS}}
|
|
47
43
|
|
|
48
44
|
const dataRules = ref({
|
|
49
45
|
{{FORM_RULES}}
|
|
@@ -55,7 +51,7 @@ const get{{CLASS_NAME}}Data = async (id: string) => {
|
|
|
55
51
|
const { data } = await getObj({ {{PK_ATTR}}: id });
|
|
56
52
|
Object.assign(form, data[0] || {});
|
|
57
53
|
} catch (error) {
|
|
58
|
-
useMessage().error('
|
|
54
|
+
useMessage().error('获取数据失败');
|
|
59
55
|
} finally {
|
|
60
56
|
loading.value = false;
|
|
61
57
|
}
|
|
@@ -64,11 +60,11 @@ const get{{CLASS_NAME}}Data = async (id: string) => {
|
|
|
64
60
|
const resetFormState = () => {
|
|
65
61
|
Object.assign(form, {
|
|
66
62
|
{{FORM_DEFAULTS}}
|
|
67
|
-
|
|
63
|
+
{{CHILD_FORM_LIST_DEFAULTS}}
|
|
68
64
|
});
|
|
69
65
|
nextTick(() => {
|
|
70
66
|
dataFormRef.value?.resetFields();
|
|
71
|
-
|
|
67
|
+
{{CHILD_RESET_LISTS}}
|
|
72
68
|
});
|
|
73
69
|
};
|
|
74
70
|
|
|
@@ -104,22 +100,22 @@ const onSubmit = async () => {
|
|
|
104
100
|
|
|
105
101
|
try {
|
|
106
102
|
form.{{PK_ATTR}} ? await putObj(form) : await addObj(form);
|
|
107
|
-
useMessage().success(form.{{PK_ATTR}} ? '
|
|
103
|
+
useMessage().success(form.{{PK_ATTR}} ? '修改成功' : '添加成功');
|
|
108
104
|
closeCurrentPage();
|
|
109
105
|
} catch (err: any) {
|
|
110
|
-
useMessage().error(err.msg || '
|
|
106
|
+
useMessage().error(err.msg || '提交失败');
|
|
111
107
|
} finally {
|
|
112
108
|
loading.value = false;
|
|
113
109
|
}
|
|
114
110
|
};
|
|
115
111
|
|
|
116
|
-
const deleteChild = async (obj:
|
|
117
|
-
if (obj
|
|
112
|
+
const deleteChild = async (obj: Record<string, any>, childPkAttr: string, childTableName?: string) => {
|
|
113
|
+
if (obj[childPkAttr]) {
|
|
118
114
|
try {
|
|
119
|
-
await delChildObj([obj
|
|
120
|
-
useMessage().success('
|
|
115
|
+
await delChildObj([obj[childPkAttr]], childTableName);
|
|
116
|
+
useMessage().success('删除成功');
|
|
121
117
|
} catch (err: any) {
|
|
122
|
-
useMessage().error(err.msg || '
|
|
118
|
+
useMessage().error(err.msg || '删除失败');
|
|
123
119
|
}
|
|
124
120
|
}
|
|
125
121
|
};
|
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.4';
|
|
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');
|
|
@@ -19,6 +19,21 @@ const TOOL_SCHEMA = {
|
|
|
19
19
|
designFile: { type: 'string', description: 'Absolute or relative Markdown design file path. Defaults to ../sql/SQL 设计说明.md when omitted.' },
|
|
20
20
|
tableName: { type: 'string', description: 'Target main table name from the design file.' },
|
|
21
21
|
style: { type: 'string', enum: Object.keys(STYLE_CATALOG), description: 'Style id from assets/style-catalog.json.' },
|
|
22
|
+
children: {
|
|
23
|
+
type: 'array',
|
|
24
|
+
description: 'Optional direct child-table relations for master_child_jump. When provided, MCP renders all listed direct child tables on the same main form.',
|
|
25
|
+
items: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
childTableName: { type: 'string', description: 'Child table name.' },
|
|
29
|
+
mainField: { type: 'string', description: 'Main table relation field.' },
|
|
30
|
+
childField: { type: 'string', description: 'Child table relation field.' },
|
|
31
|
+
relationType: { type: 'string', description: 'Optional relation type label, for example 1:N.' },
|
|
32
|
+
},
|
|
33
|
+
required: ['childTableName', 'mainField', 'childField'],
|
|
34
|
+
additionalProperties: false,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
22
37
|
childTableName: { type: 'string', description: 'Child table name. Required when style=master_child_jump.' },
|
|
23
38
|
mainField: { type: 'string', description: 'Main table relation field. Required when style=master_child_jump.' },
|
|
24
39
|
childField: { type: 'string', description: 'Child table relation field. Required when style=master_child_jump.' },
|
|
@@ -474,6 +489,15 @@ function buildRetryArguments(safeArgs, sourceFile, childTableName) {
|
|
|
474
489
|
retryArguments.designFile = sourceFile;
|
|
475
490
|
}
|
|
476
491
|
|
|
492
|
+
if (safeArgs.children && safeArgs.children.length) {
|
|
493
|
+
retryArguments.children = safeArgs.children.map((relation) => ({
|
|
494
|
+
childTableName: relation.childTableName,
|
|
495
|
+
mainField: relation.mainField,
|
|
496
|
+
childField: relation.childField,
|
|
497
|
+
relationType: relation.relationType || '',
|
|
498
|
+
}));
|
|
499
|
+
}
|
|
500
|
+
|
|
477
501
|
if (childTableName) {
|
|
478
502
|
retryArguments.childTableName = childTableName;
|
|
479
503
|
}
|
|
@@ -536,13 +560,43 @@ function loadSourceDocument(safeArgs) {
|
|
|
536
560
|
};
|
|
537
561
|
}
|
|
538
562
|
|
|
539
|
-
function
|
|
563
|
+
function normalizeChildrenInput(inputChildren) {
|
|
564
|
+
if (inputChildren === undefined || inputChildren === null) {
|
|
565
|
+
return [];
|
|
566
|
+
}
|
|
567
|
+
if (!Array.isArray(inputChildren)) {
|
|
568
|
+
throw new Error('children must be an array');
|
|
569
|
+
}
|
|
570
|
+
return inputChildren.map((item, index) => {
|
|
571
|
+
if (!item || typeof item !== 'object') {
|
|
572
|
+
throw new Error('children[' + index + '] must be an object');
|
|
573
|
+
}
|
|
574
|
+
const childTableName = item.childTableName ? String(item.childTableName) : '';
|
|
575
|
+
const mainField = item.mainField ? String(item.mainField) : '';
|
|
576
|
+
const childField = item.childField ? String(item.childField) : '';
|
|
577
|
+
const relationType = item.relationType ? String(item.relationType) : '';
|
|
578
|
+
const missingFields = ['childTableName', 'mainField', 'childField'].filter((field) => !({ childTableName, mainField, childField })[field]);
|
|
579
|
+
if (missingFields.length) {
|
|
580
|
+
throw new Error('children[' + index + '] is missing required fields: ' + missingFields.join(', '));
|
|
581
|
+
}
|
|
582
|
+
return { childTableName, mainField, childField, relationType };
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function resolveMarkdownChildRelations(sourceDocument, safeArgs) {
|
|
587
|
+
if (safeArgs.children && safeArgs.children.length) {
|
|
588
|
+
return safeArgs.children.map((relation) => ({
|
|
589
|
+
mainTableName: safeArgs.tableName,
|
|
590
|
+
childTableName: relation.childTableName,
|
|
591
|
+
mainField: relation.mainField,
|
|
592
|
+
childField: relation.childField,
|
|
593
|
+
relationType: relation.relationType || '',
|
|
594
|
+
}));
|
|
595
|
+
}
|
|
596
|
+
|
|
540
597
|
const missingFields = ['childTableName', 'mainField', 'childField'].filter((field) => !safeArgs[field]);
|
|
541
598
|
if (missingFields.length) {
|
|
542
|
-
const message =
|
|
543
|
-
'master_child_jump requires externally provided relation fields. Missing: ' +
|
|
544
|
-
missingFields.join(', ') +
|
|
545
|
-
'.';
|
|
599
|
+
const message = 'master_child_jump requires either children[] or legacy relation fields. Missing: ' + missingFields.join(', ') + '.';
|
|
546
600
|
const error = new Error(message);
|
|
547
601
|
error.details = {
|
|
548
602
|
type: 'relation_input_required',
|
|
@@ -550,11 +604,19 @@ function resolveMarkdownChildRelation(sourceDocument, safeArgs) {
|
|
|
550
604
|
tableName: safeArgs.tableName,
|
|
551
605
|
designFile: sourceDocument.path,
|
|
552
606
|
message,
|
|
553
|
-
requiredFields: ['childTableName
|
|
607
|
+
requiredFields: ['children[] or childTableName/mainField/childField'],
|
|
554
608
|
correctionEntry: {
|
|
555
|
-
fields: ['childTableName', 'mainField', 'childField'],
|
|
609
|
+
fields: ['children', 'childTableName', 'mainField', 'childField'],
|
|
556
610
|
example: {
|
|
557
611
|
...buildRetryArguments(safeArgs, sourceDocument.path, safeArgs.childTableName || '<child_table_name>'),
|
|
612
|
+
children: [
|
|
613
|
+
{
|
|
614
|
+
childTableName: safeArgs.childTableName || '<child_table_name>',
|
|
615
|
+
mainField: safeArgs.mainField || '<main_field>',
|
|
616
|
+
childField: safeArgs.childField || '<child_field>',
|
|
617
|
+
relationType: safeArgs.relationType || '1:N',
|
|
618
|
+
},
|
|
619
|
+
],
|
|
558
620
|
mainField: safeArgs.mainField || '<main_field>',
|
|
559
621
|
childField: safeArgs.childField || '<child_field>',
|
|
560
622
|
},
|
|
@@ -563,13 +625,15 @@ function resolveMarkdownChildRelation(sourceDocument, safeArgs) {
|
|
|
563
625
|
throw error;
|
|
564
626
|
}
|
|
565
627
|
|
|
566
|
-
return
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
628
|
+
return [
|
|
629
|
+
{
|
|
630
|
+
mainTableName: safeArgs.tableName,
|
|
631
|
+
childTableName: safeArgs.childTableName,
|
|
632
|
+
mainField: safeArgs.mainField,
|
|
633
|
+
childField: safeArgs.childField,
|
|
634
|
+
relationType: safeArgs.relationType || '',
|
|
635
|
+
},
|
|
636
|
+
];
|
|
573
637
|
}
|
|
574
638
|
|
|
575
639
|
function getStylePreset(styleId) {
|
|
@@ -601,31 +665,33 @@ function ensureFieldExists(fields, fieldName, tableName, role) {
|
|
|
601
665
|
return field;
|
|
602
666
|
}
|
|
603
667
|
|
|
604
|
-
function
|
|
605
|
-
if (safeArgs.style !== 'master_child_jump') return
|
|
606
|
-
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
668
|
+
function buildChildModels(sourceDocument, safeArgs, mainParsed) {
|
|
669
|
+
if (safeArgs.style !== 'master_child_jump') return [];
|
|
670
|
+
|
|
671
|
+
const relations = resolveMarkdownChildRelations(sourceDocument, safeArgs);
|
|
672
|
+
return relations.map((relation) => {
|
|
673
|
+
const childParsed = parseTableFromMarkdownDesign(sourceDocument.designDoc, relation.childTableName);
|
|
674
|
+
const childFields = normalizeFields(childParsed);
|
|
675
|
+
const mainRelationField = ensureFieldExists(mainParsed.fields, relation.mainField, safeArgs.tableName, 'Main relation');
|
|
676
|
+
const childRelationField = ensureFieldExists(childParsed.fields, relation.childField, relation.childTableName, 'Child relation');
|
|
677
|
+
const childVisibleFields = childFields.filter(
|
|
678
|
+
(field) => field.fieldName !== childParsed.pkField.fieldName && !field.isAudit && field.fieldName !== relation.childField
|
|
679
|
+
);
|
|
680
|
+
|
|
681
|
+
return {
|
|
682
|
+
tableName: relation.childTableName,
|
|
683
|
+
tableComment: childParsed.tableComment,
|
|
684
|
+
className: toPascalCase(relation.childTableName),
|
|
685
|
+
functionName: toCamelCase(relation.childTableName),
|
|
686
|
+
listName: toCamelCase(relation.childTableName) + 'List',
|
|
687
|
+
pk: childParsed.pkField,
|
|
688
|
+
fields: childFields,
|
|
689
|
+
visibleFields: childVisibleFields,
|
|
690
|
+
mainField: mainRelationField,
|
|
691
|
+
childField: childRelationField,
|
|
692
|
+
relationType: relation.relationType,
|
|
693
|
+
};
|
|
694
|
+
});
|
|
629
695
|
}
|
|
630
696
|
|
|
631
697
|
function buildModel(safeArgs) {
|
|
@@ -634,8 +700,9 @@ function buildModel(safeArgs) {
|
|
|
634
700
|
const fields = normalizeFields(mainParsed);
|
|
635
701
|
const visibleFields = fields.filter((field) => field.fieldName !== mainParsed.pkField.fieldName && !field.isAudit);
|
|
636
702
|
const gridFields = visibleFields.slice(0, 8);
|
|
637
|
-
const
|
|
638
|
-
const
|
|
703
|
+
const children = buildChildModels(sourceDocument, safeArgs, mainParsed);
|
|
704
|
+
const childDictTypes = children.flatMap((child) => child.visibleFields.map((field) => field.dictType).filter(Boolean));
|
|
705
|
+
const dictTypes = [...new Set([...visibleFields.map((field) => field.dictType).filter(Boolean), ...childDictTypes])];
|
|
639
706
|
|
|
640
707
|
return {
|
|
641
708
|
sourceFile: sourceDocument.path,
|
|
@@ -652,7 +719,7 @@ function buildModel(safeArgs) {
|
|
|
652
719
|
dictTypes,
|
|
653
720
|
frontendPath: path.resolve(safeArgs.frontendPath),
|
|
654
721
|
style: safeArgs.style,
|
|
655
|
-
|
|
722
|
+
children,
|
|
656
723
|
};
|
|
657
724
|
}
|
|
658
725
|
|
|
@@ -739,11 +806,26 @@ function renderTableColumn(field) {
|
|
|
739
806
|
|
|
740
807
|
function renderChildTableColumn(field, childListName) {
|
|
741
808
|
const label = field.comment.replace(/'/g, "\\'");
|
|
809
|
+
const rules = field.notNull ? ` :rules="[{ required: true, trigger: 'blur' }]"` : '';
|
|
810
|
+
|
|
811
|
+
let control = ` <el-input v-model="row.${field.attrName}" />`;
|
|
812
|
+
if (field.formType === 'select' && field.dictType) {
|
|
813
|
+
control = [
|
|
814
|
+
` <el-select v-model="row.${field.attrName}" placeholder="请选择${label}" style="width: 100%">`,
|
|
815
|
+
` <el-option v-for="item in ${field.dictType}" :key="item.value" :label="item.label" :value="Number(item.value)" />`,
|
|
816
|
+
' </el-select>',
|
|
817
|
+
].join('\n');
|
|
818
|
+
} else if (field.formType === 'number') {
|
|
819
|
+
const max = field.comment.includes('%') || field.comment.includes('比例') ? ' :max="100"' : '';
|
|
820
|
+
const precision = field.sqlType === 'DECIMAL' && field.scale ? ` :precision="${field.scale}" :step="0.01"` : '';
|
|
821
|
+
control = ` <el-input-number v-model="row.${field.attrName}" :min="0"${max}${precision} style="width: 100%" />`;
|
|
822
|
+
}
|
|
823
|
+
|
|
742
824
|
return [
|
|
743
825
|
` <el-table-column label="${label}" prop="${field.attrName}">`,
|
|
744
826
|
' <template #default="{ row, $index }">',
|
|
745
|
-
` <el-form-item :prop="\`${childListName}.\${$index}.${field.attrName}\`"
|
|
746
|
-
|
|
827
|
+
` <el-form-item :prop="\`${childListName}.\${$index}.${field.attrName}\`"${rules}>`,
|
|
828
|
+
control,
|
|
747
829
|
' </el-form-item>',
|
|
748
830
|
' </template>',
|
|
749
831
|
' </el-table-column>',
|
|
@@ -776,6 +858,50 @@ function renderChildTempDefaults(childModel) {
|
|
|
776
858
|
return childModel.fields.map(renderDefaultLine).join('\n');
|
|
777
859
|
}
|
|
778
860
|
|
|
861
|
+
function renderChildListDefaultLine(childModel) {
|
|
862
|
+
return ` ${childModel.listName}: [],`;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function renderChildTempDeclaration(childModel) {
|
|
866
|
+
return [
|
|
867
|
+
`const childTemp${childModel.className} = reactive({`,
|
|
868
|
+
renderChildTempDefaults(childModel),
|
|
869
|
+
'});',
|
|
870
|
+
].join('\n');
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function renderChildSection(childModel, childCount) {
|
|
874
|
+
const title = childModel.tableComment.replace(/'/g, "\\'");
|
|
875
|
+
const deleteExpression =
|
|
876
|
+
childCount > 1
|
|
877
|
+
? `deleteChild(obj, '${childModel.pk.attrName}', '${childModel.tableName}')`
|
|
878
|
+
: `deleteChild(obj, '${childModel.pk.attrName}')`;
|
|
879
|
+
|
|
880
|
+
return [
|
|
881
|
+
' <el-col :span="24" class="mb20">',
|
|
882
|
+
` <div class="mb10" style="font-weight: 600;">${title}</div>`,
|
|
883
|
+
` <sc-form-table v-model="form.${childModel.listName}" :addTemplate="childTemp${childModel.className}" @delete="(obj) => ${deleteExpression}" placeholder="暂无数据">`,
|
|
884
|
+
childModel.visibleFields.map((field) => renderChildTableColumn(field, childModel.listName)).join('\n'),
|
|
885
|
+
' </sc-form-table>',
|
|
886
|
+
' </el-col>',
|
|
887
|
+
].join('\n');
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
function renderChildFormListDefaults(children) {
|
|
891
|
+
if (!children.length) return '';
|
|
892
|
+
return children.map(renderChildListDefaultLine).join('\n');
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function renderChildTempDeclarations(children) {
|
|
896
|
+
if (!children.length) return '';
|
|
897
|
+
return children.map(renderChildTempDeclaration).join('\n\n');
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
function renderChildResetListLines(children) {
|
|
901
|
+
if (!children.length) return '';
|
|
902
|
+
return children.map((childModel) => ` form.${childModel.listName} = [];`).join('\n');
|
|
903
|
+
}
|
|
904
|
+
|
|
779
905
|
function renderDictImportBlock(dictTypes) {
|
|
780
906
|
if (!dictTypes.length) return '';
|
|
781
907
|
return [
|
|
@@ -830,12 +956,10 @@ function buildReplacements(model) {
|
|
|
830
956
|
OPTIONS_FIELDS: model.visibleFields.map(renderOptionField).join('\n'),
|
|
831
957
|
FILTER_TYPES: model.visibleFields.map(renderFilterType).filter(Boolean).join('\n'),
|
|
832
958
|
FORM_RULES: renderFormRules(model.visibleFields),
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
CHILD_TABLE_COLUMNS: model.child ? model.child.visibleFields.map((field) => renderChildTableColumn(field, model.child.listName)).join('\n') : '',
|
|
838
|
-
CHILD_TEMP_DEFAULTS: model.child ? renderChildTempDefaults(model.child) : '',
|
|
959
|
+
CHILD_FORM_LIST_DEFAULTS: renderChildFormListDefaults(model.children),
|
|
960
|
+
CHILD_TEMP_DECLARATIONS: renderChildTempDeclarations(model.children),
|
|
961
|
+
CHILD_RESET_LISTS: renderChildResetListLines(model.children),
|
|
962
|
+
CHILD_SECTIONS: model.children.map((childModel) => renderChildSection(childModel, model.children.length)).join('\n'),
|
|
839
963
|
};
|
|
840
964
|
}
|
|
841
965
|
|
|
@@ -881,6 +1005,7 @@ function ensureArguments(input) {
|
|
|
881
1005
|
designFile: input.designFile ? String(input.designFile) : null,
|
|
882
1006
|
tableName: String(input.tableName),
|
|
883
1007
|
style,
|
|
1008
|
+
children: normalizeChildrenInput(input.children),
|
|
884
1009
|
childTableName: input.childTableName ? String(input.childTableName) : null,
|
|
885
1010
|
mainField: input.mainField ? String(input.mainField) : null,
|
|
886
1011
|
childField: input.childField ? String(input.childField) : null,
|
|
@@ -906,6 +1031,16 @@ function maybeWriteFiles(files, writeToDisk, overwrite) {
|
|
|
906
1031
|
}
|
|
907
1032
|
|
|
908
1033
|
function buildManifest(model, safeArgs, stylePreset, sharedTemplates, files, note) {
|
|
1034
|
+
const relations = model.children.map((childModel) => ({
|
|
1035
|
+
childTableName: childModel.tableName,
|
|
1036
|
+
childTableComment: childModel.tableComment,
|
|
1037
|
+
mainField: childModel.mainField.fieldName,
|
|
1038
|
+
childField: childModel.childField.fieldName,
|
|
1039
|
+
childListName: childModel.listName,
|
|
1040
|
+
relationType: childModel.relationType || '',
|
|
1041
|
+
}));
|
|
1042
|
+
const selectedList = relations.map((relation) => formatRelationCandidate(relation));
|
|
1043
|
+
|
|
909
1044
|
return {
|
|
910
1045
|
mode: 'local-template',
|
|
911
1046
|
style: safeArgs.style,
|
|
@@ -919,32 +1054,23 @@ function buildManifest(model, safeArgs, stylePreset, sharedTemplates, files, not
|
|
|
919
1054
|
writeToDisk: safeArgs.writeToDisk,
|
|
920
1055
|
selectedTemplates: sharedTemplates,
|
|
921
1056
|
files: files.map((file) => ({ type: file.type, path: file.path, bytes: Buffer.byteLength(file.content, 'utf8'), status: file.status || (safeArgs.writeToDisk ? 'success' : 'rendered') })),
|
|
922
|
-
relation:
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
childTableComment: model.child.tableComment,
|
|
926
|
-
mainField: model.child.mainField.fieldName,
|
|
927
|
-
childField: model.child.childField.fieldName,
|
|
928
|
-
childListName: model.child.listName,
|
|
929
|
-
relationType: model.child.relationType || '',
|
|
930
|
-
}
|
|
931
|
-
: null,
|
|
932
|
-
relationResolution: model.child
|
|
1057
|
+
relation: relations.length === 1 ? relations[0] : null,
|
|
1058
|
+
relations,
|
|
1059
|
+
relationResolution: model.children.length
|
|
933
1060
|
? {
|
|
934
1061
|
status: 'provided',
|
|
935
1062
|
tableName: safeArgs.tableName,
|
|
936
1063
|
designFile: model.designFile,
|
|
937
1064
|
source: 'arguments',
|
|
938
|
-
message:
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
}),
|
|
1065
|
+
message:
|
|
1066
|
+
model.children.length > 1
|
|
1067
|
+
? 'Direct child relations were provided by the caller. MCP skipped design-doc relation inference and generated a single main form with multiple child tables.'
|
|
1068
|
+
: 'The relation was provided by the caller. MCP skipped design-doc relation inference and generated files directly.',
|
|
1069
|
+
selected: selectedList.length === 1 ? selectedList[0] : null,
|
|
1070
|
+
selectedList,
|
|
945
1071
|
correctionEntry: {
|
|
946
|
-
fields: ['childTableName', 'mainField', 'childField'],
|
|
947
|
-
example: buildRetryArguments(safeArgs, model.designFile, model.
|
|
1072
|
+
fields: ['children', 'childTableName', 'mainField', 'childField'],
|
|
1073
|
+
example: buildRetryArguments(safeArgs, model.designFile, model.children[0].tableName),
|
|
948
1074
|
},
|
|
949
1075
|
}
|
|
950
1076
|
: null,
|
|
@@ -953,7 +1079,9 @@ function buildManifest(model, safeArgs, stylePreset, sharedTemplates, files, not
|
|
|
953
1079
|
visibleFields: model.visibleFields.length,
|
|
954
1080
|
dictFields: model.visibleFields.filter((field) => field.dictType).map((field) => field.attrName),
|
|
955
1081
|
skippedAuditFields: model.fields.filter((field) => field.isAudit).map((field) => field.fieldName),
|
|
956
|
-
|
|
1082
|
+
childCount: model.children.length,
|
|
1083
|
+
childTables: model.children.map((childModel) => childModel.tableName),
|
|
1084
|
+
childVisibleFields: model.children.reduce((sum, childModel) => sum + childModel.visibleFields.length, 0),
|
|
957
1085
|
},
|
|
958
1086
|
note,
|
|
959
1087
|
};
|
|
@@ -996,7 +1124,7 @@ async function onMessage(message) {
|
|
|
996
1124
|
}
|
|
997
1125
|
|
|
998
1126
|
if (method === 'tools/list') {
|
|
999
|
-
writeMessage(successResponse(id, { tools: [{ name: TOOL_NAME, description: 'Generate Worsoft frontend files and menu SQL from a local Markdown design file with style-based local templates. In master_child_jump mode, the caller must provide
|
|
1127
|
+
writeMessage(successResponse(id, { tools: [{ name: TOOL_NAME, description: 'Generate Worsoft frontend files and menu SQL from a local Markdown design file with style-based local templates. In master_child_jump mode, the caller must provide either children[] or childTableName/mainField/childField.', inputSchema: TOOL_SCHEMA }] }));
|
|
1000
1128
|
return;
|
|
1001
1129
|
}
|
|
1002
1130
|
|
package/package.json
CHANGED