worsoft-frontend-codegen-local-mcp 0.1.45 → 0.1.47
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/mcp_server.js +108 -86
- package/package.json +1 -1
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.47';
|
|
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');
|
|
@@ -15,11 +15,12 @@ const DEFAULT_DICT_REGISTRY_KEYS = {
|
|
|
15
15
|
trade_standard_type: 'TRADE_STANDARD_TYPE',
|
|
16
16
|
trade_score_standard: 'TRADE_SCORE_STANDARD',
|
|
17
17
|
};
|
|
18
|
-
const DEFAULT_CRUD_SCHEMA_TEMPLATE = `export interface FieldMeta {
|
|
19
|
-
show: boolean;
|
|
20
|
-
listShow: boolean;
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
const DEFAULT_CRUD_SCHEMA_TEMPLATE = `export interface FieldMeta {
|
|
19
|
+
show: boolean;
|
|
20
|
+
listShow: boolean;
|
|
21
|
+
formShow: boolean;
|
|
22
|
+
alwaysHide: boolean;
|
|
23
|
+
smart: boolean;
|
|
23
24
|
queryType?: number;
|
|
24
25
|
labelKey: string;
|
|
25
26
|
label?: string;
|
|
@@ -27,15 +28,16 @@ const DEFAULT_CRUD_SCHEMA_TEMPLATE = `export interface FieldMeta {
|
|
|
27
28
|
dictType?: string;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
export interface FieldConfig {
|
|
31
|
-
key: string;
|
|
32
|
-
labelKey?: string;
|
|
33
|
-
label?: string;
|
|
34
|
-
width?: string;
|
|
35
|
-
show?: boolean;
|
|
36
|
-
listShow?: boolean;
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
export interface FieldConfig {
|
|
32
|
+
key: string;
|
|
33
|
+
labelKey?: string;
|
|
34
|
+
label?: string;
|
|
35
|
+
width?: string;
|
|
36
|
+
show?: boolean;
|
|
37
|
+
listShow?: boolean;
|
|
38
|
+
formShow?: boolean;
|
|
39
|
+
alwaysHide?: boolean;
|
|
40
|
+
smart?: boolean;
|
|
39
41
|
queryType?: number;
|
|
40
42
|
dictType?: string;
|
|
41
43
|
}
|
|
@@ -57,11 +59,12 @@ export interface CrudSchema {
|
|
|
57
59
|
const DEFAULT_WIDTH = '120';
|
|
58
60
|
const DEFAULT_DICT_QUERY_TYPE = 30;
|
|
59
61
|
|
|
60
|
-
export const field = (labelKey: string, width = DEFAULT_WIDTH): FieldMeta => ({
|
|
61
|
-
show: true,
|
|
62
|
-
listShow: true,
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
export const field = (labelKey: string, width = DEFAULT_WIDTH): FieldMeta => ({
|
|
63
|
+
show: true,
|
|
64
|
+
listShow: true,
|
|
65
|
+
formShow: true,
|
|
66
|
+
alwaysHide: false,
|
|
67
|
+
smart: false,
|
|
65
68
|
labelKey,
|
|
66
69
|
width,
|
|
67
70
|
});
|
|
@@ -94,10 +97,11 @@ export const collectDictTypes = (...groups: Array<Record<string, FieldMeta>>) =>
|
|
|
94
97
|
)
|
|
95
98
|
);
|
|
96
99
|
|
|
97
|
-
const normalizeField = (item: FieldConfig): FieldMeta => ({
|
|
98
|
-
show: item.show ?? true,
|
|
99
|
-
listShow: item.listShow ?? item.show ?? true,
|
|
100
|
-
|
|
100
|
+
const normalizeField = (item: FieldConfig): FieldMeta => ({
|
|
101
|
+
show: item.show ?? true,
|
|
102
|
+
listShow: item.listShow ?? item.show ?? true,
|
|
103
|
+
formShow: item.formShow ?? item.show ?? true,
|
|
104
|
+
alwaysHide: item.alwaysHide ?? false,
|
|
101
105
|
smart: item.smart ?? false,
|
|
102
106
|
...(typeof item.queryType === 'number'
|
|
103
107
|
? { queryType: item.queryType }
|
|
@@ -171,9 +175,10 @@ const TOOL_SCHEMA = {
|
|
|
171
175
|
scale: { type: ['string', 'number'], description: 'Optional decimal scale.' },
|
|
172
176
|
required: { type: ['boolean', 'string'], description: 'Whether the field is required.' },
|
|
173
177
|
readonly: { type: ['boolean', 'string'], description: 'Whether the field is readonly on the page.' },
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
178
|
+
show: { type: ['boolean', 'string'], description: 'Whether the field is shown on the page.' },
|
|
179
|
+
listShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the list page.' },
|
|
180
|
+
formShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the form page.' },
|
|
181
|
+
smart: { type: ['boolean', 'string'], description: 'Whether the field participates in smart keyword search. This must come from PRD, not inferred from type.' },
|
|
177
182
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
178
183
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
179
184
|
defaultValue: { type: ['string', 'number', 'boolean'], description: 'Optional default value.' },
|
|
@@ -212,9 +217,10 @@ const TOOL_SCHEMA = {
|
|
|
212
217
|
scale: { type: ['string', 'number'], description: 'Optional decimal scale.' },
|
|
213
218
|
required: { type: ['boolean', 'string'], description: 'Whether the field is required.' },
|
|
214
219
|
readonly: { type: ['boolean', 'string'], description: 'Whether the field is readonly on the page.' },
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
220
|
+
show: { type: ['boolean', 'string'], description: 'Whether the field is shown on the page.' },
|
|
221
|
+
listShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the list page.' },
|
|
222
|
+
formShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the form page.' },
|
|
223
|
+
smart: { type: ['boolean', 'string'], description: 'Whether the field participates in smart keyword search. This must come from PRD, not inferred from type.' },
|
|
218
224
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
219
225
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
220
226
|
defaultValue: { type: ['string', 'number', 'boolean'], description: 'Optional default value.' },
|
|
@@ -269,9 +275,10 @@ const TOOL_SCHEMA = {
|
|
|
269
275
|
scale: { type: ['string', 'number'], description: 'Optional decimal scale.' },
|
|
270
276
|
required: { type: ['boolean', 'string'], description: 'Whether the field is required.' },
|
|
271
277
|
readonly: { type: ['boolean', 'string'], description: 'Whether the field is readonly on the page.' },
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
278
|
+
show: { type: ['boolean', 'string'], description: 'Whether the field is shown on the page.' },
|
|
279
|
+
listShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the list page.' },
|
|
280
|
+
formShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the form page.' },
|
|
281
|
+
smart: { type: ['boolean', 'string'], description: 'Whether the field participates in smart keyword search. This must come from PRD, not inferred from type.' },
|
|
275
282
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
276
283
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
277
284
|
defaultValue: { type: ['string', 'number', 'boolean'], description: 'Optional default value.' },
|
|
@@ -871,22 +878,21 @@ function mapFieldType(field) {
|
|
|
871
878
|
return 'text';
|
|
872
879
|
}
|
|
873
880
|
|
|
874
|
-
function normalizeStructuredFormType(value) {
|
|
875
|
-
const normalized = String(value || '').trim().toLowerCase();
|
|
876
|
-
if (!normalized) return '';
|
|
877
|
-
if (normalized === 'date') return 'date';
|
|
878
|
-
if (normalized === 'datetime') return 'datetime';
|
|
879
|
-
if (normalized === 'microme-operator') return 'number';
|
|
880
|
-
if (normalized === 'upload'
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
}
|
|
881
|
+
function normalizeStructuredFormType(value) {
|
|
882
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
883
|
+
if (!normalized) return '';
|
|
884
|
+
if (normalized === 'date') return 'date';
|
|
885
|
+
if (normalized === 'datetime') return 'datetime';
|
|
886
|
+
if (normalized === 'microme-operator') return 'number';
|
|
887
|
+
if (normalized === 'upload') {
|
|
888
|
+
return 'upload';
|
|
889
|
+
}
|
|
890
|
+
if (normalized === 'picker') {
|
|
891
|
+
return 'text';
|
|
892
|
+
}
|
|
893
|
+
if (['text', 'select', 'textarea', 'number'].includes(normalized)) {
|
|
894
|
+
return normalized;
|
|
895
|
+
}
|
|
890
896
|
throw new Error('Unsupported explicit component/form type: ' + normalized);
|
|
891
897
|
}
|
|
892
898
|
|
|
@@ -1066,13 +1072,15 @@ function normalizeStructuredField(inputField, index, contextLabel) {
|
|
|
1066
1072
|
|
|
1067
1073
|
const { length, scale } = normalizeStructuredLengthAndScale(inputField.length, inputField.scale);
|
|
1068
1074
|
const explicitFormType = normalizeStructuredFormType(inputField.formType || inputField.componentType);
|
|
1069
|
-
const explicitQueryType =
|
|
1070
|
-
inputField.queryType === undefined || inputField.queryType === null || inputField.queryType === ''
|
|
1071
|
-
? undefined
|
|
1072
|
-
: Number.parseInt(String(inputField.queryType), 10);
|
|
1073
|
-
const
|
|
1074
|
-
|
|
1075
|
-
|
|
1075
|
+
const explicitQueryType =
|
|
1076
|
+
inputField.queryType === undefined || inputField.queryType === null || inputField.queryType === ''
|
|
1077
|
+
? undefined
|
|
1078
|
+
: Number.parseInt(String(inputField.queryType), 10);
|
|
1079
|
+
const show = parseBooleanLike(inputField.show, true);
|
|
1080
|
+
const listShow = parseBooleanLike(inputField.listShow, show);
|
|
1081
|
+
const formShow = parseBooleanLike(inputField.formShow, show);
|
|
1082
|
+
|
|
1083
|
+
return {
|
|
1076
1084
|
fieldName,
|
|
1077
1085
|
attrName: toCamelCase(fieldName),
|
|
1078
1086
|
sqlType: type,
|
|
@@ -1086,9 +1094,10 @@ function normalizeStructuredField(inputField, index, contextLabel) {
|
|
|
1086
1094
|
notNull: parseBooleanLike(inputField.required, false),
|
|
1087
1095
|
defaultValue: normalizeDefaultValue(inputField.defaultValue),
|
|
1088
1096
|
readonly: parseBooleanLike(inputField.readonly, false),
|
|
1089
|
-
show
|
|
1090
|
-
listShow,
|
|
1091
|
-
|
|
1097
|
+
show,
|
|
1098
|
+
listShow,
|
|
1099
|
+
formShow,
|
|
1100
|
+
smart: parseBooleanLike(inputField.smart, false),
|
|
1092
1101
|
queryType: Number.isNaN(explicitQueryType)
|
|
1093
1102
|
? undefined
|
|
1094
1103
|
: explicitQueryType !== undefined
|
|
@@ -1337,9 +1346,9 @@ function buildMultiLevelModule(moduleInput, levelIndex) {
|
|
|
1337
1346
|
const normalizedPk = moduleInput.primaryKey
|
|
1338
1347
|
? ensureFieldExists(fields, moduleInput.primaryKey, moduleInput.tableName, 'Primary key')
|
|
1339
1348
|
: findPrimaryKeyFromStructuredFields(fields);
|
|
1340
|
-
const optionFields = fields.filter((field) => field.fieldName !== normalizedPk.fieldName && !field.isAudit);
|
|
1341
|
-
const visibleFields = optionFields.filter((field) => field.
|
|
1342
|
-
const listFields = optionFields.filter((field) => field.listShow !== false);
|
|
1349
|
+
const optionFields = fields.filter((field) => field.fieldName !== normalizedPk.fieldName && !field.isAudit);
|
|
1350
|
+
const visibleFields = optionFields.filter((field) => field.formShow !== false);
|
|
1351
|
+
const listFields = optionFields.filter((field) => field.listShow !== false);
|
|
1343
1352
|
const statusField = detectStatusField(fields, moduleInput.statusField);
|
|
1344
1353
|
const apiPath = moduleInput.apiPath || toCamelCase(moduleInput.tableName);
|
|
1345
1354
|
const functionName = toCamelCase(moduleInput.tableName);
|
|
@@ -1460,7 +1469,7 @@ function buildChildModels(safeArgs, mainFields, mainPk) {
|
|
|
1460
1469
|
const childOptionFields = childFields.filter(
|
|
1461
1470
|
(field) => field.fieldName !== childPk.fieldName && !field.isAudit && field.fieldName !== relation.childField
|
|
1462
1471
|
);
|
|
1463
|
-
const childVisibleFields = childOptionFields.filter((field) => field.
|
|
1472
|
+
const childVisibleFields = childOptionFields.filter((field) => field.formShow !== false);
|
|
1464
1473
|
const payloadField = relation.payloadField;
|
|
1465
1474
|
const listName = payloadField;
|
|
1466
1475
|
|
|
@@ -1493,7 +1502,7 @@ function buildModel(safeArgs) {
|
|
|
1493
1502
|
fields: safeArgs.fields,
|
|
1494
1503
|
});
|
|
1495
1504
|
const optionFields = fields.filter((field) => field.fieldName !== pkField.fieldName && !field.isAudit);
|
|
1496
|
-
const visibleFields = optionFields.filter((field) => field.
|
|
1505
|
+
const visibleFields = optionFields.filter((field) => field.formShow !== false);
|
|
1497
1506
|
const listFields = optionFields.filter((field) => field.listShow !== false);
|
|
1498
1507
|
const gridFields = listFields.slice(0, 8);
|
|
1499
1508
|
const statusField = detectStatusField(fields, safeArgs.statusField);
|
|
@@ -1621,18 +1630,20 @@ function renderTableColumn(field) {
|
|
|
1621
1630
|
return ` <el-table-column prop="${field.attrName}" label="${label}" show-overflow-tooltip />`;
|
|
1622
1631
|
}
|
|
1623
1632
|
|
|
1624
|
-
function renderChildTableColumn(field, childListName) {
|
|
1625
|
-
const label = stripDictAnnotation(field.comment).replace(/'/g, "\\'");
|
|
1626
|
-
const rules = field.notNull ? ` :rules="[{ required: true, trigger: 'blur' }]"` : '';
|
|
1627
|
-
const inputPlaceholderExpr = `inputPlaceholder(getChildFieldLabel('${childListName}', '${field.attrName}'))`;
|
|
1628
|
-
const selectPlaceholderExpr = `selectPlaceholder(getChildFieldLabel('${childListName}', '${field.attrName}'))`;
|
|
1629
|
-
|
|
1630
|
-
let control = ` <el-input v-model="row.${field.attrName}" :placeholder="${inputPlaceholderExpr}" />`;
|
|
1631
|
-
if (field.formType === '
|
|
1632
|
-
control =
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1633
|
+
function renderChildTableColumn(field, childListName) {
|
|
1634
|
+
const label = stripDictAnnotation(field.comment).replace(/'/g, "\\'");
|
|
1635
|
+
const rules = field.notNull ? ` :rules="[{ required: true, trigger: 'blur' }]"` : '';
|
|
1636
|
+
const inputPlaceholderExpr = `inputPlaceholder(getChildFieldLabel('${childListName}', '${field.attrName}'))`;
|
|
1637
|
+
const selectPlaceholderExpr = `selectPlaceholder(getChildFieldLabel('${childListName}', '${field.attrName}'))`;
|
|
1638
|
+
|
|
1639
|
+
let control = ` <el-input v-model="row.${field.attrName}" :placeholder="${inputPlaceholderExpr}" />`;
|
|
1640
|
+
if (field.formType === 'upload') {
|
|
1641
|
+
control = ` <UploadFile v-model="row.${field.attrName}" />`;
|
|
1642
|
+
} else if (field.formType === 'select' && field.dictType) {
|
|
1643
|
+
control = [
|
|
1644
|
+
` <el-select v-model="row.${field.attrName}" :placeholder="${selectPlaceholderExpr}" style="width: 100%">`,
|
|
1645
|
+
` <el-option v-for="item in ${field.dictType}" :key="item.value" :label="item.label" :value="item.value" />`,
|
|
1646
|
+
' </el-select>',
|
|
1636
1647
|
].join('\n');
|
|
1637
1648
|
} else if (field.formType === 'number') {
|
|
1638
1649
|
const max = field.comment.includes('%') || /\u6BD4\u4F8B/.test(field.comment) ? ' :max="100"' : '';
|
|
@@ -1860,16 +1871,27 @@ function renderFieldCommentV2(field, indent = ' ') {
|
|
|
1860
1871
|
return indent + '<!-- 字段:' + label + ' -->';
|
|
1861
1872
|
}
|
|
1862
1873
|
|
|
1863
|
-
function renderFormFieldV2(field) {
|
|
1864
|
-
const prop = field.attrName;
|
|
1865
|
-
const labelExpr = `getMasterFieldLabel('${prop}')`;
|
|
1866
|
-
const dictExpr = `getMasterFieldMeta('${prop}')?.dictType`;
|
|
1867
|
-
const disabledAttr = renderDisabledAttrV2(field);
|
|
1868
|
-
|
|
1869
|
-
if (field.formType === '
|
|
1870
|
-
return [
|
|
1871
|
-
renderFieldCommentV2(field),
|
|
1872
|
-
` <el-col :span="
|
|
1874
|
+
function renderFormFieldV2(field) {
|
|
1875
|
+
const prop = field.attrName;
|
|
1876
|
+
const labelExpr = `getMasterFieldLabel('${prop}')`;
|
|
1877
|
+
const dictExpr = `getMasterFieldMeta('${prop}')?.dictType`;
|
|
1878
|
+
const disabledAttr = renderDisabledAttrV2(field);
|
|
1879
|
+
|
|
1880
|
+
if (field.formType === 'upload') {
|
|
1881
|
+
return [
|
|
1882
|
+
renderFieldCommentV2(field),
|
|
1883
|
+
` <el-col :span="24" class="mb20">`,
|
|
1884
|
+
` <el-form-item :label="${labelExpr}" prop="${prop}">`,
|
|
1885
|
+
` <UploadFile v-model="form.${prop}"${disabledAttr} />`,
|
|
1886
|
+
' </el-form-item>',
|
|
1887
|
+
' </el-col>',
|
|
1888
|
+
].join('\n');
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
if (field.formType === 'select') {
|
|
1892
|
+
return [
|
|
1893
|
+
renderFieldCommentV2(field),
|
|
1894
|
+
` <el-col :span="12" class="mb20">`,
|
|
1873
1895
|
` <el-form-item :label="${labelExpr}" prop="${prop}">`,
|
|
1874
1896
|
` <el-select v-model="form.${prop}" :placeholder="selectPlaceholder(${labelExpr})" style="width: 100%"${disabledAttr}>`,
|
|
1875
1897
|
` <el-option v-for="item in getDictOptions(${dictExpr})" :key="item.value" :label="item.label" :value="item.value" />`,
|
package/package.json
CHANGED