worsoft-frontend-codegen-local-mcp 0.1.27 → 0.1.28

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 +305 -535
  2. 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.27';
8
+ const SERVER_VERSION = '0.1.28';
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');
@@ -774,21 +774,21 @@ function normalizeDefaultValue(value) {
774
774
  return normalized;
775
775
  }
776
776
 
777
- function parseBooleanLike(value, defaultValue = false) {
778
- if (value === undefined || value === null || value === '') {
779
- return defaultValue;
780
- }
781
- if (typeof value === 'boolean') {
782
- return value;
783
- }
784
- const normalized = String(value).trim().toLowerCase();
785
- if (['true', '1', 'yes', 'y', 'required', 'shi', 'show', '\u662f', '\u663e\u793a'].includes(normalized)) {
786
- return true;
787
- }
788
- if (['false', '0', 'no', 'n', 'fou', 'hide', '\u5426', '\u9690\u85cf'].includes(normalized)) {
789
- return false;
790
- }
791
- return defaultValue;
777
+ function parseBooleanLike(value, defaultValue = false) {
778
+ if (value === undefined || value === null || value === '') {
779
+ return defaultValue;
780
+ }
781
+ if (typeof value === 'boolean') {
782
+ return value;
783
+ }
784
+ const normalized = String(value).trim().toLowerCase();
785
+ if (['true', '1', 'yes', 'y', 'required', 'shi', 'show', '\u662f', '\u663e\u793a'].includes(normalized)) {
786
+ return true;
787
+ }
788
+ if (['false', '0', 'no', 'n', 'fou', 'hide', '\u5426', '\u9690\u85cf'].includes(normalized)) {
789
+ return false;
790
+ }
791
+ return defaultValue;
792
792
  }
793
793
 
794
794
 
@@ -807,19 +807,19 @@ function normalizeApiPath(value) {
807
807
  .replace(/\/+$/, '');
808
808
  }
809
809
 
810
- function normalizeStructuredSqlType(value) {
811
- const normalized = String(value || '').trim();
812
- const upper = normalized.replace(/\s+/g, '').toUpperCase();
813
-
814
- if (!upper) return 'VARCHAR';
815
- if (['STRING', 'VARCHAR', 'CHAR'].includes(upper) || ['\u6587\u672c', '\u5b57\u7b26\u4e32', '\u5b57\u7b26'].includes(normalized)) return 'VARCHAR';
816
- if (['TEXTAREA', 'LONGTEXT', 'TEXT'].includes(upper) || ['\u957f\u6587\u672c', '\u6587\u672c\u57df'].includes(normalized)) return 'TEXT';
817
- if (['LONG', 'BIGINT', 'LONGINTEGER'].includes(upper) || ['\u957f\u6574\u6570', '\u957f\u6574\u578b'].includes(normalized)) return 'BIGINT';
818
- if (['INT', 'INTEGER', 'SHORT', 'SMALLINT'].includes(upper) || ['\u6574\u6570', '\u6574\u578b'].includes(normalized)) return 'INT';
819
- if (['DECIMAL', 'NUMERIC', 'BIGDECIMAL', 'DOUBLE', 'FLOAT'].includes(upper) || ['\u5c0f\u6570', '\u6570\u503c'].includes(normalized)) return 'DECIMAL';
820
- if (['LOCALDATE', 'DATE'].includes(upper) || normalized === '\u65e5\u671f') return 'DATE';
821
- if (['LOCALDATETIME', 'DATETIME', 'TIMESTAMP'].includes(upper) || ['\u65e5\u671f\u65f6\u95f4', '\u65f6\u95f4\u6233'].includes(normalized)) return 'DATETIME';
822
- return upper;
810
+ function normalizeStructuredSqlType(value) {
811
+ const normalized = String(value || '').trim();
812
+ const upper = normalized.replace(/\s+/g, '').toUpperCase();
813
+
814
+ if (!upper) return 'VARCHAR';
815
+ if (['STRING', 'VARCHAR', 'CHAR'].includes(upper) || ['\u6587\u672c', '\u5b57\u7b26\u4e32', '\u5b57\u7b26'].includes(normalized)) return 'VARCHAR';
816
+ if (['TEXTAREA', 'LONGTEXT', 'TEXT'].includes(upper) || ['\u957f\u6587\u672c', '\u6587\u672c\u57df'].includes(normalized)) return 'TEXT';
817
+ if (['LONG', 'BIGINT', 'LONGINTEGER'].includes(upper) || ['\u957f\u6574\u6570', '\u957f\u6574\u578b'].includes(normalized)) return 'BIGINT';
818
+ if (['INT', 'INTEGER', 'SHORT', 'SMALLINT'].includes(upper) || ['\u6574\u6570', '\u6574\u578b'].includes(normalized)) return 'INT';
819
+ if (['DECIMAL', 'NUMERIC', 'BIGDECIMAL', 'DOUBLE', 'FLOAT'].includes(upper) || ['\u5c0f\u6570', '\u6570\u503c'].includes(normalized)) return 'DECIMAL';
820
+ if (['LOCALDATE', 'DATE'].includes(upper) || normalized === '\u65e5\u671f') return 'DATE';
821
+ if (['LOCALDATETIME', 'DATETIME', 'TIMESTAMP'].includes(upper) || ['\u65e5\u671f\u65f6\u95f4', '\u65f6\u95f4\u6233'].includes(normalized)) return 'DATETIME';
822
+ return upper;
823
823
  }
824
824
  function normalizeStructuredLengthAndScale(lengthValue, scaleValue) {
825
825
  const parsed = splitLength(lengthValue);
@@ -1270,7 +1270,7 @@ function renderFormField(field) {
1270
1270
  ' <el-col :span="12" class="mb20">',
1271
1271
  ` <el-form-item label="${label}" prop="${prop}">`,
1272
1272
  ` <el-select v-model="form.${prop}" :placeholder="${selectPlaceholderExpr}" style="width: 100%">`,
1273
- ` <el-option v-for="item in ${field.dictType}" :key="item.value" :label="item.label" :value="Number(item.value)" />`,
1273
+ ` <el-option v-for="item in ${field.dictType}" :key="item.value" :label="item.label" :value="item.value" />`,
1274
1274
  ' </el-select>',
1275
1275
  ' </el-form-item>',
1276
1276
  ' </el-col>',
@@ -1344,7 +1344,7 @@ function renderChildTableColumn(field, childListName) {
1344
1344
  if (field.formType === 'select' && field.dictType) {
1345
1345
  control = [
1346
1346
  ` <el-select v-model="row.${field.attrName}" :placeholder="${selectPlaceholderExpr}" style="width: 100%">`,
1347
- ` <el-option v-for="item in ${field.dictType}" :key="item.value" :label="item.label" :value="Number(item.value)" />`,
1347
+ ` <el-option v-for="item in ${field.dictType}" :key="item.value" :label="item.label" :value="item.value" />`,
1348
1348
  ' </el-select>',
1349
1349
  ].join('\n');
1350
1350
  } else if (field.formType === 'number') {
@@ -1379,6 +1379,13 @@ function renderDefaultLine(field) {
1379
1379
  return ` ${field.attrName}: '',`;
1380
1380
  }
1381
1381
 
1382
+ function renderFormRulesV2(fields) {
1383
+ return fields
1384
+ .filter((field) => field.notNull)
1385
+ .map((field) => ` ${field.attrName}: [{ required: true, message: fieldRequiredMessage('${field.attrName}'), trigger: 'blur' }],`)
1386
+ .join('\n');
1387
+ }
1388
+
1382
1389
  function renderFormDefaults(model) {
1383
1390
  const lines = [` ${model.pk.attrName}: '',`];
1384
1391
  for (const field of model.fields.filter((item) => item.fieldName !== model.pk.fieldName && !item.isAudit)) lines.push(renderDefaultLine(field));
@@ -1439,63 +1446,63 @@ function renderDictImportBlock(dictTypes) {
1439
1446
  ].join('\n');
1440
1447
  }
1441
1448
 
1442
- function normalizeWidthLabel(field) {
1443
- return stripDictAnnotation(field.comment).replace(/\s+/g, '').trim();
1444
- }
1445
-
1446
- function getLabelVisibleLength(label) {
1447
- return String(label || '').replace(/[()\uFF08\uFF09{}\[\]\-_,.:\uFF1A;\uFF1B\/\\\s]/g, '').length;
1448
- }
1449
-
1450
- function matchesAnyLabel(label, values) {
1451
- return values.some((value) => label === value);
1452
- }
1453
-
1454
- function includesAnyLabel(label, values) {
1455
- return values.some((value) => label.includes(value));
1456
- }
1457
-
1458
- function getDefaultOptionFieldWidthV2(field) {
1459
- const label = normalizeWidthLabel(field);
1460
- const visibleLength = getLabelVisibleLength(label);
1461
-
1462
- if (field.formType === 'textarea') return '300';
1463
-
1464
- if (matchesAnyLabel(label, ['\u72b6\u6001', '\u5355\u636e\u72b6\u6001'])) return '80';
1465
- if (label === '\u9879\u76ee\u7f16\u53f7') return '80';
1466
- if (label === '\u7edf\u4e00\u793e\u4f1a\u4fe1\u7528\u4ee3\u7801') return '160';
1467
- if (matchesAnyLabel(label, ['\u56fd\u5bb6'])) return '100';
1468
- if (matchesAnyLabel(label, ['\u7701\u4efd', '\u57ce\u5e02'])) return '120';
1469
- if (matchesAnyLabel(label, ['\u89c4\u6a21'])) return '100';
1470
- if (matchesAnyLabel(label, ['\u9879\u76ee\u7b80\u79f0', '\u8d44\u8d28\u4f7f\u7528'])) return '200';
1471
- if (matchesAnyLabel(label, ['\u4e3b\u8ddf\u8e2a\u4eba', '\u8ddf\u8e2a\u5355\u4f4d', '\u7ec4\u7ec7\u673a\u6784\u5168\u8def\u5f84'])) return '200';
1472
- if (matchesAnyLabel(label, ['\u4e1a\u4e3b\u7b80\u79f0', '\u4f9b\u5e94\u5546\u7b80\u79f0', '\u7269\u8d44\u540d\u79f0', '\u4f5c\u4e1a\u540d\u79f0'])) return '150';
1473
- if (matchesAnyLabel(label, ['\u89c4\u683c\u578b\u53f7', '\u8ba1\u91cf\u5355\u4f4d'])) return '80';
1474
- if (matchesAnyLabel(label, ['\u4e1a\u4e3b\u5355\u4f4d', '\u76d1\u7406\u5355\u4f4d', '\u8bbe\u8ba1\u5355\u4f4d', '\u5206\u5305\u5355\u4f4d', '\u4f9b\u5e94\u5546\u540d\u79f0'])) return '250';
1475
- if (matchesAnyLabel(label, ['\u6750\u6599\u7279\u5f81'])) return '200';
1476
-
1477
- if (matchesAnyLabel(label, ['\u5408\u540c\u7f16\u53f7', '\u5355\u636e\u7f16\u53f7']) || (label !== '\u9879\u76ee\u7f16\u53f7' && label.endsWith('\u7f16\u7801'))) return '200';
1478
-
1479
- if (matchesAnyLabel(label, [
1480
- '\u9879\u76ee\u540d\u79f0',
1481
- '\u9879\u76ee\u540d\u79f0\u5408\u540c',
1482
- '\u9879\u76ee\u540d\u79f0\u4e2d\u6587',
1483
- '\u62db\u6807\u9879\u76ee\u540d\u79f0',
1484
- '\u5408\u540c\u540d\u79f0',
1485
- '\u5408\u540c\u540d\u79f0\u5408\u540c',
1486
- '\u5408\u540c\u540d\u79f0\u4e2d\u6587',
1487
- ])) return '350';
1488
-
1489
- if (includesAnyLabel(label, ['\u91d1\u989d'])) return '180';
1490
- if (includesAnyLabel(label, ['\u6570\u91cf', '\u5355\u4ef7', '\u6570\u989d'])) return '140';
1491
- if (label.endsWith('\u4eba')) return '90';
1492
- if (includesAnyLabel(label, ['\u7535\u8bdd'])) return '100';
1493
-
1494
- if (field.formType === 'datetime') return '150';
1495
- if (field.formType === 'date') return visibleLength > 7 ? '170' : '90';
1496
- if (field.formType === 'select') return visibleLength > 5 ? '200' : '100';
1497
-
1498
- return '100';
1449
+ function normalizeWidthLabel(field) {
1450
+ return stripDictAnnotation(field.comment).replace(/\s+/g, '').trim();
1451
+ }
1452
+
1453
+ function getLabelVisibleLength(label) {
1454
+ return String(label || '').replace(/[()\uFF08\uFF09{}\[\]\-_,.:\uFF1A;\uFF1B\/\\\s]/g, '').length;
1455
+ }
1456
+
1457
+ function matchesAnyLabel(label, values) {
1458
+ return values.some((value) => label === value);
1459
+ }
1460
+
1461
+ function includesAnyLabel(label, values) {
1462
+ return values.some((value) => label.includes(value));
1463
+ }
1464
+
1465
+ function getDefaultOptionFieldWidthV2(field) {
1466
+ const label = normalizeWidthLabel(field);
1467
+ const visibleLength = getLabelVisibleLength(label);
1468
+
1469
+ if (field.formType === 'textarea') return '300';
1470
+
1471
+ if (matchesAnyLabel(label, ['\u72b6\u6001', '\u5355\u636e\u72b6\u6001'])) return '80';
1472
+ if (label === '\u9879\u76ee\u7f16\u53f7') return '80';
1473
+ if (label === '\u7edf\u4e00\u793e\u4f1a\u4fe1\u7528\u4ee3\u7801') return '160';
1474
+ if (matchesAnyLabel(label, ['\u56fd\u5bb6'])) return '100';
1475
+ if (matchesAnyLabel(label, ['\u7701\u4efd', '\u57ce\u5e02'])) return '120';
1476
+ if (matchesAnyLabel(label, ['\u89c4\u6a21'])) return '100';
1477
+ if (matchesAnyLabel(label, ['\u9879\u76ee\u7b80\u79f0', '\u8d44\u8d28\u4f7f\u7528'])) return '200';
1478
+ if (matchesAnyLabel(label, ['\u4e3b\u8ddf\u8e2a\u4eba', '\u8ddf\u8e2a\u5355\u4f4d', '\u7ec4\u7ec7\u673a\u6784\u5168\u8def\u5f84'])) return '200';
1479
+ if (matchesAnyLabel(label, ['\u4e1a\u4e3b\u7b80\u79f0', '\u4f9b\u5e94\u5546\u7b80\u79f0', '\u7269\u8d44\u540d\u79f0', '\u4f5c\u4e1a\u540d\u79f0'])) return '150';
1480
+ if (matchesAnyLabel(label, ['\u89c4\u683c\u578b\u53f7', '\u8ba1\u91cf\u5355\u4f4d'])) return '80';
1481
+ if (matchesAnyLabel(label, ['\u4e1a\u4e3b\u5355\u4f4d', '\u76d1\u7406\u5355\u4f4d', '\u8bbe\u8ba1\u5355\u4f4d', '\u5206\u5305\u5355\u4f4d', '\u4f9b\u5e94\u5546\u540d\u79f0'])) return '250';
1482
+ if (matchesAnyLabel(label, ['\u6750\u6599\u7279\u5f81'])) return '200';
1483
+
1484
+ if (matchesAnyLabel(label, ['\u5408\u540c\u7f16\u53f7', '\u5355\u636e\u7f16\u53f7']) || (label !== '\u9879\u76ee\u7f16\u53f7' && label.endsWith('\u7f16\u7801'))) return '200';
1485
+
1486
+ if (matchesAnyLabel(label, [
1487
+ '\u9879\u76ee\u540d\u79f0',
1488
+ '\u9879\u76ee\u540d\u79f0\u5408\u540c',
1489
+ '\u9879\u76ee\u540d\u79f0\u4e2d\u6587',
1490
+ '\u62db\u6807\u9879\u76ee\u540d\u79f0',
1491
+ '\u5408\u540c\u540d\u79f0',
1492
+ '\u5408\u540c\u540d\u79f0\u5408\u540c',
1493
+ '\u5408\u540c\u540d\u79f0\u4e2d\u6587',
1494
+ ])) return '350';
1495
+
1496
+ if (includesAnyLabel(label, ['\u91d1\u989d'])) return '180';
1497
+ if (includesAnyLabel(label, ['\u6570\u91cf', '\u5355\u4ef7', '\u6570\u989d'])) return '140';
1498
+ if (label.endsWith('\u4eba')) return '90';
1499
+ if (includesAnyLabel(label, ['\u7535\u8bdd'])) return '100';
1500
+
1501
+ if (field.formType === 'datetime') return '150';
1502
+ if (field.formType === 'date') return visibleLength > 7 ? '170' : '90';
1503
+ if (field.formType === 'select') return visibleLength > 5 ? '200' : '100';
1504
+
1505
+ return '100';
1499
1506
  }
1500
1507
  function renderOptionFieldV2(field, labelKey, dictRegistryRefs, indent = ' ') {
1501
1508
  const fallbackLabel = stripDictAnnotation(field.comment).replace(/'/g, "\\'");
@@ -1547,16 +1554,17 @@ function renderDisabledAttrV2(field) {
1547
1554
  return field.readonly ? ' disabled' : '';
1548
1555
  }
1549
1556
 
1550
- function isAttachmentLikeField(field) {
1551
- const fieldName = String(field?.fieldName || field?.attrName || '').toLowerCase();
1552
- const comment = String(field?.comment || field?.description || '').toLowerCase();
1553
- return fieldName.includes('attachment') || comment.includes('\u9644\u4ef6') || comment.includes('\u4e0a\u4f20');
1554
- }
1555
-
1556
- function renderFieldCommentV2(field, indent = ' ') {
1557
- const label = stripDictAnnotation(field.comment || field.attrName).replace(/-->/g, '').trim() || field.attrName;
1558
- return indent + '<!-- \u5b57\u6bb5\uFF1A' + label + ' -->';
1557
+ function isAttachmentLikeField(field) {
1558
+ const fieldName = String(field?.fieldName || field?.attrName || '').toLowerCase();
1559
+ const comment = String(field?.comment || field?.description || '').toLowerCase();
1560
+ return fieldName.includes('attachment') || comment.includes('\u9644\u4ef6') || comment.includes('\u4e0a\u4f20');
1561
+ }
1562
+
1563
+ function renderFieldCommentV2(field, indent = ' ') {
1564
+ const label = stripDictAnnotation(field.comment || field.attrName).replace(/-->/g, '').trim() || field.attrName;
1565
+ return indent + '<!-- 字段:' + label + ' -->';
1559
1566
  }
1567
+
1560
1568
  function renderFormFieldV2(field) {
1561
1569
  const prop = field.attrName;
1562
1570
  const labelExpr = `getMasterFieldLabel('${prop}')`;
@@ -1569,7 +1577,7 @@ function renderFormFieldV2(field) {
1569
1577
  ` <el-col :span="12" class="mb20">`,
1570
1578
  ` <el-form-item :label="${labelExpr}" prop="${prop}">`,
1571
1579
  ` <el-select v-model="form.${prop}" :placeholder="selectPlaceholder(${labelExpr})" style="width: 100%"${disabledAttr}>`,
1572
- ` <el-option v-for="item in getDictOptions(${dictExpr})" :key="item.value" :label="item.label" :value="Number(item.value)" />`,
1580
+ ` <el-option v-for="item in getDictOptions(${dictExpr})" :key="item.value" :label="item.label" :value="item.value" />`,
1573
1581
  ' </el-select>',
1574
1582
  ' </el-form-item>',
1575
1583
  ' </el-col>',
@@ -1625,281 +1633,150 @@ function renderFormFieldV2(field) {
1625
1633
  ].join('\n');
1626
1634
  }
1627
1635
 
1628
- function renderTableColumnV2(field, dictRegistryRefs) {
1629
- const label = stripDictAnnotation(field.comment).replace(/'/g, "\\'");
1630
- const parts = [`prop: '${field.attrName}'`, `label: '${label}'`];
1636
+ function renderMultiLevelOptionField(field, model, moduleModel, dictRegistryRefs, indent = ' ') {
1637
+ const parts = [
1638
+ `key: '${field.attrName}'`,
1639
+ `labelKey: '${buildMultiLevelFieldLabelKey(model, moduleModel, field)}'`,
1640
+ ];
1631
1641
  const width = getDefaultOptionFieldWidthV2(field);
1632
-
1633
- if (width !== '120') {
1634
- parts.push(`width: '${width}'`);
1635
- }
1636
-
1637
- if (field.dictType) {
1638
- parts.push(`dictType: ${getDictRegistryReference(field.dictType, dictRegistryRefs)}`);
1639
- }
1640
-
1641
- return ` { ${parts.join(', ')} },`;
1642
- }
1643
-
1644
- function renderChildTableColumnV2(field, childListName) {
1645
- const rules = field.notNull ? ` :rules="[{ required: true, trigger: 'blur' }]"` : '';
1646
- const labelExpr = `getChildFieldLabel('${childListName}', '${field.attrName}')`;
1647
- const dictExpr = `getChildFieldMeta('${childListName}', '${field.attrName}')?.dictType`;
1648
- const disabledAttr = renderDisabledAttrV2(field);
1649
-
1650
- let control = ` <el-input v-model="row.${field.attrName}" :placeholder="inputPlaceholder(${labelExpr})"${renderTextMaxlengthAttrV2(field)}${disabledAttr} />`;
1651
- if (field.formType === 'select' && field.dictType) {
1652
- control = [
1653
- ` <el-select v-model="row.${field.attrName}" :placeholder="selectPlaceholder(${labelExpr})" style="width: 100%"${disabledAttr}>`,
1654
- ` <el-option v-for="item in getDictOptions(${dictExpr})" :key="item.value" :label="item.label" :value="Number(item.value)" />`,
1655
- ' </el-select>',
1656
- ].join('\n');
1657
- } else if (field.formType === 'number') {
1658
- const max = field.comment.includes('%') || /\u6BD4\u4F8B/.test(field.comment) ? ' :max="100"' : '';
1659
- const precision = field.sqlType === 'DECIMAL' && field.scale ? ` :precision="${field.scale}" :step="0.01"` : '';
1660
- control = ` <el-input-number v-model="row.${field.attrName}" :min="0"${max}${precision} style="width: 100%"${disabledAttr} />`;
1661
- } else if (field.formType === 'datetime' || field.formType === 'date') {
1662
- const pickerType = field.formType === 'datetime' ? 'datetime' : 'date';
1663
- const formatName = field.formType === 'datetime' ? 'dateTimeStr' : 'dateStr';
1664
- control = ` <el-date-picker type="${pickerType}" v-model="row.${field.attrName}" :value-format="${formatName}" :placeholder="inputPlaceholder(${labelExpr})" style="width: 100%"${disabledAttr}></el-date-picker>`;
1665
- } else if (field.formType === 'textarea') {
1666
- control = ` <el-input type="textarea" v-model="row.${field.attrName}" :placeholder="inputPlaceholder(${labelExpr})"${renderTextareaMaxlengthAttrsV2(field)}${disabledAttr} />`;
1667
- }
1668
-
1669
- return [
1670
- renderFieldCommentV2(field, ' '),
1671
- ` <el-table-column :label="${labelExpr}" prop="${field.attrName}">`,
1672
- ' <template #default="{ row, $index }">',
1673
- ` <el-form-item :prop="\`${childListName}.\${$index}.${field.attrName}\`"${rules}>`,
1674
- control,
1675
- ' </el-form-item>',
1676
- ' </template>',
1677
- ' </el-table-column>',
1678
- ].join('\n');
1679
- }
1680
-
1681
- function renderChildSectionV2(childModel, childCount) {
1682
- const deleteExpression = `deleteChild(obj, '${childModel.pk.attrName}')`;
1683
-
1684
- return [
1685
- ' <el-col :span="24" class="mb20">',
1686
- ` <div class="mb10" style="font-weight: 600;">{{ childSectionTitle('${childModel.listName}') }}</div>`,
1687
- ` <sc-form-table v-model="form.${childModel.listName}" :addTemplate="childTemp${childModel.className}" @delete="(obj) => ${deleteExpression}" :placeholder="t('common.noData')">`,
1688
- childModel.visibleFields.map((field) => renderChildTableColumnV2(field, childModel.listName)).join('\n'),
1689
- ' </sc-form-table>',
1690
- ' </el-col>',
1691
- ].join('\n');
1692
- }
1693
-
1694
- function renderValidationRule(field) {
1695
- if (!field.notNull) return null;
1696
- const label = stripDictAnnotation(field.comment).replace(/'/g, "\\'");
1697
- return ` ${field.attrName}: [{ required: true, message: '${label}\u4E0D\u80FD\u4E3A\u7A7A', trigger: 'blur' }],`;
1642
+ if (width) parts.push(`width: '${width}'`);
1643
+ if (field.show === false) parts.push('show: false');
1644
+ if (field.listShow === false) parts.push('listShow: false');
1645
+ if (field.dictType) parts.push(`dictType: ${getDictRegistryReference(field.dictType, dictRegistryRefs)}`);
1646
+ return `${indent}{ ${parts.join(', ')} },`;
1698
1647
  }
1699
1648
 
1700
- function renderFormRules(visibleFields) {
1701
- const lines = visibleFields.map(renderValidationRule).filter(Boolean);
1649
+ function renderMultiLevelModuleDefinition(model, moduleModel, dictRegistryRefs) {
1650
+ const lines = [
1651
+ ` ${moduleModel.key}: {`,
1652
+ ` key: '${moduleModel.key}',`,
1653
+ ` titleKey: '${buildMultiLevelModuleTitleKey(model, moduleModel)}',`,
1654
+ ` apiPath: '${moduleModel.apiPath}',`,
1655
+ ` primaryKey: '${moduleModel.pk.attrName}',`,
1656
+ ];
1657
+ if (moduleModel.queryParentField) lines.push(` queryParentField: '${moduleModel.queryParentField}',`);
1658
+ if (moduleModel.statusField) lines.push(` statusField: '${moduleModel.statusField}',`);
1659
+ if (moduleModel.statusDictType) lines.push(` statusDictType: ${getDictRegistryReference(moduleModel.statusDictType, dictRegistryRefs)},`);
1660
+ lines.push(` enableApi: '${moduleModel.enableApi.startsWith('/') ? moduleModel.enableApi : '/' + moduleModel.enableApi}',`);
1661
+ lines.push(` disableApi: '${moduleModel.disableApi.startsWith('/') ? moduleModel.disableApi : '/' + moduleModel.disableApi}',`);
1662
+ lines.push(' fields: [');
1663
+ lines.push(moduleModel.optionFields.map((field) => renderMultiLevelOptionField(field, model, moduleModel, dictRegistryRefs)).join('\n'));
1664
+ lines.push(' ],');
1665
+ lines.push(' },');
1702
1666
  return lines.join('\n');
1703
1667
  }
1704
1668
 
1705
- function renderFormRulesV2(visibleFields) {
1706
- const lines = visibleFields
1707
- .filter((field) => field.notNull)
1708
- .map((field) => ` ${field.attrName}: [{ required: true, message: fieldRequiredMessage('${field.attrName}'), trigger: 'blur' }],`);
1709
- return lines.join('\n');
1669
+ function renderMultiLevelLevelConfig(level) {
1670
+ return ` { levelIndex: ${level.levelIndex}, position: '${level.position}', moduleKeys: [${level.modules.map((moduleModel) => `'${moduleModel.key}'`).join(', ')}] },`;
1710
1671
  }
1711
1672
 
1712
- function toKebabCase(value) {
1713
- return String(value || '')
1714
- .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
1715
- .replace(/_/g, '-')
1716
- .toLowerCase();
1717
- }
1718
-
1719
- function renderMultiLevelFieldConfig(model, moduleModel, field, dictRegistryRefs, indent = ' ') {
1720
- const parts = [
1721
- `key: '${field.attrName}'`,
1722
- `labelKey: '${buildMultiLevelFieldLabelKey(model, moduleModel, field)}'`,
1723
- ];
1724
- const width = getDefaultOptionFieldWidthV2(field);
1725
- if (width !== '120') parts.push(`width: '${width}'`);
1726
- if (field.show === false) parts.push('show: false');
1727
- if (field.listShow === false) parts.push('listShow: false');
1728
- if (field.dictType) parts.push(`dictType: ${getDictRegistryReference(field.dictType, dictRegistryRefs)}`);
1729
- return `${indent}{ ${parts.join(', ')} },`;
1730
- }
1731
-
1732
1673
  function renderMultiLevelOptionsTs(model, dictRegistryRefs) {
1733
- const dictImportBlock = model.dictTypes.length ? "import { DictRegistry } from '/@/enums/dict-registry';\n" : '';
1734
- const moduleConfigsBody = model.modules
1735
- .map((moduleModel) => {
1736
- const dictTypes = [...new Set(moduleModel.optionFields.map((field) => field.dictType).filter(Boolean))];
1737
- return [
1738
- ` ${moduleModel.key}: {`,
1739
- ` key: '${moduleModel.key}',`,
1740
- ` titleKey: '${buildMultiLevelModuleTitleKey(model, moduleModel)}',`,
1741
- ` apiPath: '${moduleModel.apiPath}',`,
1742
- ` primaryKey: '${moduleModel.pk.attrName}',`,
1743
- ...(moduleModel.queryParentField ? [` queryParentField: '${moduleModel.queryParentField}',`] : []),
1744
- ...(moduleModel.statusField ? [` statusField: '${moduleModel.statusField}',`] : []),
1745
- ...(moduleModel.statusDictType ? [` statusDictType: ${getDictRegistryReference(moduleModel.statusDictType, dictRegistryRefs)},`] : []),
1746
- ` enableApi: '/${moduleModel.enableApi}',`,
1747
- ` disableApi: '/${moduleModel.disableApi}',`,
1748
- ` dictTypes: [${dictTypes.map((item) => getDictRegistryReference(item, dictRegistryRefs)).join(', ')}],`,
1749
- ' fields: [',
1750
- moduleModel.optionFields.map((field) => renderMultiLevelFieldConfig(model, moduleModel, field, dictRegistryRefs, ' ')).join('\n'),
1751
- ' ],',
1752
- ' },',
1753
- ].join('\n');
1754
- })
1755
- .join('\n');
1756
-
1757
- const levelConfigsBody = model.levels
1758
- .map(
1759
- (level) =>
1760
- ` { levelIndex: ${level.levelIndex}, position: '${level.position}', moduleKeys: [${level.modules.map((moduleModel) => `'${moduleModel.key}'`).join(', ')}] },`
1761
- )
1762
- .join('\n');
1763
-
1764
- return `import type { FieldMeta } from '/@/utils/crudSchema';\n${dictImportBlock}
1765
- export interface MultiLevelDictFieldConfig {
1766
- key: string;
1767
- labelKey: string;
1768
- width?: string;
1769
- show?: boolean;
1770
- listShow?: boolean;
1771
- dictType?: string;
1772
- }
1773
-
1774
- export interface MultiLevelDictModuleDefinition {
1775
- key: string;
1776
- titleKey: string;
1777
- apiPath: string;
1778
- primaryKey: string;
1779
- queryParentField?: string;
1780
- statusField?: string;
1781
- statusDictType?: string;
1782
- enableApi: string;
1783
- disableApi: string;
1784
- fields: MultiLevelDictFieldConfig[];
1785
- }
1786
-
1787
- export interface MultiLevelDictModuleConfig extends MultiLevelDictModuleDefinition {
1788
- dictTypes: string[];
1789
- }
1790
-
1791
- export interface MultiLevelDictLevelConfig {
1792
- levelIndex: number;
1793
- position: 'left' | 'right' | 'right-top' | 'right-bottom';
1794
- moduleKeys: string[];
1795
- }
1796
-
1797
- const toFieldMetaMap = (fields: MultiLevelDictFieldConfig[]) =>
1798
- Object.fromEntries(
1799
- fields.map((item) => [
1800
- item.key,
1801
- {
1802
- show: item.show ?? true,
1803
- listShow: item.listShow ?? item.show ?? true,
1804
- alwaysHide: false,
1805
- smart: false,
1806
- labelKey: item.labelKey,
1807
- width: item.width ?? '120',
1808
- ...(item.dictType ? { dictType: item.dictType } : {}),
1809
- } satisfies FieldMeta,
1810
- ])
1811
- ) as Record<string, FieldMeta>;
1812
-
1813
- const createModuleConfig = (definition: MultiLevelDictModuleDefinition): MultiLevelDictModuleConfig => ({
1814
- ...definition,
1815
- dictTypes: Array.from(
1816
- new Set([...(definition.fields.map((field) => field.dictType).filter(Boolean) as string[]), definition.statusDictType].filter(Boolean))
1817
- ),
1818
- });
1819
-
1820
- const moduleDefinitions: Record<string, MultiLevelDictModuleDefinition> = {
1821
- ${moduleConfigsBody}
1822
- };
1823
-
1824
- export const moduleConfigs = Object.fromEntries(
1825
- Object.entries(moduleDefinitions).map(([key, definition]) => [key, createModuleConfig(definition)])
1826
- ) as Record<string, MultiLevelDictModuleConfig>;
1827
-
1828
- export const levelConfigs: MultiLevelDictLevelConfig[] = [
1829
- ${levelConfigsBody}
1830
- ];
1831
-
1832
- export const moduleFieldGroups = Object.fromEntries(
1833
- Object.entries(moduleConfigs).map(([key, config]) => [key, toFieldMetaMap(config.fields)])
1834
- ) as Record<string, Record<string, FieldMeta>>;
1835
-
1836
- export const allDictTypes = Array.from(new Set(Object.values(moduleConfigs).flatMap((config) => config.dictTypes)));
1837
- `;
1838
- }
1674
+ return [
1675
+ "import type { FieldMeta } from '/@/utils/crudSchema';",
1676
+ "import type { MultiLevelDictFieldConfig, MultiLevelDictLevelConfig, MultiLevelDictModuleConfig, MultiLevelDictModuleDefinition } from '/@/types/multiLevelDict';",
1677
+ "import { DictRegistry } from '/@/enums/dict-registry';",
1678
+ '',
1679
+ 'const toFieldMetaMap = (fields: MultiLevelDictFieldConfig[]) =>',
1680
+ ' Object.fromEntries(',
1681
+ ' fields.map((item) => [',
1682
+ ' item.key,',
1683
+ ' {',
1684
+ ' show: item.show ?? true,',
1685
+ ' listShow: item.listShow ?? item.show ?? true,',
1686
+ ' alwaysHide: false,',
1687
+ ' smart: false,',
1688
+ ' labelKey: item.labelKey,',
1689
+ " width: item.width ?? '120',",
1690
+ " ...(item.dictType ? { dictType: item.dictType } : {}),",
1691
+ ' } satisfies FieldMeta,',
1692
+ ' ])',
1693
+ ' ) as Record<string, FieldMeta>;',
1694
+ '',
1695
+ 'const isString = (value: string | undefined): value is string => Boolean(value);',
1696
+ '',
1697
+ 'const createModuleConfig = (definition: MultiLevelDictModuleDefinition): MultiLevelDictModuleConfig => ({',
1698
+ ' ...definition,',
1699
+ ' dictTypes: Array.from(new Set([...definition.fields.map((field) => field.dictType).filter(isString), definition.statusDictType].filter(isString))),',
1700
+ '});',
1701
+ '',
1702
+ 'const moduleDefinitions: Record<string, MultiLevelDictModuleDefinition> = {',
1703
+ model.modules.map((moduleModel) => renderMultiLevelModuleDefinition(model, moduleModel, dictRegistryRefs)).join('\n'),
1704
+ '};',
1705
+ '',
1706
+ 'export const moduleConfigs = Object.fromEntries(',
1707
+ ' Object.entries(moduleDefinitions).map(([key, definition]) => [key, createModuleConfig(definition)])',
1708
+ ') as Record<string, MultiLevelDictModuleConfig>;',
1709
+ '',
1710
+ 'export const levelConfigs: MultiLevelDictLevelConfig[] = [',
1711
+ model.levels.map(renderMultiLevelLevelConfig).join('\n'),
1712
+ '];',
1713
+ '',
1714
+ 'export const moduleFieldGroups = Object.fromEntries(',
1715
+ ' Object.entries(moduleConfigs).map(([key, config]) => [key, toFieldMetaMap(config.fields)])',
1716
+ ') as Record<string, Record<string, FieldMeta>>;',
1717
+ '',
1718
+ 'export const allDictTypes = Array.from(new Set(Object.values(moduleConfigs).flatMap((config) => config.dictTypes)));',
1719
+ '',
1720
+ ].join('\n');
1721
+ }
1839
1722
 
1840
1723
  function renderMultiLevelApiFunctions(moduleModel) {
1841
- const entityVar = moduleModel.entityName;
1842
- const className = moduleModel.className;
1724
+ const pkAttr = moduleModel.pk.attrName;
1725
+ const basePath = moduleModel.apiPath.startsWith('/') ? moduleModel.apiPath : '/' + moduleModel.apiPath;
1726
+ const enablePath = moduleModel.enableApi.startsWith('/') ? moduleModel.enableApi : '/' + moduleModel.enableApi;
1727
+ const disablePath = moduleModel.disableApi.startsWith('/') ? moduleModel.disableApi : '/' + moduleModel.disableApi;
1843
1728
  return [
1844
- `export function fetch${className}List(query: any) {`,
1729
+ `export function fetch${moduleModel.className}List(query?: any) {`,
1845
1730
  ' return request({',
1846
- ` url: '/${moduleModel.apiPath}/page',`,
1731
+ ` url: '${basePath}/page',`,
1847
1732
  " method: 'get',",
1848
- ' params: {',
1849
- ' current: query?.current,',
1850
- ' size: query?.size,',
1851
- ` ${entityVar}: {`,
1852
- ...(moduleModel.queryParentField
1853
- ? [` ...(query?.${moduleModel.queryParentField} !== undefined && query?.${moduleModel.queryParentField} !== null && query?.${moduleModel.queryParentField} !== '' ? { ${moduleModel.queryParentField}: query.${moduleModel.queryParentField} } : {}),`]
1854
- : []),
1855
- ' },',
1856
- ' },',
1733
+ ' params: query,',
1857
1734
  ' });',
1858
1735
  '}',
1859
1736
  '',
1860
- `export function get${className}Obj(id: string | number) {`,
1737
+ `export function get${moduleModel.className}Obj(id: string | number) {`,
1861
1738
  ' return request({',
1862
- ` url: '/${moduleModel.apiPath}/getById',`,
1739
+ ` url: '${basePath}/getById',`,
1863
1740
  " method: 'get',",
1864
- ' params: { id },',
1741
+ ` params: { ${pkAttr}: id },`,
1865
1742
  ' });',
1866
1743
  '}',
1867
1744
  '',
1868
- `export function add${className}Obj(obj: any) {`,
1745
+ `export function add${moduleModel.className}Obj(data: any) {`,
1869
1746
  ' return request({',
1870
- ` url: '/${moduleModel.apiPath}/save',`,
1747
+ ` url: '${basePath}/save',`,
1871
1748
  " method: 'post',",
1872
- ' data: obj,',
1749
+ ' data,',
1873
1750
  ' });',
1874
1751
  '}',
1875
1752
  '',
1876
- `export function put${className}Obj(obj: any) {`,
1753
+ `export function put${moduleModel.className}Obj(data: any) {`,
1877
1754
  ' return request({',
1878
- ` url: '/${moduleModel.apiPath}/updateById',`,
1755
+ ` url: '${basePath}/updateById',`,
1879
1756
  " method: 'post',",
1880
- ' data: obj,',
1757
+ ' data,',
1881
1758
  ' });',
1882
1759
  '}',
1883
1760
  '',
1884
- `export function del${className}Objs(ids: Array<string | number>) {`,
1761
+ `export function del${moduleModel.className}Objs(data: Array<string | number>) {`,
1885
1762
  ' return request({',
1886
- ` url: '/${moduleModel.apiPath}/removeByIds',`,
1763
+ ` url: '${basePath}/removeByIds',`,
1887
1764
  " method: 'post',",
1888
- ' data: ids,',
1765
+ ' data,',
1889
1766
  ' });',
1890
1767
  '}',
1891
1768
  '',
1892
- `export function enable${className}(id: string | number) {`,
1769
+ `export function enable${moduleModel.className}(id: string | number) {`,
1893
1770
  ' return request({',
1894
- ` url: '${moduleModel.enableApi.startsWith('/') ? moduleModel.enableApi : '/' + moduleModel.enableApi}',`,
1771
+ ` url: '${enablePath}',`,
1895
1772
  " method: 'post',",
1896
1773
  ' params: { id },',
1897
1774
  ' });',
1898
1775
  '}',
1899
1776
  '',
1900
- `export function disable${className}(id: string | number) {`,
1777
+ `export function disable${moduleModel.className}(id: string | number) {`,
1901
1778
  ' return request({',
1902
- ` url: '${moduleModel.disableApi.startsWith('/') ? moduleModel.disableApi : '/' + moduleModel.disableApi}',`,
1779
+ ` url: '${disablePath}',`,
1903
1780
  " method: 'post',",
1904
1781
  ' params: { id },',
1905
1782
  ' });',
@@ -1938,7 +1815,7 @@ function renderMultiLevelFormField(field) {
1938
1815
  ' <el-col :span="12" class="mb20">',
1939
1816
  ` <el-form-item :label="${labelExpr}" prop="${prop}">`,
1940
1817
  ` <el-select v-model="form.${prop}" :placeholder="selectPlaceholder(${labelExpr})" style="width: 100%"${disabledAttr}>`,
1941
- ` <el-option v-for="item in getDictOptions(getFieldMeta('${prop}')?.dictType)" :key="item.value" :label="item.label" :value="Number(item.value)" />`,
1818
+ ` <el-option v-for="item in getDictOptions(getFieldMeta('${prop}')?.dictType)" :key="item.value" :label="item.label" :value="item.value" />`,
1942
1819
  ' </el-select>',
1943
1820
  ' </el-form-item>',
1944
1821
  ' </el-col>',
@@ -2042,6 +1919,13 @@ ${defaultLines}
2042
1919
 
2043
1920
  const form = reactive(createDefaultFormState());
2044
1921
 
1922
+ const setParentFieldValue = (parentId: string | number) => {
1923
+ const parentField = moduleConfig.queryParentField as keyof typeof form | undefined;
1924
+ if (!parentField) return;
1925
+ const currentValue = form[parentField];
1926
+ (form as Record<string, string | number>)[parentField as string] = typeof currentValue === 'number' ? Number(parentId) : String(parentId);
1927
+ };
1928
+
2045
1929
  const dataRules = ref({
2046
1930
  ${rules}
2047
1931
  });
@@ -2069,10 +1953,10 @@ const openDialog = async (id?: string | number, parentId?: string | number) => {
2069
1953
  visible.value = true;
2070
1954
  resetFormState();
2071
1955
  if (moduleConfig.queryParentField && parentId !== undefined && parentId !== null && parentId !== '') {
2072
- form[moduleConfig.queryParentField] = parentId;
1956
+ setParentFieldValue(parentId);
2073
1957
  }
2074
1958
  if (id) {
2075
- form.${moduleModel.pk.attrName} = id;
1959
+ form.${moduleModel.pk.attrName} = String(id);
2076
1960
  await getData(id);
2077
1961
  }
2078
1962
  };
@@ -2103,7 +1987,6 @@ defineExpose({
2103
1987
  </script>
2104
1988
  `;
2105
1989
  }
2106
-
2107
1990
  function renderMultiLevelPanelVue() {
2108
1991
  return `<template>
2109
1992
  <el-container class="layout-padding-auto layout-padding-view dict-page-body">
@@ -2118,7 +2001,7 @@ function renderMultiLevelPanelVue() {
2118
2001
  v-for="column in columns"
2119
2002
  :key="column.key"
2120
2003
  :prop="column.key"
2121
- :label="resolveLabel(column.labelKey, column.label)"
2004
+ :label="resolveLabel(column.labelKey, column.key || '')"
2122
2005
  :min-width="column.width || '120'"
2123
2006
  show-overflow-tooltip
2124
2007
  >
@@ -2131,8 +2014,8 @@ function renderMultiLevelPanelVue() {
2131
2014
  <template #default="scope">
2132
2015
  <el-button icon="edit-pen" text type="primary" @click="$emit('edit', scope.row)">{{ t('common.editBtn') }}</el-button>
2133
2016
  <el-button icon="delete" text type="primary" @click="$emit('delete', scope.row)">{{ t('common.delBtn') }}</el-button>
2134
- <el-button v-if="statusField && !isEnabled(scope.row[statusField])" icon="circle-check" text type="primary" @click="$emit('enable', scope.row)">闂佸憡鍑归崹鎶藉极?/el-button>
2135
- <el-button v-if="statusField && isEnabled(scope.row[statusField])" icon="remove" text type="primary" @click="$emit('disable', scope.row)">缂備礁鍊烽懗鍫曞极?/el-button>
2017
+ <el-button v-if="statusField && !isEnabled(scope.row[statusField])" icon="circle-check" text type="primary" @click="$emit('enable', scope.row)">闂備礁鎲¢崙褰掑垂閹惰棄鏋?/el-button>
2018
+ <el-button v-if="statusField && isEnabled(scope.row[statusField])" icon="remove" text type="primary" @click="$emit('disable', scope.row)">缂傚倷绀侀崐鐑芥嚄閸洖鏋?/el-button>
2136
2019
  </template>
2137
2020
  </el-table-column>
2138
2021
  </el-table>
@@ -2194,36 +2077,36 @@ const handleCurrentChange = (row: any) => {
2194
2077
  `;
2195
2078
  }
2196
2079
 
2197
- function renderMultiLevelLevelSlot(levelVarName, activeKeyVarName, activeModuleVarName, levelLabel) {
2198
- return [
2199
- ` <div class="multi-level-slot" v-if="${levelVarName}">`,
2200
- ' <el-tabs class="multi-level-tabs" v-if="' + `${levelVarName}.moduleKeys.length > 1` + `" v-model="${activeKeyVarName}">`,
2201
- ` <el-tab-pane v-for="moduleKey in ${levelVarName}.moduleKeys" :key="moduleKey" :label="resolveModuleTitle(moduleConfigs[moduleKey])" :name="moduleKey" />`,
2202
- ' </el-tabs>',
2203
- ' <div class="multi-level-panel" v-if="' + `${activeModuleVarName}` + `">`,
2204
- ` <LevelPanel`,
2205
- ` :columns="getListFields(${activeModuleVarName}.key)"`,
2206
- ` :dict-types="${activeModuleVarName}.dictTypes"`,
2207
- ` :data-list="moduleStateMap[${activeModuleVarName}.key].dataList"`,
2208
- ` :loading="moduleStateMap[${activeModuleVarName}.key].loading"`,
2209
- ` :add-disabled="isAddDisabled(${activeModuleVarName}.key)"`,
2210
- ` :status-field="${activeModuleVarName}.statusField"`,
2211
- ` :current-page="moduleStateMap[${activeModuleVarName}.key].currentPage"`,
2212
- ` :page-size="moduleStateMap[${activeModuleVarName}.key].pageSize"`,
2213
- ` :total="moduleStateMap[${activeModuleVarName}.key].total"`,
2214
- ` @add="openCreate(${activeModuleVarName}.key)"`,
2215
- ` @edit="openEdit(${activeModuleVarName}.key, $event)"`,
2216
- ` @delete="handleDelete(${activeModuleVarName}.key, $event)"`,
2217
- ` @enable="handleEnable(${activeModuleVarName}.key, $event)"`,
2218
- ` @disable="handleDisable(${activeModuleVarName}.key, $event)"`,
2219
- ` @current-change="handleCurrentPageChange(${activeModuleVarName}.key, $event)"`,
2220
- ` @size-change="handlePageSizeChange(${activeModuleVarName}.key, $event)"`,
2221
- ` @select-row="handleSelectRow(${activeModuleVarName}.key, $event)"`,
2222
- ' />',
2223
- ' </div>',
2224
- ` </div>`,
2225
- ].join('\n');
2226
- }
2080
+ function renderMultiLevelLevelSlot(levelVarName, activeKeyVarName, activeModuleVarName) {
2081
+ return [
2082
+ ` <div class="multi-level-slot" v-if="${levelVarName}">`,
2083
+ ' <el-tabs class="multi-level-tabs" v-if="' + `${levelVarName}.moduleKeys.length > 1` + `" v-model="${activeKeyVarName}">`,
2084
+ ` <el-tab-pane v-for="moduleKey in ${levelVarName}.moduleKeys" :key="moduleKey" :label="resolveModuleTitle(moduleConfigs[moduleKey])" :name="moduleKey" />`,
2085
+ ' </el-tabs>',
2086
+ ' <div class="multi-level-panel" v-if="' + `${activeModuleVarName}` + `">`,
2087
+ ` <LevelPanel`,
2088
+ ` :columns="getListFields(${activeModuleVarName}.key)"`,
2089
+ ` :dict-types="${activeModuleVarName}.dictTypes"`,
2090
+ ` :data-list="moduleStateMap[${activeModuleVarName}.key].dataList"`,
2091
+ ` :loading="moduleStateMap[${activeModuleVarName}.key].loading"`,
2092
+ ` :add-disabled="isAddDisabled(${activeModuleVarName}.key)"`,
2093
+ ` :status-field="${activeModuleVarName}.statusField"`,
2094
+ ` :current-page="moduleStateMap[${activeModuleVarName}.key].currentPage"`,
2095
+ ` :page-size="moduleStateMap[${activeModuleVarName}.key].pageSize"`,
2096
+ ` :total="moduleStateMap[${activeModuleVarName}.key].total"`,
2097
+ ` @add="openCreate(${activeModuleVarName}.key)"`,
2098
+ ` @edit="openEdit(${activeModuleVarName}.key, $event)"`,
2099
+ ` @delete="handleDelete(${activeModuleVarName}.key, $event)"`,
2100
+ ` @enable="handleEnable(${activeModuleVarName}.key, $event)"`,
2101
+ ` @disable="handleDisable(${activeModuleVarName}.key, $event)"`,
2102
+ ` @current-change="handleCurrentPageChange(${activeModuleVarName}.key, $event)"`,
2103
+ ` @size-change="handlePageSizeChange(${activeModuleVarName}.key, $event)"`,
2104
+ ` @select-row="handleSelectRow(${activeModuleVarName}.key, $event)"`,
2105
+ ' />',
2106
+ ' </div>',
2107
+ ` </div>`,
2108
+ ].join('\n');
2109
+ }
2227
2110
 
2228
2111
  function renderMultiLevelIndexVue(model) {
2229
2112
  const level2 = model.levels.find((level) => level.levelIndex === 2);
@@ -2261,17 +2144,17 @@ function renderMultiLevelIndexVue(model) {
2261
2144
 
2262
2145
  return `<template>
2263
2146
  <div class="layout-padding">
2264
- <!-- 闂傚倸鍊搁崐鎼佸磹閹间礁纾瑰瀣捣閻棗銆掑锝呬壕濡ょ姷鍋涢ˇ鐢稿垂妤e啫绠涘ù锝呮贡缁嬩礁鈹戦悩鍨毄濠殿喚鏁婚幊婵嬪礈瑜忛悳缁樹繆閵堝懏鍣洪柣鎾寸洴閺屾盯濡烽敐鍛闂佽绻嗛弲婵堟閹烘鐒垫い鎺戝绾惧吋绻涢幋鐐垫噧鐎殿喖鐏濋埞鎴︽晬閸曨偂鍝楀銈嗘肠閸曨収娲告俊銈忕到閸燁垶宕愰悽鍛婂仭婵炲棗绻愰顏嗙棯閻愵剚鍊愰柡灞剧⊕閹棃濡搁妶鍡楁灓婵$偑鍊戦崹楦挎懌缂備浇娅i弲顐ゅ垝濞嗘垶宕夐柕濞垮劗閸嬫捇鎮欓悜妯锋嫼缂備緡鍨卞ú鏍ㄦ櫠閺屻儲鐓熼柣鏃€娼欓崝婊呯磼閸屾稑娴柡灞芥椤撳ジ宕卞▎蹇撶?{model.featureTitle.replace(/--/g, '')} -->
2147
+ <!-- 闂備礁鎲″濠氬窗閺囥垹绀傛慨妞诲亾鐎规洏鍎遍濂稿炊閸℃瑥浠遍柡?{model.featureTitle.replace(/--/g, '')} -->
2265
2148
  <div class="multi-level-dict-layout">
2266
2149
  <div class="multi-level-left">
2267
- ${renderMultiLevelLevelSlot('level1Config', 'activeLevel1Key', 'activeLevel1Module', 'left')}
2150
+ ${renderMultiLevelLevelSlot('level1Config', 'activeLevel1Key', 'activeLevel1Module')}
2268
2151
  </div>
2269
2152
  <div class="multi-level-right">
2270
2153
  <div class="multi-level-right-top">
2271
- ${level2 ? renderMultiLevelLevelSlot('level2Config', 'activeLevel2Key', 'activeLevel2Module', 'right-top') : ''}
2154
+ ${level2 ? renderMultiLevelLevelSlot('level2Config', 'activeLevel2Key', 'activeLevel2Module') : ''}
2272
2155
  </div>
2273
2156
  <div v-if="level3Config" class="multi-level-right-bottom">
2274
- ${level3 ? renderMultiLevelLevelSlot('level3Config', 'activeLevel3Key', 'activeLevel3Module', 'right-bottom') : ''}
2157
+ ${level3 ? renderMultiLevelLevelSlot('level3Config', 'activeLevel3Key', 'activeLevel3Module') : ''}
2275
2158
  </div>
2276
2159
  </div>
2277
2160
  </div>
@@ -2282,8 +2165,10 @@ ${formComponents}
2282
2165
 
2283
2166
  <script setup lang="ts" name="system${model.className}">
2284
2167
  import { useMessage, useMessageBox } from '/@/hooks/message';
2168
+ import { useMultiLevelDictMeta } from '/@/hooks/useMultiLevelDictMeta';
2169
+ import { useMultiLevelDictPage } from '/@/hooks/useMultiLevelDictPage';
2285
2170
  import { useI18n } from 'vue-i18n';
2286
- import LevelPanel from '/@/components/DictLevelPanel/index.vue';
2171
+ import LevelPanel from '/@/components/DictLevelPanel/index.vue';
2287
2172
  import { levelConfigs, moduleConfigs } from './options';
2288
2173
  import { ${apiImports} } from '/@/api/${model.moduleName}/${model.functionName}';
2289
2174
 
@@ -2295,33 +2180,18 @@ ${apiHandlerMap}
2295
2180
 
2296
2181
  const { t } = useI18n();
2297
2182
 
2298
- const moduleStateMap = reactive(
2299
- Object.fromEntries(
2300
- Object.keys(moduleConfigs).map((key) => [
2301
- key,
2302
- {
2303
- dataList: [],
2304
- loading: false,
2305
- currentPage: 1,
2306
- pageSize: 10,
2307
- total: 0,
2308
- currentRow: null,
2309
- },
2310
- ])
2311
- ) as Record<string, any>
2312
- );
2313
-
2314
- const level1Config = levelConfigs.find((item) => item.levelIndex === 1) || null;
2315
- const level2Config = levelConfigs.find((item) => item.levelIndex === 2) || null;
2316
- const level3Config = levelConfigs.find((item) => item.levelIndex === 3) || null;
2317
-
2318
- const activeLevel1Key = ref(level1Config?.moduleKeys?.[0] || '');
2319
- const activeLevel2Key = ref(level2Config?.moduleKeys?.[0] || '');
2320
- const activeLevel3Key = ref(level3Config?.moduleKeys?.[0] || '');
2321
-
2322
- const activeLevel1Module = computed(() => (activeLevel1Key.value ? moduleConfigs[activeLevel1Key.value] : null));
2323
- const activeLevel2Module = computed(() => (activeLevel2Key.value ? moduleConfigs[activeLevel2Key.value] : null));
2324
- const activeLevel3Module = computed(() => (activeLevel3Key.value ? moduleConfigs[activeLevel3Key.value] : null));
2183
+ const {
2184
+ level1Config,
2185
+ level2Config,
2186
+ level3Config,
2187
+ activeLevel1Key,
2188
+ activeLevel2Key,
2189
+ activeLevel3Key,
2190
+ activeLevel1Module,
2191
+ activeLevel2Module,
2192
+ activeLevel3Module,
2193
+ getModuleLevel,
2194
+ } = useMultiLevelDictMeta(levelConfigs, moduleConfigs);
2325
2195
 
2326
2196
  const resolveLabel = (labelKey?: string, fallback = '') => {
2327
2197
  if (!labelKey) return fallback;
@@ -2329,101 +2199,38 @@ const resolveLabel = (labelKey?: string, fallback = '') => {
2329
2199
  return translated === labelKey ? fallback : translated;
2330
2200
  };
2331
2201
 
2332
- const resolveModuleTitle = (moduleConfig: any) => resolveLabel(moduleConfig?.titleKey, moduleConfig?.title || moduleConfig?.key || '');
2333
-
2334
- const getModuleLevel = (moduleKey: string) => levelConfigs.find((level) => level.moduleKeys.includes(moduleKey))?.levelIndex || 1;
2335
-
2336
- const getParentIdForModule = (moduleKey: string) => {
2337
- const levelIndex = getModuleLevel(moduleKey);
2338
- if (levelIndex === 2 && activeLevel1Module.value) {
2339
- const parentRow = moduleStateMap[activeLevel1Module.value.key]?.currentRow;
2340
- return parentRow?.[activeLevel1Module.value.primaryKey] ?? '';
2341
- }
2342
- if (levelIndex === 3 && activeLevel2Module.value) {
2343
- const parentRow = moduleStateMap[activeLevel2Module.value.key]?.currentRow;
2344
- return parentRow?.[activeLevel2Module.value.primaryKey] ?? '';
2345
- }
2346
- return '';
2347
- };
2348
-
2349
- const getListFields = (moduleKey: string) => (moduleConfigs[moduleKey]?.fields || []).filter((field: any) => field.listShow !== false);
2350
-
2351
- const isAddDisabled = (moduleKey: string) => {
2352
- const moduleConfig = moduleConfigs[moduleKey];
2353
- return Boolean(moduleConfig?.queryParentField) && !getParentIdForModule(moduleKey);
2354
- };
2355
-
2356
- const resetDescendantStates = (levelIndex: number) => {
2357
- levelConfigs
2358
- .filter((level) => level.levelIndex > levelIndex)
2359
- .forEach((level) => {
2360
- level.moduleKeys.forEach((moduleKey) => {
2361
- Object.assign(moduleStateMap[moduleKey], {
2362
- dataList: [],
2363
- total: 0,
2364
- currentPage: 1,
2365
- currentRow: null,
2366
- });
2367
- });
2368
- });
2369
- };
2370
-
2371
- const loadModuleData = async (moduleKey: string, resetPage = false) => {
2372
- const moduleConfig = moduleConfigs[moduleKey];
2373
- const state = moduleStateMap[moduleKey];
2374
- if (!moduleConfig || !state) return;
2375
-
2376
- const parentId = getParentIdForModule(moduleKey);
2377
- if (moduleConfig.queryParentField && !parentId) {
2378
- Object.assign(state, { dataList: [], total: 0, currentRow: null });
2379
- return;
2380
- }
2381
-
2382
- if (resetPage) state.currentPage = 1;
2383
- state.loading = true;
2384
- try {
2385
- const query = {
2386
- current: state.currentPage,
2387
- size: state.pageSize,
2388
- ...(moduleConfig.queryParentField && parentId !== '' ? { [moduleConfig.queryParentField]: parentId } : {}),
2389
- };
2390
- const { data } = await apiHandlerMap[moduleKey].page(query);
2391
- const pageData = Array.isArray(data) ? { records: data, total: data.length, current: 1, size: data.length } : data || {};
2392
- state.dataList = pageData.records || pageData.list || [];
2393
- state.total = pageData.total || state.dataList.length;
2394
- } finally {
2395
- state.loading = false;
2396
- }
2397
- };
2398
-
2399
- const handleSelectRow = (moduleKey: string, row: any) => {
2400
- moduleStateMap[moduleKey].currentRow = row;
2401
- const levelIndex = getModuleLevel(moduleKey);
2402
- resetDescendantStates(levelIndex);
2403
- if (levelIndex === 1 && activeLevel2Module.value) loadModuleData(activeLevel2Module.value.key, true);
2404
- if (levelIndex === 2 && activeLevel3Module.value) loadModuleData(activeLevel3Module.value.key, true);
2405
- };
2406
-
2407
- const handleCurrentPageChange = (moduleKey: string, page: number) => {
2408
- moduleStateMap[moduleKey].currentPage = page;
2409
- loadModuleData(moduleKey);
2410
- };
2411
-
2412
- const handlePageSizeChange = (moduleKey: string, pageSize: number) => {
2413
- moduleStateMap[moduleKey].pageSize = pageSize;
2414
- moduleStateMap[moduleKey].currentPage = 1;
2415
- loadModuleData(moduleKey);
2416
- };
2202
+ const resolveModuleTitle = (moduleConfig: any) => resolveLabel(moduleConfig?.titleKey, moduleConfig?.key || '');
2203
+
2204
+ const {
2205
+ moduleStateMap,
2206
+ getParentIdForModule,
2207
+ getListFields,
2208
+ isAddDisabled,
2209
+ loadModuleData,
2210
+ handleSelectRow,
2211
+ handleCurrentPageChange,
2212
+ handlePageSizeChange,
2213
+ handleFormRefresh,
2214
+ } = useMultiLevelDictPage(
2215
+ levelConfigs,
2216
+ moduleConfigs,
2217
+ activeLevel1Key,
2218
+ activeLevel2Key,
2219
+ activeLevel3Key,
2220
+ activeLevel1Module,
2221
+ activeLevel2Module,
2222
+ activeLevel3Module,
2223
+ getModuleLevel,
2224
+ apiHandlerMap
2225
+ );
2417
2226
 
2418
2227
  const openCreate = (moduleKey: string) => {
2419
- const formRef = formRefMap[moduleKey];
2420
- formRef?.value?.openDialog(undefined, getParentIdForModule(moduleKey));
2228
+ formRefMap[moduleKey]?.value?.openDialog(undefined, getParentIdForModule(moduleKey));
2421
2229
  };
2422
2230
 
2423
2231
  const openEdit = (moduleKey: string, row: any) => {
2424
- const formRef = formRefMap[moduleKey];
2425
2232
  const primaryKey = moduleConfigs[moduleKey].primaryKey;
2426
- formRef?.value?.openDialog(row?.[primaryKey], getParentIdForModule(moduleKey));
2233
+ formRefMap[moduleKey]?.value?.openDialog(row?.[primaryKey], getParentIdForModule(moduleKey));
2427
2234
  };
2428
2235
 
2429
2236
  const handleDelete = async (moduleKey: string, row: any) => {
@@ -2448,9 +2255,9 @@ const handleEnable = async (moduleKey: string, row: any) => {
2448
2255
  const primaryKey = moduleConfigs[moduleKey].primaryKey;
2449
2256
  await apiHandlerMap[moduleKey].enable(row?.[primaryKey]);
2450
2257
  await loadModuleData(moduleKey);
2451
- useMessage().success('闂傚倸鍊搁崐鎼佸磹妞嬪海鐭嗗〒姘e亾妤犵偛顦甸弫鎾绘偐閼碱剦妲遍柣鐔哥矌婢ф鏁幒妤€鍨傞柛宀€鍋為悡娆撴煙椤栨粌顣兼い銉ヮ樀閺岋紕鈧絺鏅濈粣鏃堟煛鐏炵偓绀嬬€规洜鍘ч埞鎴﹀箛椤撳濡囩槐鎾存媴閸濆嫷鈧挾绱撳鍕獢鐎殿喖顭烽幃銏ゅ礂閼测晛濮洪梻浣瑰濞插秹宕戦幘鎰佺唵鐟滄粓宕板Δ鍛?);
2258
+ useMessage().success(t('common.messages.enableSuccess'));
2452
2259
  } catch (err: any) {
2453
- useMessage().error(err.msg || '闂傚倸鍊搁崐鎼佸磹妞嬪海鐭嗗〒姘e亾妤犵偛顦甸弫鎾绘偐閼碱剦妲遍柣鐔哥矌婢ф鏁幒妤€鍨傞柛宀€鍋為悡娆撴煙椤栨粌顣兼い銉ヮ樀閺岋紕鈧絺鏅濈粣鏃堟煛鐏炵偓绀嬬€规洜鍘ч埞鎴﹀炊閼告妫х紓鍌氬€风粈浣规櫠鎼淬劌纾婚柕鍫濇媼閸ゆ洟鏌涢锝嗙闂佸崬娲﹂妵鍕箛閸偆绐旈梺鍛婂灣缁瑩骞?);
2260
+ useMessage().error(err.msg || t('common.messages.enableError'));
2454
2261
  }
2455
2262
  };
2456
2263
 
@@ -2459,54 +2266,15 @@ const handleDisable = async (moduleKey: string, row: any) => {
2459
2266
  const primaryKey = moduleConfigs[moduleKey].primaryKey;
2460
2267
  await apiHandlerMap[moduleKey].disable(row?.[primaryKey]);
2461
2268
  await loadModuleData(moduleKey);
2462
- useMessage().success('缂傚倸鍊搁崐鎼佸磹閹间礁纾瑰瀣捣閻棗銆掑锝呬壕閻庤娲橀崝娆撳箖濠婂牊鍤嶉柕澶堝劜閻i亶姊绘担绋款棌闁稿鎳庣叅闁哄稁鍘奸崙鐘层€掑锝呬壕闂佸搫鏈ú鐔风暦閻撳簶鏀介柟閭﹀帨閿斿墽纾藉ù锝呮惈椤庢挾绱撳鍕獢鐎殿喖顭烽幃銏ゅ礂閼测晛濮洪梻浣瑰濞插秹宕戦幘鎰佺唵鐟滄粓宕板Δ鍛?);
2269
+ useMessage().success(t('common.messages.disableSuccess'));
2463
2270
  } catch (err: any) {
2464
- useMessage().error(err.msg || '缂傚倸鍊搁崐鎼佸磹閹间礁纾瑰瀣捣閻棗銆掑锝呬壕閻庤娲橀崝娆撳箖濠婂牊鍤嶉柕澶堝劜閻i亶姊绘担绋款棌闁稿鎳庣叅闁哄稁鍘奸崙鐘层€掑锝呬壕闂佸搫鏈ú鐔风暦閻撳簶鏀介柛銉ㄥ煐椤ワ絿绱撻崒娆戭槮濠⒀冮叄瀹曟垿濡堕崪浣告闂佸壊鍋呭ú鏍煁閸ャ劊浜滈柟鏉跨仛缁舵岸鏌涢幋婊呯煓闁?);
2271
+ useMessage().error(err.msg || t('common.messages.disableError'));
2465
2272
  }
2466
2273
  };
2467
-
2468
- const handleFormRefresh = (moduleKey: string) => {
2469
- loadModuleData(moduleKey);
2470
- };
2471
-
2472
- watch(activeLevel1Key, async (moduleKey) => {
2473
- if (!moduleKey) return;
2474
- const level1Module = moduleConfigs[moduleKey];
2475
- if (!level1Module) return;
2476
- Object.assign(moduleStateMap[level1Module.key], {
2477
- currentRow: null,
2478
- currentPage: 1,
2479
- });
2480
- resetDescendantStates(1);
2481
- await loadModuleData(level1Module.key, true);
2482
- });
2483
-
2484
- watch(activeLevel2Key, (moduleKey) => {
2485
- if (moduleKey) {
2486
- if (level3Config) {
2487
- level3Config.moduleKeys.forEach((key) => {
2488
- moduleStateMap[key].currentRow = null;
2489
- moduleStateMap[key].dataList = [];
2490
- moduleStateMap[key].total = 0;
2491
- });
2492
- }
2493
- loadModuleData(moduleKey, true);
2494
- }
2495
- });
2496
-
2497
- watch(activeLevel3Key, (moduleKey) => {
2498
- if (moduleKey) loadModuleData(moduleKey, true);
2499
- });
2500
-
2501
- onMounted(async () => {
2502
- if (activeLevel1Module.value) {
2503
- await loadModuleData(activeLevel1Module.value.key, true);
2504
- }
2505
- });
2506
2274
  </script>
2507
2275
 
2508
- `;
2509
- }
2276
+ `;
2277
+ }
2510
2278
 
2511
2279
  function renderMultiLevelMenuSql(model) {
2512
2280
  const menuBaseId = Date.now();
@@ -2521,9 +2289,9 @@ function renderMultiLevelFiles(model, sharedSupport, localeZhSupport) {
2521
2289
  const viewRoot = path.join(model.frontendPath, 'src', 'views', ...model.moduleName.split('/'), model.functionName);
2522
2290
  const apiRoot = path.join(model.frontendPath, 'src', 'api', ...model.moduleName.split('/'));
2523
2291
  const menuRoot = path.join(model.frontendPath, 'menu');
2524
- const files = [
2525
- { type: 'list', path: path.join(viewRoot, 'index.vue'), content: renderMultiLevelIndexVue(model) },
2526
- { type: 'options', path: path.join(viewRoot, 'options.ts'), content: renderMultiLevelOptionsTs(model, dictRegistryRefs) },
2292
+ const files = [
2293
+ { type: 'list', path: path.join(viewRoot, 'index.vue'), content: renderMultiLevelIndexVue(model) },
2294
+ { type: 'options', path: path.join(viewRoot, 'options.ts'), content: renderMultiLevelOptionsTs(model, dictRegistryRefs) },
2527
2295
  { type: 'api', path: path.join(apiRoot, `${model.functionName}.ts`), content: renderMultiLevelApiTs(model) },
2528
2296
  {
2529
2297
  type: 'i18nZh',
@@ -2896,3 +2664,5 @@ start();
2896
2664
 
2897
2665
 
2898
2666
 
2667
+
2668
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worsoft-frontend-codegen-local-mcp",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "Worsoft frontend local-template code generation MCP server.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "worsoft <sw@worsoft.vip>",