worsoft-frontend-codegen-local-mcp 0.1.59 → 0.1.62
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
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.62';
|
|
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');
|
|
@@ -178,6 +178,7 @@ const TOOL_SCHEMA = {
|
|
|
178
178
|
show: { type: ['boolean', 'string'], description: 'Whether the field is shown on the page.' },
|
|
179
179
|
listShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the list page.' },
|
|
180
180
|
formShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the form page.' },
|
|
181
|
+
formOrder: { type: ['number', 'string'], description: 'Generation-only form field order from PRD detail/form table. This is not emitted to options.ts.' },
|
|
181
182
|
smart: { type: ['boolean', 'string'], description: 'Whether the field participates in smart keyword search. This must come from PRD, not inferred from type.' },
|
|
182
183
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
183
184
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
@@ -220,6 +221,7 @@ const TOOL_SCHEMA = {
|
|
|
220
221
|
show: { type: ['boolean', 'string'], description: 'Whether the field is shown on the page.' },
|
|
221
222
|
listShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the list page.' },
|
|
222
223
|
formShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the form page.' },
|
|
224
|
+
formOrder: { type: ['number', 'string'], description: 'Generation-only form field order from PRD detail/form table. This is not emitted to options.ts.' },
|
|
223
225
|
smart: { type: ['boolean', 'string'], description: 'Whether the field participates in smart keyword search. This must come from PRD, not inferred from type.' },
|
|
224
226
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
225
227
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
@@ -278,6 +280,7 @@ const TOOL_SCHEMA = {
|
|
|
278
280
|
show: { type: ['boolean', 'string'], description: 'Whether the field is shown on the page.' },
|
|
279
281
|
listShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the list page.' },
|
|
280
282
|
formShow: { type: ['boolean', 'string'], description: 'Whether the field is shown on the form page.' },
|
|
283
|
+
formOrder: { type: ['number', 'string'], description: 'Generation-only form field order from PRD detail/form table. This is not emitted to options.ts.' },
|
|
281
284
|
smart: { type: ['boolean', 'string'], description: 'Whether the field participates in smart keyword search. This must come from PRD, not inferred from type.' },
|
|
282
285
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
283
286
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
@@ -1139,16 +1142,50 @@ function normalizeStructuredSqlType(value) {
|
|
|
1139
1142
|
if (['LOCALDATETIME', 'DATETIME', 'TIMESTAMP'].includes(upper) || ['\u65e5\u671f\u65f6\u95f4', '\u65f6\u95f4\u6233'].includes(normalized)) return 'DATETIME';
|
|
1140
1143
|
return upper;
|
|
1141
1144
|
}
|
|
1142
|
-
function normalizeStructuredLengthAndScale(lengthValue, scaleValue) {
|
|
1143
|
-
const parsed = splitLength(lengthValue);
|
|
1144
|
-
const normalizedScale = scaleValue === undefined || scaleValue === null || scaleValue === '' ? parsed.scale : String(scaleValue).trim();
|
|
1145
|
-
return {
|
|
1146
|
-
length: parsed.length,
|
|
1147
|
-
scale: normalizedScale || '',
|
|
1148
|
-
};
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
function
|
|
1145
|
+
function normalizeStructuredLengthAndScale(lengthValue, scaleValue) {
|
|
1146
|
+
const parsed = splitLength(lengthValue);
|
|
1147
|
+
const normalizedScale = scaleValue === undefined || scaleValue === null || scaleValue === '' ? parsed.scale : String(scaleValue).trim();
|
|
1148
|
+
return {
|
|
1149
|
+
length: parsed.length,
|
|
1150
|
+
scale: normalizedScale || '',
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
function parseOptionalOrder(value, label) {
|
|
1155
|
+
if (value === undefined || value === null || value === '') return undefined;
|
|
1156
|
+
const order = Number.parseInt(String(value), 10);
|
|
1157
|
+
if (Number.isNaN(order) || order < 0) {
|
|
1158
|
+
throw new Error(`${label} must be a non-negative integer when provided`);
|
|
1159
|
+
}
|
|
1160
|
+
return order;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
function sortFieldsForForm(fields) {
|
|
1164
|
+
const seenOrders = new Map();
|
|
1165
|
+
fields.forEach((field) => {
|
|
1166
|
+
if (field.formOrder === undefined) return;
|
|
1167
|
+
const existing = seenOrders.get(field.formOrder);
|
|
1168
|
+
if (existing) {
|
|
1169
|
+
throw new Error(`Duplicate formOrder ${field.formOrder} on fields ${existing} and ${field.fieldName}`);
|
|
1170
|
+
}
|
|
1171
|
+
seenOrders.set(field.formOrder, field.fieldName);
|
|
1172
|
+
});
|
|
1173
|
+
return fields
|
|
1174
|
+
.map((field, index) => ({ field, index }))
|
|
1175
|
+
.sort((left, right) => {
|
|
1176
|
+
const leftOrder = left.field.formOrder;
|
|
1177
|
+
const rightOrder = right.field.formOrder;
|
|
1178
|
+
if (leftOrder !== undefined && rightOrder !== undefined && leftOrder !== rightOrder) {
|
|
1179
|
+
return leftOrder - rightOrder;
|
|
1180
|
+
}
|
|
1181
|
+
if (leftOrder !== undefined && rightOrder === undefined) return -1;
|
|
1182
|
+
if (leftOrder === undefined && rightOrder !== undefined) return 1;
|
|
1183
|
+
return left.index - right.index;
|
|
1184
|
+
})
|
|
1185
|
+
.map((item) => item.field);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
function normalizeStructuredField(inputField, index, contextLabel) {
|
|
1152
1189
|
if (!inputField || typeof inputField !== 'object') {
|
|
1153
1190
|
throw new Error(contextLabel + '[' + index + '] must be an object');
|
|
1154
1191
|
}
|
|
@@ -1177,6 +1214,7 @@ function normalizeStructuredField(inputField, index, contextLabel) {
|
|
|
1177
1214
|
const show = parseBooleanLike(inputField.show, true);
|
|
1178
1215
|
const listShow = parseBooleanLike(inputField.listShow, show);
|
|
1179
1216
|
const formShow = parseBooleanLike(inputField.formShow, show);
|
|
1217
|
+
const formOrder = parseOptionalOrder(inputField.formOrder, contextLabel + '[' + index + '].formOrder');
|
|
1180
1218
|
|
|
1181
1219
|
return {
|
|
1182
1220
|
fieldName,
|
|
@@ -1195,6 +1233,7 @@ function normalizeStructuredField(inputField, index, contextLabel) {
|
|
|
1195
1233
|
show,
|
|
1196
1234
|
listShow,
|
|
1197
1235
|
formShow,
|
|
1236
|
+
formOrder,
|
|
1198
1237
|
smart: parseBooleanLike(inputField.smart, false),
|
|
1199
1238
|
queryType: Number.isNaN(explicitQueryType)
|
|
1200
1239
|
? undefined
|
|
@@ -1450,7 +1489,7 @@ function normalizeExtraApis(inputExtraApis, currentTargetApiModule) {
|
|
|
1450
1489
|
if (!['get', 'post', 'put', 'delete'].includes(method)) {
|
|
1451
1490
|
throw new Error('extraApis[' + index + '].method must be one of get, post, put, delete');
|
|
1452
1491
|
}
|
|
1453
|
-
const url = String(item.url || '').trim();
|
|
1492
|
+
const url = normalizeExtraApiUrl(String(item.url || '').trim(), currentTargetApiModule);
|
|
1454
1493
|
if (!url) {
|
|
1455
1494
|
throw new Error('extraApis[' + index + '].url is required');
|
|
1456
1495
|
}
|
|
@@ -1474,7 +1513,7 @@ function normalizeExtraApis(inputExtraApis, currentTargetApiModule) {
|
|
|
1474
1513
|
return {
|
|
1475
1514
|
functionName,
|
|
1476
1515
|
description,
|
|
1477
|
-
url
|
|
1516
|
+
url,
|
|
1478
1517
|
method,
|
|
1479
1518
|
requestType,
|
|
1480
1519
|
targetApiModule,
|
|
@@ -1484,6 +1523,17 @@ function normalizeExtraApis(inputExtraApis, currentTargetApiModule) {
|
|
|
1484
1523
|
});
|
|
1485
1524
|
}
|
|
1486
1525
|
|
|
1526
|
+
function normalizeExtraApiUrl(rawUrl, currentTargetApiModule) {
|
|
1527
|
+
if (!rawUrl) return '';
|
|
1528
|
+
if (/^https?:\/\//i.test(rawUrl)) return rawUrl;
|
|
1529
|
+
let url = rawUrl.startsWith('/') ? rawUrl : '/' + rawUrl;
|
|
1530
|
+
const moduleRoot = String(currentTargetApiModule || '').split('/')[0] || '';
|
|
1531
|
+
if (moduleRoot && !url.startsWith('/' + moduleRoot + '/')) {
|
|
1532
|
+
url = '/' + moduleRoot + url;
|
|
1533
|
+
}
|
|
1534
|
+
return url;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1487
1537
|
function ensureFieldExists(fields, fieldName, tableName, role) {
|
|
1488
1538
|
const field = fields.find((item) => item.fieldName === fieldName);
|
|
1489
1539
|
if (!field) throw new Error(role + ' field "' + fieldName + '" was not found on table ' + tableName);
|
|
@@ -1508,7 +1558,7 @@ function buildMultiLevelModule(moduleInput, levelIndex) {
|
|
|
1508
1558
|
? ensureFieldExists(fields, moduleInput.primaryKey, moduleInput.tableName, 'Primary key')
|
|
1509
1559
|
: findPrimaryKeyFromStructuredFields(fields);
|
|
1510
1560
|
const optionFields = fields.filter((field) => field.fieldName !== normalizedPk.fieldName && !field.isAudit);
|
|
1511
|
-
const visibleFields = optionFields.filter((field) => field.formShow !== false);
|
|
1561
|
+
const visibleFields = sortFieldsForForm(optionFields.filter((field) => field.formShow !== false));
|
|
1512
1562
|
const listFields = optionFields.filter((field) => field.listShow !== false);
|
|
1513
1563
|
const statusField = detectStatusField(fields, moduleInput.statusField);
|
|
1514
1564
|
const apiPath = moduleInput.apiPath || toCamelCase(moduleInput.tableName);
|
|
@@ -1631,7 +1681,7 @@ function buildChildModels(safeArgs, mainFields, mainPk) {
|
|
|
1631
1681
|
const childOptionFields = childFields.filter(
|
|
1632
1682
|
(field) => field.fieldName !== childPk.fieldName && !field.isAudit && field.fieldName !== relation.childField
|
|
1633
1683
|
);
|
|
1634
|
-
const childVisibleFields = childOptionFields.filter((field) => field.formShow !== false);
|
|
1684
|
+
const childVisibleFields = sortFieldsForForm(childOptionFields.filter((field) => field.formShow !== false));
|
|
1635
1685
|
const payloadField = relation.payloadField;
|
|
1636
1686
|
const listName = payloadField;
|
|
1637
1687
|
|
|
@@ -1664,7 +1714,7 @@ function buildModel(safeArgs) {
|
|
|
1664
1714
|
fields: safeArgs.fields,
|
|
1665
1715
|
});
|
|
1666
1716
|
const optionFields = fields.filter((field) => field.fieldName !== pkField.fieldName && !field.isAudit);
|
|
1667
|
-
const visibleFields = optionFields.filter((field) => field.formShow !== false);
|
|
1717
|
+
const visibleFields = sortFieldsForForm(optionFields.filter((field) => field.formShow !== false));
|
|
1668
1718
|
const listFields = optionFields.filter((field) => field.listShow !== false);
|
|
1669
1719
|
const gridFields = listFields.slice(0, 8);
|
|
1670
1720
|
const statusField = detectStatusField(fields, safeArgs.statusField);
|
|
@@ -2405,7 +2455,7 @@ function renderMultiLevelFormVue(model, moduleModel) {
|
|
|
2405
2455
|
` tenantId: Session.getTenant(),`,
|
|
2406
2456
|
].join('\n');
|
|
2407
2457
|
const rules = renderFormRulesV2(moduleModel.visibleFields);
|
|
2408
|
-
return `<template>
|
|
2458
|
+
return `<template>
|
|
2409
2459
|
<el-dialog v-model="visible" :title="form.${moduleModel.pk.attrName} ? t('common.editBtn') : t('common.addBtn')" :close-on-click-modal="false" draggable>
|
|
2410
2460
|
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
|
|
2411
2461
|
<el-row :gutter="24">
|
|
@@ -2666,10 +2716,10 @@ function renderMultiLevelIndexVue(model) {
|
|
|
2666
2716
|
)
|
|
2667
2717
|
.join('\n');
|
|
2668
2718
|
|
|
2669
|
-
return
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
<div class="multi-level-dict-layout">
|
|
2719
|
+
return `<!-- 功能名称:${sanitizeHtmlComment(model.featureTitle)} -->
|
|
2720
|
+
<template>
|
|
2721
|
+
<div class="layout-padding">
|
|
2722
|
+
<div class="multi-level-dict-layout">
|
|
2673
2723
|
<div class="multi-level-left">
|
|
2674
2724
|
${renderMultiLevelSchemaListSlot('level1Config', 'activeLevel1Key', 'activeLevel1Module')}
|
|
2675
2725
|
</div>
|
|
@@ -3057,6 +3107,10 @@ function sanitizeComment(value) {
|
|
|
3057
3107
|
return String(value || '').replace(/\*\//g, '* /').replace(/\r?\n/g, ' ').trim();
|
|
3058
3108
|
}
|
|
3059
3109
|
|
|
3110
|
+
function sanitizeHtmlComment(value) {
|
|
3111
|
+
return String(value || '').replace(/--/g, '').replace(/\r?\n/g, ' ').trim();
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3060
3114
|
function renderExtraApiFunctions(model) {
|
|
3061
3115
|
if (!Array.isArray(model.extraApis) || !model.extraApis.length) return '';
|
|
3062
3116
|
return model.extraApis
|
package/package.json
CHANGED