worsoft-frontend-codegen-local-mcp 0.1.11 → 0.1.12

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.
Files changed (2) hide show
  1. package/mcp_server.js +50 -94
  2. package/package.json +1 -1
package/mcp_server.js CHANGED
@@ -4,8 +4,8 @@
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
 
7
- const SERVER_NAME = 'worsoft-codegen-local';
8
- const SERVER_VERSION = '0.1.11';
7
+ const SERVER_NAME = 'worsoft-codegen-local';
8
+ const SERVER_VERSION = '0.1.12';
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');
@@ -127,7 +127,7 @@ export function createCrudSchema(
127
127
  const TOOL_SCHEMA = {
128
128
  type: 'object',
129
129
  properties: {
130
- designFile: { type: 'string', description: 'Absolute or relative Markdown design file path. Defaults to ../sql/SQL 闁荤姳鐒﹀畷姗€顢橀崫銉﹀珰閻庢稒蓱椤?md when omitted.' },
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
133
  children: {
@@ -549,7 +549,6 @@ function isAuditField(fieldName) {
549
549
 
550
550
  function findDictType(comment) {
551
551
  return extractDictType(comment);
552
- const match = comment.match(/(?:闁诲孩绋掗〃鍛村触閳х憢dict)[闂?\s]*([a-zA-Z0-9_]+)/i);
553
552
  }
554
553
 
555
554
  function mapFieldType(field) {
@@ -588,7 +587,7 @@ function stripDictAnnotation(label) {
588
587
  return '';
589
588
  }
590
589
  return text
591
- .replace(/\s*[\(][^()()]*?(?:字典|dict)(?:[_\s-]*type)?[^()()]*?[\)]\s*/gi, '')
590
+ .replace(/\s*[\(\uFF08][^()\uFF08\uFF09]*?(?:\u5B57\u5178|dict)(?:[_\s-]*type)?[^()\uFF08\uFF09]*?[\)\uFF09]\s*/gi, '')
592
591
  .replace(/\s+/g, ' ')
593
592
  .trim();
594
593
  }
@@ -600,8 +599,8 @@ function extractDictType(text) {
600
599
  }
601
600
 
602
601
  const patterns = [
603
- /(?:鐎涙鍚€|dict)(?:[_\s-]*type)?\s*[:閿涙瓥\s*([a-zA-Z0-9_-]+)/i,
604
- /[\(閿涘溈\s*(?:鐎涙鍚€|dict)(?:[_\s-]*type)?\s*[:閿涙瓥?\s*([a-zA-Z0-9_-]+)\s*[\)閿涘、/i,
602
+ /(?:\u5B57\u5178|dict)(?:[_\s-]*type)?\s*[:\uFF1A]\s*([a-zA-Z0-9_-]+)/i,
603
+ /[\(\uFF08]\s*(?:\u5B57\u5178|dict)(?:[_\s-]*type)?\s*[:\uFF1A]?\s*([a-zA-Z0-9_-]+)\s*[\)\uFF09]/i,
605
604
  ];
606
605
 
607
606
  for (const pattern of patterns) {
@@ -641,7 +640,7 @@ function normalizeDefaultValue(value) {
641
640
 
642
641
  function parseRequiredFlag(value) {
643
642
  const normalized = String(value || '').trim().toLowerCase();
644
- return ['闂?, 'y', 'yes', '1', 'true', '闂婎偄娲ら幊搴ㄦ晲?].includes(normalized);
643
+ return ['y', 'yes', '1', 'true', 'required'].includes(normalized);
645
644
  }
646
645
 
647
646
  function parseMarkdownRow(line) {
@@ -667,7 +666,7 @@ function findPrimaryKeyFromText(text, fields) {
667
666
  return quotedMatch[1];
668
667
  }
669
668
 
670
- const commentPk = fields.find((field) => /婵炴垶鎸搁…鐑藉极?i.test(field.comment));
669
+ const commentPk = fields.find((field) => /主键|primary\s+key|\bpk\b/i.test(field.comment));
671
670
  if (commentPk) {
672
671
  return commentPk.fieldName;
673
672
  }
@@ -700,14 +699,14 @@ function parseMarkdownTableSection(tableName, tableComment, sectionLines) {
700
699
 
701
700
  const headers = rows[0];
702
701
  const dataRows = rows.slice(1).filter((cells) => !isMarkdownSeparatorRow(cells));
703
- const fieldNameIndex = findHeaderIndex(headers, [/闁诲孩绋掗〃鍡涱敊瀹€鍕Е?, /闂佸憡甯楅〃鍛村箖?i, /field/i]);
704
- const sqlTypeIndex = findHeaderIndex(headers, [/缂備緡鍋夐褔鎮?, /闂佽桨鑳舵晶妤€鐣垫担铏瑰暗閻犲洩灏欓埀?, /type/i]);
705
- const lengthIndex = findHeaderIndex(headers, [/闂傚倵鍋撻柛顭戝枛椤?, /缂備緡鍠楅崕宕団偓?, /length/i]);
706
- const requiredIndex = findHeaderIndex(headers, [/闂婎偄娲ら幊搴ㄦ晲?, /闂婎偄娲ら幊鎾舵?, /required/i]);
707
- const defaultIndex = findHeaderIndex(headers, [/婵帗绋掗…鍫ヮ敇?, /default/i]);
708
- const commentIndex = findHeaderIndex(headers, [/闁荤姴娲ら悺銊ノ?, /婵犮垼娉涘ú锕傚极?, /comment/i]);
702
+ const fieldNameIndex = findHeaderIndex(headers, [/field/i, /\u5B57\u6BB5/i, /\u5B57\u6BB5\u540D/i, /\u5217\u540D/i]);
703
+ const sqlTypeIndex = findHeaderIndex(headers, [/type/i, /\u7C7B\u578B/i, /\u5B57\u6BB5\u7C7B\u578B/i, /\u6570\u636E\u7C7B\u578B/i]);
704
+ const lengthIndex = findHeaderIndex(headers, [/length/i, /\u957F\u5EA6/i, /\u5B57\u6BB5\u957F\u5EA6/i]);
705
+ const requiredIndex = findHeaderIndex(headers, [/required/i, /\u5FC5\u586B/i, /\u662F\u5426\u5FC5\u586B/i, /\u4E0D\u80FD\u4E3A\u7A7A/i]);
706
+ const defaultIndex = findHeaderIndex(headers, [/default/i, /\u9ED8\u8BA4/i, /\u9ED8\u8BA4\u503C/i]);
707
+ const commentIndex = findHeaderIndex(headers, [/comment/i, /\u5907\u6CE8/i, /\u8BF4\u660E/i, /\u5B57\u6BB5\u8BF4\u660E/i]);
709
708
 
710
- const dictTypeIndex = findHeaderIndex(headers, [/闁诲孩绋掗〃鍛村触閳?, /dict/i, /dict_?type/i]);
709
+ const dictTypeIndex = findHeaderIndex(headers, [/dict/i, /dict_?type/i, /\u5B57\u5178/i, /\u5B57\u5178\u7C7B\u578B/i]);
711
710
 
712
711
  if (fieldNameIndex < 0 || sqlTypeIndex < 0) {
713
712
  throw new Error('Markdown field table headers are missing required columns for ' + tableName);
@@ -755,7 +754,7 @@ function parseMarkdownDesignTables(markdownText) {
755
754
 
756
755
  for (let index = 0; index < lines.length; index += 1) {
757
756
  const heading = lines[index].trim();
758
- const headingMatch = heading.match(/^###\s+\S+\s+([a-zA-Z0-9_]+)\s*[\(闂佹寧绋戝┃?[^)\n闂佹寧绋戦妴?)[\)闂佹寧绋戦妴?);
757
+ const headingMatch = heading.match(/^###\s+\S+\s+([a-zA-Z0-9_]+)\s*[\(\uFF08]([^)\n\uFF09]+)[\)\uFF09]/);
759
758
  if (!headingMatch) continue;
760
759
 
761
760
  const tableName = headingMatch[1];
@@ -785,59 +784,10 @@ function stripMarkdownSyntax(text) {
785
784
  .replace(/\r\n?/g, '\n');
786
785
  }
787
786
 
788
- function parseMarkdownRelations(markdownText) {
789
- const text = stripMarkdownSyntax(markdownText);
790
- const blocks = [];
791
- const headingRegex = /^(###|####)\s+(.+)$/gm;
792
- let current = null;
793
- let match = headingRegex.exec(text);
794
-
795
- while (match) {
796
- if (current) {
797
- current.body = text.slice(current.start, match.index);
798
- blocks.push(current);
799
- }
800
-
801
- current = {
802
- title: match[2].trim(),
803
- start: headingRegex.lastIndex,
804
- body: '',
805
- };
806
- match = headingRegex.exec(text);
807
- }
808
-
809
- if (current) {
810
- current.body = text.slice(current.start);
811
- blocks.push(current);
812
- }
813
-
814
- const relations = [];
815
- for (const block of blocks) {
816
- const lines = block.body.split('\n').map((line) => line.trim()).filter(Boolean);
817
- const mainLine = lines.find((line) => /(婵炴垶鎸诲Σ鎺楀Υ閸庡嫰鏌i弽銊ュ姸闁?\s*[:闂佹寧绋掗悺?.test(line));
818
- const childLine = lines.find((line) => /(婵炲濮村锕傚Υ閸庡嫰鎮楀☉娆忓闁?\s*[:闂佹寧绋掗悺?.test(line));
819
- const fieldLine = lines.find((line) => /闂佺绻愰悿鍥ㄧ閸懇鍋撳☉娆樻畷妞ゆ柨顒竤*[:闂佹寧绋掗悺?.test(line));
820
- if (!mainLine || !childLine || !fieldLine) continue;
821
-
822
- const mainIdentifiers = extractIdentifiersFromLine(mainLine);
823
- const childIdentifiers = extractIdentifiersFromLine(childLine);
824
- const fieldIdentifiers = extractIdentifiersFromLine(fieldLine);
825
- if (!mainIdentifiers.length || !childIdentifiers.length || fieldIdentifiers.length < 2) continue;
826
-
827
- const relationTypeLine = lines.find((line) => /闂佺绻愮壕顓㈡焾鐎靛摜灏甸悹鍥皺閳ь剛锕*[:闂佹寧绋掗悺?.test(line));
828
- relations.push({
829
- title: block.title,
830
- mainTableName: mainIdentifiers[0],
831
- childTableName: childIdentifiers[0],
832
- childField: fieldIdentifiers[0],
833
- mainField: fieldIdentifiers[1],
834
- relationType: relationTypeLine ? relationTypeLine.split(/[:闂佹寧绋掗悺?).slice(1).join(':').trim() : '',
835
- });
836
- }
837
-
838
- return relations;
839
- }
840
-
787
+ function parseMarkdownRelations(markdownText) {
788
+ void markdownText;
789
+ return [];
790
+ }
841
791
  function parseMarkdownDesignFile(markdownText) {
842
792
  return {
843
793
  tables: parseMarkdownDesignTables(markdownText),
@@ -968,13 +918,13 @@ function buildRetryArguments(safeArgs, sourceFile, childTableName) {
968
918
  function buildRelationCorrectionEntry(safeArgs, sourceFile, candidates, selectedChildTableName) {
969
919
  return {
970
920
  field: 'childTableName',
971
- title: '婵炴垶鎹侀褔鎮哄▎鎴炲仒闁靛鍎辫ぐ鐘绘煠鏉堛劍鐓i柟渚垮妼椤垽鏁愰崨顓炴辈闂?,
921
+ title: '请选择子表',
972
922
  tableName: safeArgs.tableName,
973
923
  style: safeArgs.style,
974
924
  designFile: sourceFile,
975
925
  description: candidates.length
976
926
  ? 'If the current child relation is not the one you want, pass childTableName to choose a candidate from the design file.'
977
- : 'No child relation was found. Check the 婵炴垶鎹侀濠勫垝閻樺灚鍋橀柕濞垮劚瑜扮娀鏌ゆ潏銊︻梿妞ゆ洍鏅犲?section in the design file.',
927
+ : 'No child relation was found. Check the child relation section in the design file.',
978
928
  currentValue: selectedChildTableName || '',
979
929
  options: candidates.map((item) => item.childTableName),
980
930
  candidates,
@@ -1182,12 +1132,14 @@ function renderTemplate(templateText, replacements) {
1182
1132
  function renderFormField(field) {
1183
1133
  const label = stripDictAnnotation(field.comment).replace(/'/g, "\\'");
1184
1134
  const prop = field.attrName;
1135
+ const inputPlaceholderExpr = `inputPlaceholder(getMasterFieldLabel('${prop}'))`;
1136
+ const selectPlaceholderExpr = `selectPlaceholder(getMasterFieldLabel('${prop}'))`;
1185
1137
 
1186
1138
  if (field.formType === 'select') {
1187
1139
  return [
1188
1140
  ' <el-col :span="12" class="mb20">',
1189
1141
  ` <el-form-item label="${label}" prop="${prop}">`,
1190
- ` <el-select v-model="form.${prop}" placeholder="闁荤姴娲ㄩ崗姗€鍩€椤掆偓椤︽壆鈧?{label}" style="width: 100%">`,
1142
+ ` <el-select v-model="form.${prop}" :placeholder="${selectPlaceholderExpr}" style="width: 100%">`,
1191
1143
  ` <el-option v-for="item in ${field.dictType}" :key="item.value" :label="item.label" :value="Number(item.value)" />`,
1192
1144
  ' </el-select>',
1193
1145
  ' </el-form-item>',
@@ -1196,12 +1148,12 @@ function renderFormField(field) {
1196
1148
  }
1197
1149
 
1198
1150
  if (field.formType === 'number') {
1199
- const max = field.comment.includes('%') || field.comment.includes('濠殿噯绲鹃弻褏娆?) ? ' :max="100"' : '';
1151
+ const max = field.comment.includes('%') || /\u6BD4\u4F8B/.test(field.comment) ? ' :max="100"' : '';
1200
1152
  const precision = field.sqlType === 'DECIMAL' && field.scale ? ` :precision="${field.scale}" :step="0.01"` : '';
1201
1153
  return [
1202
1154
  ' <el-col :span="12" class="mb20">',
1203
1155
  ` <el-form-item label="${label}" prop="${prop}">`,
1204
- ` <el-input-number v-model="form.${prop}" :min="0"${max}${precision} placeholder="闁荤姴娲ㄩ弻澶屾椤撱垹绀?{label}" style="width: 100%" />`,
1156
+ ` <el-input-number v-model="form.${prop}" :min="0"${max}${precision} :placeholder="${inputPlaceholderExpr}" style="width: 100%" />`,
1205
1157
  ' </el-form-item>',
1206
1158
  ' </el-col>',
1207
1159
  ].join('\n');
@@ -1213,7 +1165,7 @@ function renderFormField(field) {
1213
1165
  return [
1214
1166
  ' <el-col :span="12" class="mb20">',
1215
1167
  ` <el-form-item label="${label}" prop="${prop}">`,
1216
- ` <el-date-picker type="${pickerType}" placeholder="闁荤姴娲ㄩ崗姗€鍩€椤掆偓椤︽壆鈧?{label}" v-model="form.${prop}" :value-format="${formatName}" style="width: 100%"></el-date-picker>`,
1168
+ ` <el-date-picker type="${pickerType}" :placeholder="${inputPlaceholderExpr}" v-model="form.${prop}" :value-format="${formatName}" style="width: 100%"></el-date-picker>`,
1217
1169
  ' </el-form-item>',
1218
1170
  ' </el-col>',
1219
1171
  ].join('\n');
@@ -1223,7 +1175,7 @@ function renderFormField(field) {
1223
1175
  return [
1224
1176
  ' <el-col :span="24" class="mb20">',
1225
1177
  ` <el-form-item label="${label}" prop="${prop}">`,
1226
- ` <el-input type="textarea" v-model="form.${prop}" placeholder="闁荤姴娲ㄩ弻澶屾椤撱垹绀?{label}" />`,
1178
+ ` <el-input type="textarea" v-model="form.${prop}" :placeholder="${inputPlaceholderExpr}" />`,
1227
1179
  ' </el-form-item>',
1228
1180
  ' </el-col>',
1229
1181
  ].join('\n');
@@ -1232,7 +1184,7 @@ function renderFormField(field) {
1232
1184
  return [
1233
1185
  ' <el-col :span="12" class="mb20">',
1234
1186
  ` <el-form-item label="${label}" prop="${prop}">`,
1235
- ` <el-input v-model="form.${prop}" placeholder="闁荤姴娲ㄩ弻澶屾椤撱垹绀?{label}" />`,
1187
+ ` <el-input v-model="form.${prop}" :placeholder="${inputPlaceholderExpr}" />`,
1236
1188
  ' </el-form-item>',
1237
1189
  ' </el-col>',
1238
1190
  ].join('\n');
@@ -1255,16 +1207,18 @@ function renderTableColumn(field) {
1255
1207
  function renderChildTableColumn(field, childListName) {
1256
1208
  const label = stripDictAnnotation(field.comment).replace(/'/g, "\\'");
1257
1209
  const rules = field.notNull ? ` :rules="[{ required: true, trigger: 'blur' }]"` : '';
1210
+ const inputPlaceholderExpr = `inputPlaceholder(getChildFieldLabel('${childListName}', '${field.attrName}'))`;
1211
+ const selectPlaceholderExpr = `selectPlaceholder(getChildFieldLabel('${childListName}', '${field.attrName}'))`;
1258
1212
 
1259
- let control = ` <el-input v-model="row.${field.attrName}" />`;
1213
+ let control = ` <el-input v-model="row.${field.attrName}" :placeholder="${inputPlaceholderExpr}" />`;
1260
1214
  if (field.formType === 'select' && field.dictType) {
1261
1215
  control = [
1262
- ` <el-select v-model="row.${field.attrName}" placeholder="闁荤姴娲ㄩ崗姗€鍩€椤掆偓椤︽壆鈧?{label}" style="width: 100%">`,
1216
+ ` <el-select v-model="row.${field.attrName}" :placeholder="${selectPlaceholderExpr}" style="width: 100%">`,
1263
1217
  ` <el-option v-for="item in ${field.dictType}" :key="item.value" :label="item.label" :value="Number(item.value)" />`,
1264
1218
  ' </el-select>',
1265
1219
  ].join('\n');
1266
1220
  } else if (field.formType === 'number') {
1267
- const max = field.comment.includes('%') || field.comment.includes('濠殿噯绲鹃弻褏娆?) ? ' :max="100"' : '';
1221
+ const max = field.comment.includes('%') || /\u6BD4\u4F8B/.test(field.comment) ? ' :max="100"' : '';
1268
1222
  const precision = field.sqlType === 'DECIMAL' && field.scale ? ` :precision="${field.scale}" :step="0.01"` : '';
1269
1223
  control = ` <el-input-number v-model="row.${field.attrName}" :min="0"${max}${precision} style="width: 100%" />`;
1270
1224
  }
@@ -1417,7 +1371,7 @@ function renderFormFieldV2(field) {
1417
1371
  }
1418
1372
 
1419
1373
  if (field.formType === 'number') {
1420
- const max = field.comment.includes('%') || field.comment.includes('婵犳鍣徊楣冨蓟瑜忓▎?) ? ' :max="100"' : '';
1374
+ const max = field.comment.includes('%') || /\u6BD4\u4F8B/.test(field.comment) ? ' :max="100"' : '';
1421
1375
  const precision = field.sqlType === 'DECIMAL' && field.scale ? ` :precision="${field.scale}" :step="0.01"` : '';
1422
1376
  return [
1423
1377
  ` <el-col v-if="${visibilityExpr}" :span="12" class="mb20">`,
@@ -1434,26 +1388,28 @@ function renderFormFieldV2(field) {
1434
1388
  return [
1435
1389
  ` <el-col v-if="${visibilityExpr}" :span="12" class="mb20">`,
1436
1390
  ` <el-form-item :label="${labelExpr}" prop="${prop}">`,
1437
- ` <el-date-picker type="${pickerType}" :placeholder="selectPlaceholder(${labelExpr})" v-model="form.${prop}" :value-format="${formatName}" style="width: 100%"></el-date-picker>`,
1391
+ ` <el-date-picker type="${pickerType}" :placeholder="inputPlaceholder(${labelExpr})" v-model="form.${prop}" :value-format="${formatName}" style="width: 100%"></el-date-picker>`,
1438
1392
  ' </el-form-item>',
1439
1393
  ' </el-col>',
1440
1394
  ].join('\n');
1441
1395
  }
1442
1396
 
1443
1397
  if (field.formType === 'textarea') {
1398
+ const textareaAttrs = renderTextareaMaxlengthAttrsV2(field);
1444
1399
  return [
1445
1400
  ` <el-col v-if="${visibilityExpr}" :span="24" class="mb20">`,
1446
1401
  ` <el-form-item :label="${labelExpr}" prop="${prop}">`,
1447
- ` <el-input type="textarea" v-model="form.${prop}" :placeholder="inputPlaceholder(${labelExpr})"${renderTextareaMaxlengthAttrsV2(field)} />`,
1402
+ ` <el-input type="textarea" v-model="form.${prop}" :placeholder="inputPlaceholder(${labelExpr})"${textareaAttrs} />`,
1448
1403
  ' </el-form-item>',
1449
1404
  ' </el-col>',
1450
1405
  ].join('\n');
1451
1406
  }
1452
1407
 
1408
+ const maxlengthAttr = renderInputMaxlengthAttr(field);
1453
1409
  return [
1454
1410
  ` <el-col v-if="${visibilityExpr}" :span="12" class="mb20">`,
1455
1411
  ` <el-form-item :label="${labelExpr}" prop="${prop}">`,
1456
- ` <el-input v-model="form.${prop}" :placeholder="inputPlaceholder(${labelExpr})"${renderTextMaxlengthAttrV2(field)} />`,
1412
+ ` <el-input v-model="form.${prop}" :placeholder="inputPlaceholder(${labelExpr})"${maxlengthAttr} />`,
1457
1413
  ' </el-form-item>',
1458
1414
  ' </el-col>',
1459
1415
  ].join('\n');
@@ -1480,7 +1436,7 @@ function renderChildTableColumnV2(field, childListName) {
1480
1436
  const labelExpr = `getChildFieldLabel('${childListName}', '${field.attrName}')`;
1481
1437
  const dictExpr = `getChildFieldMeta('${childListName}', '${field.attrName}')?.dictType`;
1482
1438
 
1483
- let control = ` <el-input v-model="row.${field.attrName}"${renderTextMaxlengthAttrV2(field)} />`;
1439
+ let control = ` <el-input v-model="row.${field.attrName}" :placeholder="inputPlaceholder(${labelExpr})"${renderTextMaxlengthAttrV2(field)} />`;
1484
1440
  if (field.formType === 'select' && field.dictType) {
1485
1441
  control = [
1486
1442
  ` <el-select v-model="row.${field.attrName}" :placeholder="selectPlaceholder(${labelExpr})" style="width: 100%">`,
@@ -1488,15 +1444,15 @@ function renderChildTableColumnV2(field, childListName) {
1488
1444
  ' </el-select>',
1489
1445
  ].join('\n');
1490
1446
  } else if (field.formType === 'number') {
1491
- const max = field.comment.includes('%') || field.comment.includes('婵犳鍣徊楣冨蓟瑜忓▎?) ? ' :max="100"' : '';
1447
+ const max = field.comment.includes('%') || /\u6BD4\u4F8B/.test(field.comment) ? ' :max="100"' : '';
1492
1448
  const precision = field.sqlType === 'DECIMAL' && field.scale ? ` :precision="${field.scale}" :step="0.01"` : '';
1493
1449
  control = ` <el-input-number v-model="row.${field.attrName}" :min="0"${max}${precision} style="width: 100%" />`;
1494
1450
  } else if (field.formType === 'datetime' || field.formType === 'date') {
1495
1451
  const pickerType = field.formType === 'datetime' ? 'datetime' : 'date';
1496
1452
  const formatName = field.formType === 'datetime' ? 'dateTimeStr' : 'dateStr';
1497
- control = ` <el-date-picker type="${pickerType}" v-model="row.${field.attrName}" :value-format="${formatName}" :placeholder="selectPlaceholder(${labelExpr})" style="width: 100%"></el-date-picker>`;
1453
+ control = ` <el-date-picker type="${pickerType}" v-model="row.${field.attrName}" :value-format="${formatName}" :placeholder="inputPlaceholder(${labelExpr})" style="width: 100%"></el-date-picker>`;
1498
1454
  } else if (field.formType === 'textarea') {
1499
- control = ` <el-input type="textarea" v-model="row.${field.attrName}"${renderTextareaMaxlengthAttrsV2(field)} />`;
1455
+ control = ` <el-input type="textarea" v-model="row.${field.attrName}" :placeholder="inputPlaceholder(${labelExpr})"${renderTextareaMaxlengthAttrsV2(field)} />`;
1500
1456
  }
1501
1457
 
1502
1458
  return [
@@ -1526,11 +1482,11 @@ function renderChildSectionV2(childModel, childCount) {
1526
1482
  ].join('\n');
1527
1483
  }
1528
1484
 
1529
- function renderValidationRule(field) {
1530
- if (!field.notNull) return null;
1531
- const label = stripDictAnnotation(field.comment).replace(/'/g, "\\'");
1532
- return ` ${field.attrName}: [{ required: true, message: '${label}婵炴垶鎸哥粔鐑藉礂濡崵鈻旈柧蹇撳帨閺?, trigger: 'blur' }],`;
1533
- }
1485
+ function renderValidationRule(field) {
1486
+ if (!field.notNull) return null;
1487
+ const label = stripDictAnnotation(field.comment).replace(/'/g, "\\'");
1488
+ return ` ${field.attrName}: [{ required: true, message: '${label}\u4E0D\u80FD\u4E3A\u7A7A', trigger: 'blur' }],`;
1489
+ }
1534
1490
 
1535
1491
  function renderFormRules(visibleFields) {
1536
1492
  const lines = visibleFields.map(renderValidationRule).filter(Boolean);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worsoft-frontend-codegen-local-mcp",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Worsoft frontend local-template code generation MCP server.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "worsoft <sw@worsoft.vip>",