worsoft-frontend-codegen-local-mcp 0.1.64 → 0.1.66

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.
@@ -1,48 +1,7 @@
1
- // 请求工具
2
- import request from '/@/utils/request';
1
+ // 通用 CRUD 接口工厂
2
+ import { createCrudApi } from '/@/api/common/crudFactory';
3
+ {{API_REQUEST_IMPORT}}
3
4
 
4
- // 查询分页列表
5
- export function fetchList(query?: object) {
6
- return request({
7
- url: '/{{API_PATH}}/page',
8
- method: 'get',
9
- params: query,
10
- });
11
- }
12
-
13
- // 新增数据
14
- export function addObj(obj?: object) {
15
- return request({
16
- url: '/{{API_PATH}}/save',
17
- method: 'post',
18
- data: obj,
19
- });
20
- }
21
-
22
- // 查询详情数据
23
- export function getObj(obj?: object) {
24
- return request({
25
- url: '/{{API_PATH}}/getById',
26
- method: 'get',
27
- params: obj,
28
- });
29
- }
30
-
31
- // 删除数据
32
- export function delObjs(ids?: object) {
33
- return request({
34
- url: '/{{API_PATH}}/removeByIds',
35
- method: 'post',
36
- data: ids,
37
- });
38
- }
39
-
40
- // 更新数据
41
- export function putObj(obj?: object) {
42
- return request({
43
- url: '/{{API_PATH}}/updateById',
44
- method: 'post',
45
- data: obj,
46
- });
47
- }
5
+ // {{FEATURE_TITLE}} 通用列表、新增、详情、删除、更新接口
6
+ export const { fetchList, addObj, getObj, delObjs, putObj } = createCrudApi('/{{API_PATH}}');
48
7
  {{EXTRA_API_FUNCTIONS}}
@@ -1,9 +1,13 @@
1
1
  <template>
2
+ <!-- 页面布局:{{FEATURE_TITLE}}主子表表单页 -->
2
3
  <div class="layout-padding-auto layout-padding-view">
4
+ <!-- 表单卡片:承载标题、操作按钮、主表字段和子表明细 -->
3
5
  <el-card shadow="never" class="w100">
4
6
  <template #header>
7
+ <!-- 表单页头:根据新增、编辑、详情模式展示标题和按钮 -->
5
8
  <div style="display: flex; justify-content: space-between; align-items: center;">
6
9
  <span style="font-size: 16px; font-weight: bold;">{{ form.{{PK_ATTR}} ? (detail ? t('common.viewBtn') : t('common.editBtn')) : t('common.addBtn') }}</span>
10
+ <!-- 编辑模式操作按钮:保存、流转、提交、取消 -->
7
11
  <div v-if="!detail">
8
12
  <el-button @click="onSubmit('save')" :disabled="loading" icon="document">{{ commonActionLabel('save') }}</el-button>
9
13
  <el-button @click="onSubmit('flow')" :disabled="loading" plain type="success" icon="position">{{ commonActionLabel('flow') }}</el-button>
@@ -11,15 +15,19 @@
11
15
  <el-divider direction="vertical" />
12
16
  <el-button @click="handleBack" icon="close">{{ t('common.cancelButtonText') }}</el-button>
13
17
  </div>
18
+ <!-- 详情模式操作按钮:返回列表页 -->
14
19
  <div v-else>
15
20
  <el-button @click="handleBack" icon="back">{{ commonActionLabel('back') }}</el-button>
16
21
  </div>
17
22
  </div>
18
23
  </template>
24
+ <!-- 主子表表单:按 PRD 表单显隐和顺序渲染字段 -->
19
25
  <el-form ref="dataFormRef" :model="form" :rules="dataRules" :disabled="detail" v-loading="loading">
26
+ <!-- 主表字段区:字段级注释由 MCP 根据字段名称生成 -->
20
27
  <el-row :gutter="24">
21
28
  {{FORM_FIELDS}}
22
29
  </el-row>
30
+ <!-- 子表明细区:按 children 配置渲染多个子表 -->
23
31
  <el-row :gutter="24">
24
32
  {{CHILD_SECTIONS}}
25
33
  </el-row>
@@ -1,7 +1,9 @@
1
1
  <!-- 功能名称:{{FEATURE_TITLE}} -->
2
2
  <template>
3
+ <!-- 页面布局:{{FEATURE_TITLE}}列表页 -->
3
4
  <div class="layout-padding">
4
5
  <div class="layout-padding-auto layout-padding-view flex h-full flex-col">
6
+ <!-- 列表工具栏:搜索、新增、导入、批量删除、导出、刷新 -->
5
7
  <SchemaListToolbar
6
8
  v-bind="toolbarProps"
7
9
  @update:keyword="state.queryForm.smartVal = $event"
@@ -15,6 +17,7 @@
15
17
  @refresh="handleToolbarRefresh"
16
18
  />
17
19
 
20
+ <!-- 列表表格:展示{{FEATURE_TITLE}}列表数据、分页、排序和行操作 -->
18
21
  <SchemaListTable
19
22
  v-bind="tableProps"
20
23
  row-id-key="{{PK_ATTR}}"
@@ -23,16 +26,23 @@
23
26
  @size-change="handleTableSizeChange"
24
27
  @current-change="handleTableCurrentChange"
25
28
  >
29
+ <!-- 行操作按钮:查看、编辑、提交、流转、删除 -->
26
30
  <template #actions="{ row }">
31
+ <!-- 查看{{FEATURE_TITLE}}详情 -->
27
32
  <el-button text type="primary" icon="view" v-auth="'{{PERMISSION_PREFIX}}_view'" @click="handleDetail(row.{{PK_ATTR}})">{{ t('common.viewBtn') }}</el-button>
33
+ <!-- 编辑{{FEATURE_TITLE}}:业务单据编辑态控制按需生效 -->
28
34
  <el-button{{BUSINESS_EDIT_IF}} icon="edit-pen" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_edit'" @click="handleEdit(row.{{PK_ATTR}})">{{ t('common.editBtn') }}</el-button>
35
+ <!-- 提交{{FEATURE_TITLE}} -->
29
36
  <el-button text type="primary" icon="check" @click="handleQuickAction(row, 'submit')">{{ commonActionLabel('submit') }}</el-button>
37
+ <!-- 流转{{FEATURE_TITLE}} -->
30
38
  <el-button text type="success" icon="position" @click="handleQuickAction(row, 'flow')">{{ commonActionLabel('flow') }}</el-button>
39
+ <!-- 删除{{FEATURE_TITLE}} -->
31
40
  <el-button icon="delete" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_del'" @click="handleDelete([row.{{PK_ATTR}}])">{{ t('common.delBtn') }}</el-button>
32
41
  </template>
33
42
  </SchemaListTable>
34
43
  </div>
35
44
 
45
+ <!-- Excel 导入组件:导入{{FEATURE_TITLE}}列表数据 -->
36
46
  <upload-excel ref="excelUploadRef" :title="t('common.importBtn')" url="/{{API_PATH}}/import" temp-url="/admin/sys-file/local/file/{{FUNCTION_NAME}}.xlsx" @refreshDataList="getDataList" />
37
47
  </div>
38
48
  </template>
@@ -5,6 +5,10 @@
5
5
  insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
6
6
  values ({{MENU_BASE_ID}}, '-1', '/{{MENU_ROUTE_PATH}}/index', '', '0', 'icon-bangzhushouji', '0', null, '8', null, '{{TABLE_COMMENT}}管理', 1);
7
7
 
8
+ -- Form route SQL for jump-based pages. Hidden from sidebar, used by list page add/edit/view navigation.
9
+ insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, visible, del_flag, create_time, sort_order, update_time, name, tenant_id)
10
+ values ({{MENU_BASE_ID_PLUS_6}}, {{MENU_BASE_ID}}, '/{{MENU_ROUTE_PATH}}/form', '', '0', 'icon-biaodan', '0', '0', null, '9', null, '{{TABLE_COMMENT}}表单', 1);
11
+
8
12
  insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
9
13
  values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1);
10
14
 
@@ -18,4 +22,4 @@ insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del
18
22
  values ({{MENU_BASE_ID_PLUS_4}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_del', '1', null, '1', '0', null, '3', null, '{{TABLE_COMMENT}}删除', 1);
19
23
 
20
24
  insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
21
- values ({{MENU_BASE_ID_PLUS_5}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_export', '1', null, '1', '0', null, '4', null, '导入导出', 1);
25
+ values ({{MENU_BASE_ID_PLUS_5}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_export', '1', null, '1', '0', null, '4', null, '导入导出', 1);
@@ -1,50 +1,9 @@
1
- // 请求工具
2
- import request from '/@/utils/request';
1
+ // 通用 CRUD 接口工厂
2
+ import { createCrudApi } from '/@/api/common/crudFactory';
3
+ {{API_REQUEST_IMPORT}}
3
4
 
4
- // 查询分页列表
5
- export function fetchList(query?: object) {
6
- return request({
7
- url: '/{{API_PATH}}/page',
8
- method: 'get',
9
- params: query,
10
- });
11
- }
12
-
13
- // 新增数据
14
- export function addObj(obj?: object) {
15
- return request({
16
- url: '/{{API_PATH}}',
17
- method: 'post',
18
- data: obj,
19
- });
20
- }
21
-
22
- // 查询详情数据
23
- export function getObj(obj?: object) {
24
- return request({
25
- url: '/{{API_PATH}}/details',
26
- method: 'get',
27
- params: obj,
28
- });
29
- }
30
-
31
- // 删除数据
32
- export function delObjs(ids?: object) {
33
- return request({
34
- url: '/{{API_PATH}}',
35
- method: 'delete',
36
- data: ids,
37
- });
38
- }
39
-
40
- // 更新数据
41
- export function putObj(obj?: object) {
42
- return request({
43
- url: '/{{API_PATH}}',
44
- method: 'put',
45
- data: obj,
46
- });
47
- }
5
+ // {{FEATURE_TITLE}} 通用列表、新增、详情、删除、更新接口
6
+ export const { fetchList, addObj, getObj, delObjs, putObj } = createCrudApi('/{{API_PATH}}');
48
7
 
49
8
  {{DICT_API_FUNCTIONS}}
50
9
  {{EXTRA_API_FUNCTIONS}}
@@ -1,10 +1,14 @@
1
1
  <template>
2
+ <!-- 表单弹窗:新增或编辑{{FEATURE_TITLE}}数据 -->
2
3
  <el-dialog v-model="visible" :title="form.{{PK_ATTR}} ? t('common.editBtn') : t('common.addBtn')" :close-on-click-modal="false" draggable>
4
+ <!-- 弹窗表单:按 PRD 表单显隐和顺序渲染字段 -->
3
5
  <el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
6
+ <!-- 主表字段区:字段级注释由 MCP 根据字段名称生成 -->
4
7
  <el-row :gutter="24">
5
8
  {{FORM_FIELDS}}
6
9
  </el-row>
7
10
  </el-form>
11
+ <!-- 弹窗底部操作按钮:取消和确认提交 -->
8
12
  <template #footer>
9
13
  <span class="dialog-footer">
10
14
  <el-button @click="visible = false">{{ t('common.cancelButtonText') }}</el-button>
@@ -1,7 +1,9 @@
1
1
  <!-- 功能名称:{{FEATURE_TITLE}} -->
2
2
  <template>
3
+ <!-- 页面布局:{{FEATURE_TITLE}}列表页 -->
3
4
  <div class="layout-padding">
4
5
  <div class="layout-padding-auto layout-padding-view flex h-full flex-col">
6
+ <!-- 列表工具栏:搜索、新增、导入、批量删除、导出、刷新 -->
5
7
  <SchemaListToolbar
6
8
  v-bind="toolbarProps"
7
9
  @update:keyword="state.queryForm.smartVal = $event"
@@ -15,6 +17,7 @@
15
17
  @refresh="handleToolbarRefresh"
16
18
  />
17
19
 
20
+ <!-- 列表表格:展示{{FEATURE_TITLE}}列表数据、分页、排序和行操作 -->
18
21
  <SchemaListTable
19
22
  v-bind="tableProps"
20
23
  row-id-key="{{PK_ATTR}}"
@@ -24,13 +27,16 @@
24
27
  @size-change="handleTableSizeChange"
25
28
  @current-change="handleTableCurrentChange"
26
29
  >
30
+ <!-- 行操作按钮:编辑、删除、启用、禁用 -->
27
31
  <template #actions="{ row }">
28
32
  {{LIST_ACTIONS}}
29
33
  </template>
30
34
  </SchemaListTable>
31
35
  </div>
32
36
 
37
+ <!-- 表单弹窗组件:新增和编辑{{FEATURE_TITLE}}数据 -->
33
38
  <form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
39
+ <!-- Excel 导入组件:导入{{FEATURE_TITLE}}列表数据 -->
34
40
  <upload-excel ref="excelUploadRef" :title="t('common.importBtn')" url="/{{API_PATH}}/import" temp-url="/admin/sys-file/local/file/{{FUNCTION_NAME}}.xlsx" @refreshDataList="getDataList" />
35
41
  </div>
36
42
  </template>
@@ -1,48 +1,7 @@
1
- // 请求工具
2
- import request from '/@/utils/request';
1
+ // 通用 CRUD 接口工厂
2
+ import { createCrudApi } from '/@/api/common/crudFactory';
3
+ {{API_REQUEST_IMPORT}}
3
4
 
4
- // 查询分页列表
5
- export function fetchList(query?: object) {
6
- return request({
7
- url: '/{{API_PATH}}/page',
8
- method: 'get',
9
- params: query,
10
- });
11
- }
12
-
13
- // 新增数据
14
- export function addObj(obj?: object) {
15
- return request({
16
- url: '/{{API_PATH}}',
17
- method: 'post',
18
- data: obj,
19
- });
20
- }
21
-
22
- // 查询详情数据
23
- export function getObj(obj?: object) {
24
- return request({
25
- url: '/{{API_PATH}}/details',
26
- method: 'get',
27
- params: obj,
28
- });
29
- }
30
-
31
- // 删除数据
32
- export function delObjs(ids?: object) {
33
- return request({
34
- url: '/{{API_PATH}}',
35
- method: 'delete',
36
- data: ids,
37
- });
38
- }
39
-
40
- // 更新数据
41
- export function putObj(obj?: object) {
42
- return request({
43
- url: '/{{API_PATH}}',
44
- method: 'put',
45
- data: obj,
46
- });
47
- }
5
+ // {{FEATURE_TITLE}} 通用列表、新增、详情、删除、更新接口
6
+ export const { fetchList, addObj, getObj, delObjs, putObj } = createCrudApi('/{{API_PATH}}');
48
7
  {{EXTRA_API_FUNCTIONS}}
@@ -1,9 +1,13 @@
1
1
  <template>
2
+ <!-- 页面布局:{{FEATURE_TITLE}}表单页 -->
2
3
  <div class="layout-padding-auto layout-padding-view">
4
+ <!-- 表单卡片:承载标题、操作按钮和主表字段 -->
3
5
  <el-card shadow="never" class="w100">
4
6
  <template #header>
7
+ <!-- 表单页头:根据新增、编辑、详情模式展示标题和按钮 -->
5
8
  <div style="display: flex; justify-content: space-between; align-items: center;">
6
9
  <span style="font-size: 16px; font-weight: bold;">{{ form.{{PK_ATTR}} ? (detail ? t('common.viewBtn') : t('common.editBtn')) : t('common.addBtn') }}</span>
10
+ <!-- 编辑模式操作按钮:保存、流转、提交、取消 -->
7
11
  <div v-if="!detail">
8
12
  <el-button @click="onSubmit('save')" :disabled="loading" icon="document">{{ commonActionLabel('save') }}</el-button>
9
13
  <el-button @click="onSubmit('flow')" :disabled="loading" plain type="success" icon="position">{{ commonActionLabel('flow') }}</el-button>
@@ -11,12 +15,15 @@
11
15
  <el-divider direction="vertical" />
12
16
  <el-button @click="handleBack" icon="close">{{ t('common.cancelButtonText') }}</el-button>
13
17
  </div>
18
+ <!-- 详情模式操作按钮:返回列表页 -->
14
19
  <div v-else>
15
20
  <el-button @click="handleBack" icon="back">{{ commonActionLabel('back') }}</el-button>
16
21
  </div>
17
22
  </div>
18
23
  </template>
24
+ <!-- 主表表单:按 PRD 表单显隐和顺序渲染字段 -->
19
25
  <el-form ref="dataFormRef" :model="form" :rules="dataRules" :disabled="detail" v-loading="loading">
26
+ <!-- 主表字段区:字段级注释由 MCP 根据字段名称生成 -->
20
27
  <el-row :gutter="24">
21
28
  {{FORM_FIELDS}}
22
29
  </el-row>
@@ -1,7 +1,9 @@
1
1
  <!-- 功能名称:{{FEATURE_TITLE}} -->
2
2
  <template>
3
+ <!-- 页面布局:{{FEATURE_TITLE}}列表页 -->
3
4
  <div class="layout-padding">
4
5
  <div class="layout-padding-auto layout-padding-view flex h-full flex-col">
6
+ <!-- 列表工具栏:搜索、新增、导入、批量删除、导出、刷新 -->
5
7
  <SchemaListToolbar
6
8
  v-bind="toolbarProps"
7
9
  @update:keyword="state.queryForm.smartVal = $event"
@@ -15,6 +17,7 @@
15
17
  @refresh="handleToolbarRefresh"
16
18
  />
17
19
 
20
+ <!-- 列表表格:展示{{FEATURE_TITLE}}列表数据、分页、排序和行操作 -->
18
21
  <SchemaListTable
19
22
  v-bind="tableProps"
20
23
  row-id-key="{{PK_ATTR}}"
@@ -24,14 +27,19 @@
24
27
  @size-change="handleTableSizeChange"
25
28
  @current-change="handleTableCurrentChange"
26
29
  >
30
+ <!-- 行操作按钮:查看、编辑、删除 -->
27
31
  <template #actions="{ row }">
32
+ <!-- 查看{{FEATURE_TITLE}}详情 -->
28
33
  <el-button text type="primary" icon="view" v-auth="'{{PERMISSION_PREFIX}}_view'" @click="handleDetail(row.{{PK_ATTR}})">{{ t('common.viewBtn') }}</el-button>
34
+ <!-- 编辑{{FEATURE_TITLE}}:业务单据编辑态控制按需生效 -->
29
35
  <el-button{{BUSINESS_EDIT_IF}} icon="edit-pen" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_edit'" @click="handleEdit(row.{{PK_ATTR}})">{{ t('common.editBtn') }}</el-button>
36
+ <!-- 删除{{FEATURE_TITLE}} -->
30
37
  <el-button icon="delete" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_del'" @click="handleDelete([row.{{PK_ATTR}}])">{{ t('common.delBtn') }}</el-button>
31
38
  </template>
32
39
  </SchemaListTable>
33
40
  </div>
34
41
 
42
+ <!-- Excel 导入组件:导入{{FEATURE_TITLE}}列表数据 -->
35
43
  <upload-excel ref="excelUploadRef" :title="t('common.importBtn')" url="/{{API_PATH}}/import" temp-url="/admin/sys-file/local/file/{{FUNCTION_NAME}}.xlsx" @refreshDataList="getDataList" />
36
44
  </div>
37
45
  </template>
@@ -5,6 +5,10 @@
5
5
  insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
6
6
  values ({{MENU_BASE_ID}}, '-1', '/{{MENU_ROUTE_PATH}}/index', '', '0', 'icon-bangzhushouji', '0', null, '8', null, '{{TABLE_COMMENT}}管理', 1);
7
7
 
8
+ -- Form route SQL for jump-based pages. Hidden from sidebar, used by list page add/edit/view navigation.
9
+ insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, visible, del_flag, create_time, sort_order, update_time, name, tenant_id)
10
+ values ({{MENU_BASE_ID_PLUS_6}}, {{MENU_BASE_ID}}, '/{{MENU_ROUTE_PATH}}/form', '', '0', 'icon-biaodan', '0', '0', null, '9', null, '{{TABLE_COMMENT}}表单', 1);
11
+
8
12
  insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
9
13
  values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1);
10
14
 
@@ -18,4 +22,4 @@ insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del
18
22
  values ({{MENU_BASE_ID_PLUS_4}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_del', '1', null, '1', '0', null, '3', null, '{{TABLE_COMMENT}}删除', 1);
19
23
 
20
24
  insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
21
- values ({{MENU_BASE_ID_PLUS_5}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_export', '1', null, '1', '0', null, '4', null, '导入导出', 1);
25
+ values ({{MENU_BASE_ID_PLUS_5}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_export', '1', null, '1', '0', null, '4', null, '导入导出', 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.64';
8
+ const SERVER_VERSION = '0.1.66';
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');
@@ -146,10 +146,65 @@ export function createCrudSchema(
146
146
  }
147
147
 
148
148
  return buildSchema(masterOrDefinition as Record<string, FieldMeta>, children);
149
- }
150
- `;
151
-
152
- const TOOL_SCHEMA = {
149
+ }
150
+ `;
151
+
152
+ const DEFAULT_CRUD_FACTORY_TEMPLATE = `import request from '/@/utils/request';
153
+
154
+ export interface CrudApiPathOverrides {
155
+ page?: string;
156
+ save?: string;
157
+ detail?: string;
158
+ remove?: string;
159
+ update?: string;
160
+ }
161
+
162
+ const joinApiPath = (baseUrl: string, suffix: string) => {
163
+ const normalizedBase = String(baseUrl || '').replace(/\\/+$/, '');
164
+ const normalizedSuffix = String(suffix || '').replace(/^\\/+/, '');
165
+ return normalizedSuffix ? \`\${normalizedBase}/\${normalizedSuffix}\` : normalizedBase;
166
+ };
167
+
168
+ // 生成标准业务功能 CRUD 接口:分页、新增、详情、删除、更新。
169
+ export const createCrudApi = (baseUrl: string, overrides: CrudApiPathOverrides = {}) => ({
170
+ fetchList: (query?: object) =>
171
+ request({
172
+ url: overrides.page || joinApiPath(baseUrl, 'page'),
173
+ method: 'get',
174
+ params: query,
175
+ }),
176
+
177
+ addObj: (obj?: object) =>
178
+ request({
179
+ url: overrides.save || joinApiPath(baseUrl, 'save'),
180
+ method: 'post',
181
+ data: obj,
182
+ }),
183
+
184
+ getObj: (obj?: object) =>
185
+ request({
186
+ url: overrides.detail || joinApiPath(baseUrl, 'getById'),
187
+ method: 'get',
188
+ params: obj,
189
+ }),
190
+
191
+ delObjs: (ids?: object) =>
192
+ request({
193
+ url: overrides.remove || joinApiPath(baseUrl, 'removeByIds'),
194
+ method: 'post',
195
+ data: ids,
196
+ }),
197
+
198
+ putObj: (obj?: object) =>
199
+ request({
200
+ url: overrides.update || joinApiPath(baseUrl, 'updateById'),
201
+ method: 'post',
202
+ data: obj,
203
+ }),
204
+ });
205
+ `;
206
+
207
+ const TOOL_SCHEMA = {
153
208
  type: 'object',
154
209
  properties: {
155
210
  featureTitle: { type: 'string', description: 'Feature title from pre-parsed structured metadata.' },
@@ -340,11 +395,11 @@ const TOOL_SCHEMA = {
340
395
  },
341
396
  writeToDisk: { type: 'boolean', default: true, description: 'Whether to write generated files.' },
342
397
  overwrite: { type: 'boolean', default: true, description: 'Whether to overwrite existing files. If false, existing files are skipped.' },
343
- writeSupportFiles: {
344
- type: 'boolean',
345
- default: true,
346
- description: 'Whether to write project support files such as src/enums/dict-registry.ts and src/utils/crudSchema.ts.'
347
- },
398
+ writeSupportFiles: {
399
+ type: 'boolean',
400
+ default: true,
401
+ description: 'Whether to write project support files such as src/enums/dict-registry.ts, src/utils/crudSchema.ts and src/api/common/crudFactory.ts.'
402
+ },
348
403
  mergeI18nZh: {
349
404
  type: 'boolean',
350
405
  default: true,
@@ -835,7 +890,7 @@ function prepareDictRegistry(frontendPath, dictTypes) {
835
890
  };
836
891
  }
837
892
 
838
- function ensureCrudSchemaSupportFile(frontendPath) {
893
+ function ensureCrudSchemaSupportFile(frontendPath) {
839
894
  const schemaPath = path.join(frontendPath, 'src', 'utils', 'crudSchema.ts');
840
895
  const exists = fs.existsSync(schemaPath);
841
896
  const currentContent = exists ? readUtf8File(schemaPath) : '';
@@ -852,12 +907,27 @@ function ensureCrudSchemaSupportFile(frontendPath) {
852
907
  exists,
853
908
  isCompatible: hasCoreShape && supportsLabelKey,
854
909
  needsWrite: !exists || shouldUpgradeLegacy,
855
- };
856
- }
857
-
858
- function ensureDirectory(filePath) {
859
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
860
- }
910
+ };
911
+ }
912
+
913
+ function ensureCrudFactorySupportFile(frontendPath) {
914
+ const factoryPath = path.join(frontendPath, 'src', 'api', 'common', 'crudFactory.ts');
915
+ const exists = fs.existsSync(factoryPath);
916
+ const currentContent = exists ? readUtf8File(factoryPath) : '';
917
+ const isCompatible = !exists || currentContent.includes('export const createCrudApi');
918
+
919
+ return {
920
+ path: factoryPath,
921
+ content: DEFAULT_CRUD_FACTORY_TEMPLATE,
922
+ exists,
923
+ isCompatible,
924
+ needsWrite: !exists,
925
+ };
926
+ }
927
+
928
+ function ensureDirectory(filePath) {
929
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
930
+ }
861
931
 
862
932
  function writeSupportFile(filePath, content) {
863
933
  ensureDirectory(filePath);
@@ -880,30 +950,52 @@ function prepareSharedSupport(frontendPath, dictTypes, writeSupportFiles) {
880
950
  const normalizedDictTypes = [...new Set((dictTypes || []).filter(Boolean))];
881
951
  const dictRegistry = writeSupportFiles ? prepareDictRegistry(frontendPath, normalizedDictTypes) : buildVirtualDictRegistry(normalizedDictTypes);
882
952
  const crudSchemaPath = path.join(frontendPath, 'src', 'utils', 'crudSchema.ts');
883
- const crudSchema = writeSupportFiles
884
- ? ensureCrudSchemaSupportFile(frontendPath)
885
- : {
886
- path: crudSchemaPath,
953
+ const crudSchema = writeSupportFiles
954
+ ? ensureCrudSchemaSupportFile(frontendPath)
955
+ : {
956
+ path: crudSchemaPath,
887
957
  content: DEFAULT_CRUD_SCHEMA_TEMPLATE,
888
958
  exists: fs.existsSync(crudSchemaPath),
889
959
  isCompatible: true,
890
- needsWrite: false,
891
- writeEnabled: false,
892
- };
893
- return {
894
- dictRegistry,
895
- crudSchema,
896
- writeEnabled: Boolean(writeSupportFiles),
897
- };
898
- }
960
+ needsWrite: false,
961
+ writeEnabled: false,
962
+ };
963
+ const crudFactoryPath = path.join(frontendPath, 'src', 'api', 'common', 'crudFactory.ts');
964
+ const crudFactory = writeSupportFiles
965
+ ? ensureCrudFactorySupportFile(frontendPath)
966
+ : {
967
+ path: crudFactoryPath,
968
+ content: DEFAULT_CRUD_FACTORY_TEMPLATE,
969
+ exists: fs.existsSync(crudFactoryPath),
970
+ isCompatible: true,
971
+ needsWrite: false,
972
+ writeEnabled: false,
973
+ };
974
+ return {
975
+ dictRegistry,
976
+ crudSchema,
977
+ crudFactory,
978
+ writeEnabled: Boolean(writeSupportFiles),
979
+ };
980
+ }
899
981
 
900
982
  function maybeWriteSharedSupport(sharedSupport, writeToDisk) {
901
983
  if (!writeToDisk || !sharedSupport.writeEnabled) return;
902
984
 
903
- if (sharedSupport.crudSchema.needsWrite) {
904
- writeSupportFile(sharedSupport.crudSchema.path, sharedSupport.crudSchema.content);
905
- }
906
-
985
+ if (sharedSupport.crudSchema.needsWrite) {
986
+ writeSupportFile(sharedSupport.crudSchema.path, sharedSupport.crudSchema.content);
987
+ }
988
+
989
+ if (sharedSupport.crudFactory.needsWrite) {
990
+ if (!sharedSupport.crudFactory.isCompatible) {
991
+ throw new Error(
992
+ 'Detected an existing src/api/common/crudFactory.ts that MCP could not merge safely. ' +
993
+ 'Please align the createCrudApi export manually before enabling writeSupportFiles.'
994
+ );
995
+ }
996
+ writeSupportFile(sharedSupport.crudFactory.path, sharedSupport.crudFactory.content);
997
+ }
998
+
907
999
  if (sharedSupport.dictRegistry.needsWrite) {
908
1000
  if (!sharedSupport.dictRegistry.isCompatible) {
909
1001
  throw new Error(
@@ -918,11 +1010,11 @@ function maybeWriteSharedSupport(sharedSupport, writeToDisk) {
918
1010
  function buildSupportNote(sharedSupport, localeZhSupport) {
919
1011
  const notes = [];
920
1012
 
921
- if (!sharedSupport.writeEnabled) {
922
- notes.push(
923
- 'Shared support file writing is disabled. Generated code still references src/utils/crudSchema.ts and may reference src/enums/dict-registry.ts, so those helpers must already exist in the target project.'
924
- );
925
- }
1013
+ if (!sharedSupport.writeEnabled) {
1014
+ notes.push(
1015
+ 'Shared support file writing is disabled. Generated code still references src/utils/crudSchema.ts, src/api/common/crudFactory.ts and may reference src/enums/dict-registry.ts, so those helpers must already exist in the target project.'
1016
+ );
1017
+ }
926
1018
 
927
1019
  if (sharedSupport.crudSchema.exists && !sharedSupport.crudSchema.isCompatible) {
928
1020
  notes.push(
@@ -931,6 +1023,13 @@ function buildSupportNote(sharedSupport, localeZhSupport) {
931
1023
  );
932
1024
  }
933
1025
 
1026
+ if (sharedSupport.crudFactory.exists && !sharedSupport.crudFactory.isCompatible) {
1027
+ notes.push(
1028
+ 'Detected an existing src/api/common/crudFactory.ts that does not export createCrudApi. ' +
1029
+ 'MCP preserved the existing file and did not overwrite it. Generated API modules now depend on that file being manually aligned.'
1030
+ );
1031
+ }
1032
+
934
1033
  if (sharedSupport.dictRegistry.exists && !sharedSupport.dictRegistry.isCompatible) {
935
1034
  notes.push(
936
1035
  'Detected an existing src/enums/dict-registry.ts that MCP could not merge safely. ' +
@@ -1956,10 +2055,12 @@ function renderChildSection(childModel, childCount) {
1956
2055
 
1957
2056
  return [
1958
2057
  ' <el-col :span="24" class="mb20">',
2058
+ ` <!-- 子表区域:${sanitizeHtmlComment(childModel.comment || childModel.tableName)} -->`,
1959
2059
  ` <div class="mb10" style="font-weight: 600;">{{ childSectionTitle('${childModel.listName}') }}</div>`,
2060
+ ' <!-- 子表编辑表格:支持新增行、删除行和行内字段编辑 -->',
1960
2061
  ` <sc-form-table v-model="form.${childModel.listName}" :addTemplate="childTemp${childModel.className}" @delete="(obj) => ${deleteExpression}" :placeholder="t('common.noData')">`,
1961
2062
  childModel.visibleFields.map((field) => renderChildTableColumn(field, childModel.listName)).join('\n'),
1962
- ' </sc-form-table>',
2063
+ ' </sc-form-table>',
1963
2064
  ' </el-col>',
1964
2065
  ].join('\n');
1965
2066
  }
@@ -2504,14 +2605,18 @@ function renderMultiLevelFormVue(model, moduleModel) {
2504
2605
  ].join('\n');
2505
2606
  const rules = renderFormRulesV2(moduleModel.visibleFields);
2506
2607
  return `<template>
2507
- <el-dialog v-model="visible" :title="form.${moduleModel.pk.attrName} ? t('common.editBtn') : t('common.addBtn')" :close-on-click-modal="false" draggable>
2508
- <el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
2509
- <el-row :gutter="24">
2510
- ${moduleModel.visibleFields.map(renderMultiLevelFormField).join('\n')}
2511
- </el-row>
2512
- </el-form>
2513
- <template #footer>
2514
- <span class="dialog-footer">
2608
+ <!-- 表单弹窗:新增或编辑${sanitizeHtmlComment(moduleModel.comment || moduleModel.tableName)}数据 -->
2609
+ <el-dialog v-model="visible" :title="form.${moduleModel.pk.attrName} ? t('common.editBtn') : t('common.addBtn')" :close-on-click-modal="false" draggable>
2610
+ <!-- 弹窗表单:按 PRD 表单显隐和顺序渲染字段 -->
2611
+ <el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
2612
+ <!-- 字典字段区:字段级注释由 MCP 根据字段名称生成 -->
2613
+ <el-row :gutter="24">
2614
+ ${moduleModel.visibleFields.map(renderMultiLevelFormField).join('\n')}
2615
+ </el-row>
2616
+ </el-form>
2617
+ <!-- 弹窗底部操作按钮:取消和确认提交 -->
2618
+ <template #footer>
2619
+ <span class="dialog-footer">
2515
2620
  <el-button @click="visible = false">{{ t('common.cancelButtonText') }}</el-button>
2516
2621
  <el-button type="primary" @click="onSubmit" :disabled="loading">{{ t('common.confirmButtonText') }}</el-button>
2517
2622
  </span>
@@ -2642,11 +2747,14 @@ ${moduleModel.visibleFields.map(renderMultiLevelFormField).join('\n')}
2642
2747
  function renderMultiLevelSchemaListSlot(levelVarName, activeKeyVarName, activeModuleVarName) {
2643
2748
  return [
2644
2749
  ` <div class="multi-level-slot" v-if="${levelVarName}">`,
2750
+ ' <!-- 多层字典页签:切换当前层级下的字典模块 -->',
2645
2751
  ' <el-tabs class="multi-level-tabs" v-if="' + `${levelVarName}.moduleKeys.length > 1` + `" v-model="${activeKeyVarName}">`,
2646
2752
  ` <el-tab-pane v-for="moduleKey in ${levelVarName}.moduleKeys" :key="moduleKey" :label="resolveModuleTitle(moduleConfigs[moduleKey])" :name="moduleKey" />`,
2647
2753
  ' </el-tabs>',
2754
+ ' <!-- 多层字典列表面板:承载当前层级的工具栏和表格 -->',
2648
2755
  ' <div class="multi-level-panel" v-if="' + `${activeModuleVarName}` + `">`,
2649
2756
  ' <div class="layout-padding-auto layout-padding-view flex h-full flex-col">',
2757
+ ' <!-- 当前层级工具栏:搜索、新增和重置 -->',
2650
2758
  ` <SchemaListToolbar`,
2651
2759
  ` v-bind="getPanelToolbarProps(${activeModuleVarName}.key)"`,
2652
2760
  ` @update:keyword="handlePanelKeywordChange(${activeModuleVarName}.key, $event)"`,
@@ -2654,16 +2762,22 @@ function renderMultiLevelSchemaListSlot(levelVarName, activeKeyVarName, activeMo
2654
2762
  ` @query="handlePanelQuery(${activeModuleVarName}.key)"`,
2655
2763
  ` @reset="handlePanelReset(${activeModuleVarName}.key)"`,
2656
2764
  ` />`,
2765
+ ' <!-- 当前层级表格:展示字典数据、分页和行操作 -->',
2657
2766
  ` <SchemaListTable`,
2658
2767
  ` v-bind="getPanelTableProps(${activeModuleVarName}.key)"`,
2659
2768
  ` @row-current-change="handlePanelCurrentChange(${activeModuleVarName}.key, $event.row)"`,
2660
2769
  ` @current-change="handleCurrentPageChange(${activeModuleVarName}.key, $event.current)"`,
2661
2770
  ` @size-change="handlePageSizeChange(${activeModuleVarName}.key, $event.size)"`,
2662
2771
  ` >`,
2772
+ ' <!-- 行操作按钮:编辑、删除、启用、禁用 -->',
2663
2773
  ` <template #actions="{ row }">`,
2774
+ ' <!-- 编辑当前字典数据 -->',
2664
2775
  ` <el-button v-if="showModuleAction(${activeModuleVarName}.key, 'edit', row)" icon="edit-pen" text type="primary" @click="openEdit(${activeModuleVarName}.key, row)">{{ t('common.editBtn') }}</el-button>`,
2776
+ ' <!-- 删除当前字典数据 -->',
2665
2777
  ` <el-button v-if="showModuleAction(${activeModuleVarName}.key, 'delete', row)" icon="delete" text type="primary" @click="handleDelete(${activeModuleVarName}.key, row)">{{ t('common.delBtn') }}</el-button>`,
2778
+ ' <!-- 启用当前字典数据 -->',
2666
2779
  ` <el-button v-if="showModuleAction(${activeModuleVarName}.key, 'enable', row)" icon="circle-check" text type="primary" @click="handleEnable(${activeModuleVarName}.key, row)">闁告凹鍨抽弫?/el-button>`,
2780
+ ' <!-- 禁用当前字典数据 -->',
2667
2781
  ` <el-button v-if="showModuleAction(${activeModuleVarName}.key, 'disable', row)" icon="remove" text type="primary" @click="handleDisable(${activeModuleVarName}.key, row)">缂佸倷鑳堕弫?/el-button>`,
2668
2782
  ` </template>`,
2669
2783
  ` </SchemaListTable>`,
@@ -2676,14 +2790,18 @@ function renderMultiLevelSchemaListSlot(levelVarName, activeKeyVarName, activeMo
2676
2790
  function renderMultiLevelLevelSlot(levelVarName, activeKeyVarName, activeModuleVarName) {
2677
2791
  return [
2678
2792
  ` <div class="multi-level-slot" v-if="${levelVarName}">`,
2793
+ ' <!-- 多层字典页签:切换当前层级下的字典模块 -->',
2679
2794
  ' <el-tabs class="multi-level-tabs" v-if="' + `${levelVarName}.moduleKeys.length > 1` + `" v-model="${activeKeyVarName}">`,
2680
2795
  ` <el-tab-pane v-for="moduleKey in ${levelVarName}.moduleKeys" :key="moduleKey" :label="resolveModuleTitle(moduleConfigs[moduleKey])" :name="moduleKey" />`,
2681
2796
  ' </el-tabs>',
2797
+ ' <!-- 多层字典列表面板:承载当前层级的新增按钮、表格和分页 -->',
2682
2798
  ' <div class="multi-level-panel" v-if="' + `${activeModuleVarName}` + `">`,
2683
2799
  ' <div class="layout-padding-auto layout-padding-view flex h-full flex-col">',
2800
+ ' <!-- 当前层级新增按钮:受父级状态约束控制 -->',
2684
2801
  ' <div class="mb8" style="width: 100%">',
2685
2802
  ` <el-button icon="folder-add" type="primary" class="ml10" :disabled="isAddDisabled(${activeModuleVarName}.key)" @click="openCreate(${activeModuleVarName}.key)">{{ t('common.addBtn') }}</el-button>`,
2686
2803
  ' </div>',
2804
+ ' <!-- 当前层级表格:展示字典数据并处理行选中联动 -->',
2687
2805
  ' <el-table',
2688
2806
  ` :data="moduleStateMap[${activeModuleVarName}.key].dataList"`,
2689
2807
  ` v-loading="moduleStateMap[${activeModuleVarName}.key].loading"`,
@@ -2692,7 +2810,9 @@ function renderMultiLevelLevelSlot(levelVarName, activeKeyVarName, activeModuleV
2692
2810
  ' highlight-current-row',
2693
2811
  ` @current-change="handlePanelCurrentChange(${activeModuleVarName}.key, $event)"`,
2694
2812
  ' >',
2813
+ ' <!-- 序号列 -->',
2695
2814
  ` <el-table-column type="index" :label="t('common.serial')" width="60" />`,
2815
+ ' <!-- 字典业务字段列 -->',
2696
2816
  ` <el-table-column`,
2697
2817
  ` v-for="column in getListFields(${activeModuleVarName}.key)"`,
2698
2818
  ' :key="column.key"',
@@ -2706,15 +2826,21 @@ function renderMultiLevelLevelSlot(levelVarName, activeKeyVarName, activeModuleV
2706
2826
  ' <span v-else>{{ scope.row[column.key] }}</span>',
2707
2827
  ' </template>',
2708
2828
  ' </el-table-column>',
2829
+ ' <!-- 行操作按钮:编辑、删除、启用、禁用 -->',
2709
2830
  ` <el-table-column :label="t('common.action')" width="260">`,
2710
2831
  ' <template #default="{ row }">',
2832
+ ' <!-- 编辑当前字典数据 -->',
2711
2833
  ` <el-button v-if="showModuleAction(${activeModuleVarName}.key, 'edit', row)" icon="edit-pen" text type="primary" @click="openEdit(${activeModuleVarName}.key, row)">{{ t('common.editBtn') }}</el-button>`,
2834
+ ' <!-- 删除当前字典数据 -->',
2712
2835
  ` <el-button v-if="showModuleAction(${activeModuleVarName}.key, 'delete', row)" icon="delete" text type="primary" @click="handleDelete(${activeModuleVarName}.key, row)">{{ t('common.delBtn') }}</el-button>`,
2836
+ ' <!-- 启用当前字典数据 -->',
2713
2837
  ` <el-button v-if="showModuleAction(${activeModuleVarName}.key, 'enable', row)" icon="circle-check" text type="primary" @click="handleEnable(${activeModuleVarName}.key, row)">{{ t('common.actions.enable') }}</el-button>`,
2838
+ ' <!-- 禁用当前字典数据 -->',
2714
2839
  ` <el-button v-if="showModuleAction(${activeModuleVarName}.key, 'disable', row)" icon="remove" text type="primary" @click="handleDisable(${activeModuleVarName}.key, row)">{{ t('common.actions.disable') }}</el-button>`,
2715
2840
  ' </template>',
2716
2841
  ' </el-table-column>',
2717
2842
  ' </el-table>',
2843
+ ' <!-- 当前层级分页组件 -->',
2718
2844
  ' <div class="mt-2.5 flex shrink-0 justify-end">',
2719
2845
  ' <pagination',
2720
2846
  ` :current-page="moduleStateMap[${activeModuleVarName}.key].currentPage"`,
@@ -2766,24 +2892,31 @@ function renderMultiLevelIndexVue(model) {
2766
2892
 
2767
2893
  return `<!-- 功能名称:${sanitizeHtmlComment(model.featureTitle)} -->
2768
2894
  <template>
2895
+ <!-- 页面布局:${sanitizeHtmlComment(model.featureTitle)}多层字典列表页 -->
2769
2896
  <div class="layout-padding">
2897
+ <!-- 多层字典主布局:左侧一级字典,右侧二级和三级字典联动 -->
2770
2898
  <div class="multi-level-dict-layout">
2771
- <div class="multi-level-left">
2899
+ <!-- 一级字典区域 -->
2900
+ <div class="multi-level-left">
2772
2901
  ${renderMultiLevelSchemaListSlot('level1Config', 'activeLevel1Key', 'activeLevel1Module')}
2773
- </div>
2774
- <div class="multi-level-right">
2775
- <div class="multi-level-right-top">
2902
+ </div>
2903
+ <!-- 二级和三级字典区域 -->
2904
+ <div class="multi-level-right">
2905
+ <!-- 二级字典区域 -->
2906
+ <div class="multi-level-right-top">
2776
2907
  ${level2 ? renderMultiLevelSchemaListSlot('level2Config', 'activeLevel2Key', 'activeLevel2Module') : ''}
2777
- </div>
2778
- <div v-if="level3Config" class="multi-level-right-bottom">
2908
+ </div>
2909
+ <!-- 三级字典区域 -->
2910
+ <div v-if="level3Config" class="multi-level-right-bottom">
2779
2911
  ${level3 ? renderMultiLevelSchemaListSlot('level3Config', 'activeLevel3Key', 'activeLevel3Module') : ''}
2780
- </div>
2781
- </div>
2782
- </div>
2783
-
2784
- ${formComponents}
2785
- </div>
2786
- </template>
2912
+ </div>
2913
+ </div>
2914
+ </div>
2915
+
2916
+ <!-- 多层字典表单弹窗:按模块异步挂载 -->
2917
+ ${formComponents}
2918
+ </div>
2919
+ </template>
2787
2920
 
2788
2921
  <script setup lang="ts" name="system${model.className}">
2789
2922
  // 通用消息与确认弹窗
@@ -3055,11 +3188,15 @@ function renderSingleTableDialogActions(model, permissionPrefix) {
3055
3188
  const pkAttr = model.pk.attrName;
3056
3189
  const isDictWithStatus = model.pageType === 'dict' && model.statusField;
3057
3190
  const lines = [
3191
+ ` <!-- 编辑${sanitizeHtmlComment(model.featureTitle)} -->`,
3058
3192
  ` <el-button${isDictWithStatus ? ` v-if="showDictAction('edit', row)"` : ''} icon="edit-pen" text type="primary" v-auth="'${permissionPrefix}_edit'" @click="formDialogRef.openDialog(row.${pkAttr})">{{ t('common.editBtn') }}</el-button>`,
3193
+ ` <!-- 删除${sanitizeHtmlComment(model.featureTitle)} -->`,
3059
3194
  ` <el-button${isDictWithStatus ? ` v-if="showDictAction('delete', row)"` : ''} icon="delete" text type="primary" v-auth="'${permissionPrefix}_del'" @click="handleDelete([row.${pkAttr}])">{{ t('common.delBtn') }}</el-button>`,
3060
3195
  ];
3061
3196
  if (isDictWithStatus) {
3197
+ lines.push(` <!-- 启用${sanitizeHtmlComment(model.featureTitle)} -->`);
3062
3198
  lines.push(` <el-button v-if="showDictAction('enable', row)" icon="circle-check" text type="primary" @click="handleEnable(row.${pkAttr})">{{ t('common.actions.enable') }}</el-button>`);
3199
+ lines.push(` <!-- 禁用${sanitizeHtmlComment(model.featureTitle)} -->`);
3063
3200
  lines.push(` <el-button v-if="showDictAction('disable', row)" icon="remove" text type="primary" @click="handleDisable(row.${pkAttr})">{{ t('common.actions.disable') }}</el-button>`);
3064
3201
  }
3065
3202
  return lines.join('\n');
@@ -3188,6 +3325,38 @@ function renderExtraApiFunctions(model) {
3188
3325
  .join('\n');
3189
3326
  }
3190
3327
 
3328
+ function renderApiRequestImport(model) {
3329
+ const hasExtraApis = Array.isArray(model.extraApis) && model.extraApis.length > 0;
3330
+ const hasDictStatusApis = model.pageType === 'dict' && !!model.statusField;
3331
+ return hasExtraApis || hasDictStatusApis ? "import request from '/@/utils/request';" : '';
3332
+ }
3333
+
3334
+ function renderExtraApiFunctionsV2(model) {
3335
+ if (!Array.isArray(model.extraApis) || !model.extraApis.length) return '';
3336
+ return model.extraApis
3337
+ .map((api) => {
3338
+ const requestField = api.requestType === 'data' ? 'data' : 'params';
3339
+ const lines = ['', `// 额外接口:${sanitizeComment(api.description)}`];
3340
+ if (api.usedBy) {
3341
+ lines.push(`// 使用场景:${sanitizeComment(api.usedBy)}`);
3342
+ }
3343
+ if (api.source) {
3344
+ lines.push(`// 来源说明:${sanitizeComment(api.source)}`);
3345
+ }
3346
+ lines.push(
3347
+ `export function ${api.functionName}(payload?: any) {`,
3348
+ ' return request({',
3349
+ ` url: '${api.url}',`,
3350
+ ` method: '${api.method}',`,
3351
+ ` ${requestField}: payload,`,
3352
+ ' });',
3353
+ '}'
3354
+ );
3355
+ return lines.join('\n');
3356
+ })
3357
+ .join('\n');
3358
+ }
3359
+
3191
3360
  function buildReplacements(model, sharedSupport) {
3192
3361
  const menuBaseId = Date.now();
3193
3362
  const apiModulePath = model.targetApiModule || `${model.moduleName}/${model.functionName}`;
@@ -3218,16 +3387,18 @@ function buildReplacements(model, sharedSupport) {
3218
3387
  BUSINESS_STATUS_IMPORTS: renderBusinessStatusImports(model),
3219
3388
  BUSINESS_EDIT_IF: hasBusinessBillStateEditControl(model) ? ' v-if="showEditAction(row)"' : '',
3220
3389
  BUSINESS_STATUS_HELPERS: renderBusinessStatusHelpers(model),
3221
- EXTRA_API_FUNCTIONS: renderExtraApiFunctions(model),
3390
+ API_REQUEST_IMPORT: renderApiRequestImport(model),
3391
+ EXTRA_API_FUNCTIONS: renderExtraApiFunctionsV2(model),
3222
3392
  CUSTOM_QUERY_FIELDS_EXPR: model.pageType === 'dict' ? '[]' : 'queryableDictOptions.value',
3223
3393
  SHOW_RIGHT_TOOLS: model.pageType === 'dict' ? 'false' : 'true',
3224
3394
  MENU_BASE_ID: menuBaseId,
3225
3395
  MENU_BASE_ID_PLUS_1: menuBaseId + 1,
3226
3396
  MENU_BASE_ID_PLUS_2: menuBaseId + 2,
3227
- MENU_BASE_ID_PLUS_3: menuBaseId + 3,
3228
- MENU_BASE_ID_PLUS_4: menuBaseId + 4,
3229
- MENU_BASE_ID_PLUS_5: menuBaseId + 5,
3230
- GENERATED_AT: new Date().toISOString(),
3397
+ MENU_BASE_ID_PLUS_3: menuBaseId + 3,
3398
+ MENU_BASE_ID_PLUS_4: menuBaseId + 4,
3399
+ MENU_BASE_ID_PLUS_5: menuBaseId + 5,
3400
+ MENU_BASE_ID_PLUS_6: menuBaseId + 6,
3401
+ GENERATED_AT: new Date().toISOString(),
3231
3402
  FORM_FIELDS: model.visibleFields.map(renderFormFieldV2).join('\n'),
3232
3403
  TABLE_COLUMNS: model.gridFields.map((field) => renderTableColumn(field, dictRegistryRefs)).join('\n'),
3233
3404
  FORM_DEFAULTS: renderFormDefaults(model),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worsoft-frontend-codegen-local-mcp",
3
- "version": "0.1.64",
3
+ "version": "0.1.66",
4
4
  "description": "Worsoft frontend local-template code generation MCP server.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "worsoft <sw@worsoft.vip>",