worsoft-frontend-codegen-local-mcp 0.1.84 → 0.1.88

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 (29) hide show
  1. package/README.md +49 -12
  2. package/assets/style-catalog.json +46 -0
  3. package/assets/templates/ledger_master_child_jump/api.tpl +2 -2
  4. package/assets/templates/ledger_master_child_jump/form.tpl +1 -1
  5. package/assets/templates/ledger_master_child_jump/index.tpl +50 -18
  6. package/assets/templates/ledger_master_child_jump/menu.sql.tpl +10 -7
  7. package/assets/templates/ledger_single_table_jump/api.tpl +2 -2
  8. package/assets/templates/ledger_single_table_jump/index.tpl +49 -17
  9. package/assets/templates/ledger_single_table_jump/menu.sql.tpl +10 -7
  10. package/assets/templates/master_child_jump/form.tpl +41 -0
  11. package/assets/templates/master_child_jump/index.tpl +19 -7
  12. package/assets/templates/master_child_jump/menu.sql.tpl +14 -14
  13. package/assets/templates/single_table_dialog/index.tpl +15 -3
  14. package/assets/templates/single_table_dialog/menu.sql.tpl +12 -12
  15. package/assets/templates/single_table_jump/index.tpl +19 -7
  16. package/assets/templates/single_table_jump/menu.sql.tpl +14 -14
  17. package/assets/templates/single_tree_table/api.tpl +17 -0
  18. package/assets/templates/single_tree_table/form.tpl +139 -0
  19. package/assets/templates/single_tree_table/index.tpl +203 -0
  20. package/assets/templates/single_tree_table/menu.sql.tpl +21 -0
  21. package/assets/templates/single_tree_table/options.tpl +35 -0
  22. package/assets/templates/tree_left_list/README.md +1 -0
  23. package/assets/templates/tree_left_list/api.tpl +4 -0
  24. package/assets/templates/tree_left_list/form.tpl +3 -0
  25. package/assets/templates/tree_left_list/index.tpl +4 -0
  26. package/assets/templates/tree_left_list/menu.sql.tpl +1 -0
  27. package/assets/templates/tree_left_list/options.tpl +3 -0
  28. package/mcp_server.js +1205 -507
  29. package/package.json +1 -1
package/README.md CHANGED
@@ -9,10 +9,24 @@ This MCP generates Worsoft frontend files from structured JSON metadata.
9
9
  - local template rendering
10
10
  - single-table runtime generation
11
11
  - master-child runtime generation with explicit `children[]`
12
+ - single tree runtime generation with `listByParentId`
13
+ - left-tree-right-list runtime generation with explicit `levels[]`
12
14
  - optional write-to-disk
13
15
 
14
16
  ## Current Inputs
15
17
 
18
+ Preferred input:
19
+
20
+ - `parseResultPath`
21
+ - `writeToDisk`
22
+ - `overwrite`
23
+ - `writeSupportFiles`
24
+ - `mergeI18nZh`
25
+
26
+ When `parseResultPath` is provided, MCP reads `parseResult.json` locally and uses `parseResult.mcpPayload` as the generation input. The caller should not expand `mcpPayload` in the model context.
27
+
28
+ Legacy-compatible low-level inputs:
29
+
16
30
  - `featureTitle`
17
31
  - `tableName`
18
32
  - `tableComment`
@@ -33,7 +47,28 @@ If the caller accidentally passes the `src` directory, MCP will normalize it bac
33
47
 
34
48
  ## Recommended MCP Arguments
35
49
 
36
- Single table:
50
+ Preferred:
51
+
52
+ ```json
53
+ {
54
+ "parseResultPath": "E:/own-worker-platform/trunk/docs/03-modules/.../parseResult.json",
55
+ "writeToDisk": true,
56
+ "overwrite": true
57
+ }
58
+ ```
59
+
60
+ If `parseResult.mcpPayload` does not contain `frontendPath`, pass it as a non-semantic runtime argument:
61
+
62
+ ```json
63
+ {
64
+ "parseResultPath": "E:/own-worker-platform/trunk/docs/03-modules/.../parseResult.json",
65
+ "frontendPath": "E:/own-worker-platform/trunk/worsoft-ui",
66
+ "writeToDisk": true,
67
+ "overwrite": true
68
+ }
69
+ ```
70
+
71
+ Legacy low-level single table:
37
72
 
38
73
  ```json
39
74
  {
@@ -54,7 +89,7 @@ Single table:
54
89
  }
55
90
  ```
56
91
 
57
- Master-child:
92
+ Legacy low-level master-child:
58
93
 
59
94
  ```json
60
95
  {
@@ -103,10 +138,13 @@ Node:
103
138
 
104
139
  ```bash
105
140
  node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario single
141
+ node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario parse_result_path
106
142
  node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario single_dialog
107
143
  node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario dict_single
108
144
  node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario master
109
145
  node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario dict_multi
146
+ node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario single_tree
147
+ node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario tree_left_list
110
148
  ```
111
149
 
112
150
  Optional overrides:
@@ -130,9 +168,9 @@ Optional overrides:
130
168
  - `mergeI18nZh=false`:
131
169
  MCP renders `zh-cn.ts` from current metadata only and does not parse the existing file
132
170
 
133
- ## Master-Child Contract
171
+ ## Legacy Low-Level Master-Child Contract
134
172
 
135
- For `master_child_jump`, MCP does not infer relations.
173
+ For low-level `master_child_jump` calls, MCP does not infer relations. `parseResultPath` callers should let `doc-parse` populate `parseResult.mcpPayload.children`.
136
174
 
137
175
  The caller must provide explicit `children[]`, and each child must include:
138
176
 
@@ -145,9 +183,9 @@ The caller must provide explicit `children[]`, and each child must include:
145
183
 
146
184
  If these fields are missing, MCP returns a structured validation error.
147
185
 
148
- ## Multi-Level Dictionary Contract
186
+ ## Legacy Low-Level Multi-Level Dictionary Contract
149
187
 
150
- For `multi_level_dict`, MCP does not infer hierarchy.
188
+ For low-level `multi_level_dict`, MCP does not infer hierarchy. `parseResultPath` callers should let `doc-parse` populate `parseResult.mcpPayload.levels`.
151
189
 
152
190
  The caller must provide explicit `levels[]`. Each level must include:
153
191
 
@@ -160,6 +198,10 @@ Each module must include:
160
198
  - `apiPath`
161
199
  - `fields`
162
200
 
201
+ For `tree_left_list`, level 1 is rendered as the left lazy tree and level 2 is rendered as the right linked list.
202
+ The left module may pass `treeParentField` when the parent field is not `parentId`.
203
+ When the same physical table is split into different logical regions, pass `moduleKey` to keep the UI state independent.
204
+
163
205
  ## Current Outputs
164
206
 
165
207
  - `src/views/<module>/<function>/index.vue`
@@ -174,12 +216,7 @@ Each module must include:
174
216
  - Human guide: `STYLE_TEMPLATE_GUIDE.md`
175
217
  - Machine source: `assets/style-catalog.json`
176
218
 
177
- Declared styles:
178
-
179
- - `single_table_jump`
180
- - `single_table_dialog`
181
- - `master_child_jump`
182
- - `multi_level_dict`
219
+ Declared styles are defined by `assets/style-catalog.json`; README intentionally does not duplicate the full enum as a second source.
183
220
 
184
221
  ## Notes
185
222
 
@@ -90,5 +90,51 @@
90
90
  "menuSql": "menu.sql.tpl"
91
91
  }
92
92
  }
93
+ },
94
+ "single_tree_table": {
95
+ "id": "single_tree_table",
96
+ "label": "单树形结构模板",
97
+ "description": "单表树形字典页面,使用 SchemaTreeTable 和 listByParentId 懒加载子节点。",
98
+ "templateFiles": {
99
+ "api": "api.md",
100
+ "options": "options.md",
101
+ "form": "表单V2025001.md",
102
+ "list": "树形表格V2025001.md",
103
+ "menuSql": "权限菜单V2025001.md"
104
+ },
105
+ "runtime": {
106
+ "supported": true,
107
+ "templateDir": "assets/templates/single_tree_table",
108
+ "files": {
109
+ "api": "api.tpl",
110
+ "options": "options.tpl",
111
+ "form": "form.tpl",
112
+ "list": "index.tpl",
113
+ "menuSql": "menu.sql.tpl"
114
+ }
115
+ }
116
+ },
117
+ "tree_left_list": {
118
+ "id": "tree_left_list",
119
+ "label": "左树右列表模板",
120
+ "description": "左侧树形表格懒加载,右侧列表根据左侧当前选中节点联动查询。",
121
+ "templateFiles": {
122
+ "api": "api.md",
123
+ "options": "options.md",
124
+ "form": "表单V2025001.md",
125
+ "list": "左树右列表V2025001.md",
126
+ "menuSql": "权限菜单V2025001.md"
127
+ },
128
+ "runtime": {
129
+ "supported": true,
130
+ "templateDir": "assets/templates/tree_left_list",
131
+ "files": {
132
+ "api": "api.tpl",
133
+ "options": "options.tpl",
134
+ "form": "form.tpl",
135
+ "list": "index.tpl",
136
+ "menuSql": "menu.sql.tpl"
137
+ }
138
+ }
93
139
  }
94
140
  }
@@ -2,6 +2,6 @@
2
2
  import { createCrudApi } from '/@/api/common/crudFactory';
3
3
  {{API_REQUEST_IMPORT}}
4
4
 
5
- // {{FEATURE_TITLE}}主子表台账只读接口:分页查询、详情查询
6
- export const { fetchList, getObj } = createCrudApi('/{{API_PATH}}');
5
+ // {{FEATURE_TITLE}}主子表台账只读接口:分页查询、详情查询、导出
6
+ export const { fetchList, getObj, exportObj } = createCrudApi('/{{API_PATH}}');
7
7
  {{EXTRA_API_FUNCTIONS}}
@@ -47,7 +47,7 @@ import { allDictTypes, childFieldGroups, dataMasterEntity } from './options';
47
47
  const dictRefs = useDict(...allDictTypes);
48
48
  // 国际化方法
49
49
  const { t } = useI18n();
50
- // 子表表格组件:仅用于只读展示,模板中 hide-add/hide-delete 固定启用
50
+ // 子表表格组件:模板默认保留新增/删除按钮,后续可由任务拆分按业务规则移除
51
51
  const scFormTable = defineAsyncComponent(() => import('/@/components/FormTable/index.vue'));
52
52
  // 当前路由信息
53
53
  const route = useRoute();
@@ -1,28 +1,32 @@
1
+ <!-- 功能名称:{{FEATURE_TITLE}} -->
1
2
  <template>
2
- <!-- 页面布局:{{FEATURE_TITLE}}主子表台账列表页,只提供查询和查看能力 -->
3
+ <!-- 页面布局:{{FEATURE_TITLE}}主子表台账列表页 -->
3
4
  <div class="layout-padding">
4
5
  <div class="layout-padding-auto layout-padding-view flex h-full flex-col">
5
- <!-- 台账工具栏:保留关键字查询、重置、高级查询和刷新,隐藏新增、导入、删除、导出 -->
6
+ <!-- 列表工具栏:搜索、重置、高级查询、导出和刷新,台账页不提供新增、导入、删除入口 -->
6
7
  <SchemaListToolbar
7
8
  v-bind="toolbarProps"
8
9
  @update:keyword="state.queryForm.smartVal = $event"
9
10
  @query="handleToolbarQuery"
10
11
  @reset="handleToolbarReset"
11
12
  @custom-query-confirm="handleToolbarCustomQueryConfirm"
13
+ @export="handleToolbarExport"
12
14
  @refresh="handleToolbarRefresh"
13
15
  />
14
16
 
15
- <!-- 台账列表表格:展示主表列表数据和行内查看按钮,不提供勾选批量操作 -->
17
+ <!-- 列表表格:展示{{FEATURE_TITLE}}主表台账数据、分页、排序和行内查看操作 -->
16
18
  <SchemaListTable
17
19
  v-bind="tableProps"
18
20
  row-id-key="{{PK_ATTR}}"
19
21
  :action-column-width="100"
22
+ @row-current-change="handleTableRowCurrentChange"
20
23
  @sort-change="handleTableSortChange"
21
24
  @size-change="handleTableSizeChange"
22
25
  @current-change="handleTableCurrentChange"
23
26
  >
24
- <!-- 行操作按钮:主子表台账只允许查看详情 -->
27
+ <!-- 行操作按钮:主子表台账页只允许查看详情 -->
25
28
  <template #actions="{ row }">
29
+ <!-- 查看{{FEATURE_TITLE}}主子表台账详情 -->
26
30
  <el-button text type="primary" icon="view" v-auth="'{{PERMISSION_PREFIX}}_view'" @click="handleDetail(row.{{PK_ATTR}})">{{ t('common.viewBtn') }}</el-button>
27
31
  </template>
28
32
  </SchemaListTable>
@@ -31,13 +35,15 @@
31
35
  </template>
32
36
 
33
37
  <script setup lang="ts" name="system{{CLASS_NAME}}">
34
- // 台账列表接口:只消费分页查询能力
35
- import { fetchList } from '/@/api/{{API_MODULE_PATH}}';
38
+ // 列表接口:台账页只消费分页查询能力
39
+ import { fetchList, exportObj } from '/@/api/{{API_MODULE_PATH}}';
36
40
  // 字典数据加载
37
41
  import { useDict } from '/@/hooks/dict';
38
- // 列表字段元数据能力
42
+ // 列表页字段元数据能力
39
43
  import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
40
- // 列表页查询、分页、排序状态管理
44
+ // 列表当前行选中保持能力
45
+ import { useTableCurrentRow } from '/@/hooks/useTableCurrentRow';
46
+ // 列表页查询、分页和排序状态管理
41
47
  import { useSchemaListQuery } from '/@/hooks/useSchemaListQuery';
42
48
  // 统一列表工具栏组件
43
49
  import SchemaListToolbar from '/@/components/schema-list/SchemaListToolbar.vue';
@@ -54,19 +60,27 @@ const dictRefs = useDict(...allDictTypes);
54
60
  const { t } = useI18n();
55
61
  // 路由跳转能力
56
62
  const router = useRouter();
63
+ // 当前高亮行
57
64
 
58
- // 统一管理台账列表查询、分页和排序
59
- const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle, resetQueryForm } = useSchemaListQuery({
65
+ // 统一管理主子表台账列表查询、分页、排序和导出
66
+ const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle, exportExcel: runExportExcel, resetQueryForm } = useSchemaListQuery({
60
67
  schema: crudSchema,
61
68
  pageList: fetchList,
62
- exportUrl: '/{{API_PATH}}/export',
63
- exportFileName: '{{FUNCTION_NAME}}.xlsx',
69
+ exportApi: exportObj,
70
+ exportFileName: '{{FEATURE_TITLE}}.xlsx',
71
+ exportColumns: () => tableColumns.value,
64
72
  });
65
73
 
74
+ // 统一处理台账列表加载后的首行选中,以及刷新后的当前行保持
75
+ const { currentRowKey, handleCurrentChange } = useTableCurrentRow(
76
+ computed(() => state.dataList),
77
+ '{{PK_ATTR}}'
78
+ );
79
+
66
80
  // 提供字段标签、字典选项和查询区描述
67
81
  const { resolveLabel, getDictOptions, visibleTableColumns, searchKeywordTooltip, queryableDictFields } = useCrudPageMeta(dataMasterEntity, dictRefs);
68
82
 
69
- // 高级查询字典字段配置
83
+ // 自定义查询区的字典字段配置
70
84
  const queryableDictOptions = computed(() =>
71
85
  queryableDictFields.value.map((field) => ({
72
86
  ...field,
@@ -85,7 +99,7 @@ const tableColumns = computed(() =>
85
99
  }))
86
100
  );
87
101
 
88
- // 台账工具栏透传属性:显式关闭新增、导入、删除和导出入口
102
+ // 工具栏透传属性:显式关闭新增、导入、删除入口,保留导出
89
103
  const toolbarProps = computed(() => ({
90
104
  keyword: state.queryForm.smartVal,
91
105
  searchPlaceholder: searchKeywordTooltip.value,
@@ -95,10 +109,10 @@ const toolbarProps = computed(() => ({
95
109
  showAdd: false,
96
110
  showImport: false,
97
111
  showDelete: false,
98
- exportPermission: false,
112
+ exportPermission: '{{PERMISSION_PREFIX}}_export',
99
113
  }));
100
114
 
101
- // 台账表格透传属性:关闭勾选列,仅保留查看操作列
115
+ // 表格透传属性:关闭勾选列,仅保留查看操作列和当前行高亮
102
116
  const tableProps = computed(() => ({
103
117
  data: state.dataList,
104
118
  loading: !!state.loading,
@@ -106,16 +120,24 @@ const tableProps = computed(() => ({
106
120
  pagination: state.pagination,
107
121
  tableStyle,
108
122
  showSelection: false,
123
+ currentRowKey: currentRowKey.value,
124
+ highlightCurrentRow: true,
109
125
  }));
110
126
 
111
- // 详情页路由路径
127
+ // 列表刷新后恢复当前行:优先恢复原选中行,不存在时选中第一行
128
+ // 表单页路由路径
112
129
  const getFormPath = () => '/{{VIEW_MODULE_PATH}}/form';
113
130
 
114
- // 跳转台账详情页,固定 detail=1
131
+ // 跳转主子表台账详情页,固定 detail=1
115
132
  const handleDetail = (id: string) => {
116
133
  router.push({ path: getFormPath(), query: { id, detail: '1', tagsViewName: t('common.viewBtn') } });
117
134
  };
118
135
 
136
+ // 导出台账数据
137
+ const exportExcel = () => {
138
+ runExportExcel([]);
139
+ };
140
+
119
141
  // 应用高级查询条件
120
142
  const handleQueryFilterConfirm = (values: Record<string, any>) => {
121
143
  queryableDictFields.value.forEach((field) => {
@@ -150,11 +172,21 @@ const handleToolbarCustomQueryConfirm = (payload: { values: Record<string, any>
150
172
  handleQueryFilterConfirm(payload.values);
151
173
  };
152
174
 
175
+ // 工具栏导出事件
176
+ const handleToolbarExport = () => {
177
+ exportExcel();
178
+ };
179
+
153
180
  // 工具栏刷新事件
154
181
  const handleToolbarRefresh = () => {
155
182
  getDataList();
156
183
  };
157
184
 
185
+ // 表格当前行变化事件
186
+ const handleTableRowCurrentChange = (payload: { row: any }) => {
187
+ handleCurrentChange(payload);
188
+ };
189
+
158
190
  // 表格排序事件
159
191
  const handleTableSortChange = (payload: { raw: any }) => {
160
192
  sortChangeHandle(payload.raw);
@@ -3,13 +3,16 @@
3
3
  -- Generated by worsoft-codegen-local on {{GENERATED_AT}}
4
4
 
5
5
  -- 主子表台账列表菜单:只读台账入口
6
- insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
7
- values ({{MENU_BASE_ID}}, '-1', '/{{MENU_ROUTE_PATH}}/index', '', '0', 'icon-bangzhushouji', '0', null, '8', null, '{{TABLE_COMMENT}}管理', 1);
6
+ insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name, tenant_id, bill_code)
7
+ values ({{MENU_BASE_ID}}, '-1', '/{{MENU_ROUTE_PATH}}/index', '', '0', 'icon-bangzhushouji', '0', null, '8', null, '{{TABLE_COMMENT}}管理', 1, '{{BILL_CODE}}');
8
8
 
9
9
  -- 主子表台账详情路由:隐藏菜单,用于列表页查看跳转
10
- 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)
11
- values ({{MENU_BASE_ID_PLUS_6}}, '-1', '/{{MENU_ROUTE_PATH}}/form', '', '0', 'icon-biaodan', '0', '0', null, '9', null, '{{TABLE_COMMENT}}详情', 1);
10
+ 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, bill_code)
11
+ values ({{MENU_BASE_ID_PLUS_6}}, '-1', '/{{MENU_ROUTE_PATH}}/form', '', '0', 'icon-biaodan', '0', '0', null, '9', null, '{{TABLE_COMMENT}}详情', 1, '{{BILL_CODE}}');
12
12
 
13
- -- 台账查看权限:不生成新增、编辑、删除等操作权限
14
- insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
15
- values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1);
13
+ -- 台账操作权限:只生成查看、导出权限,不生成新增、编辑、删除等操作权限
14
+ insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id, bill_code)
15
+ values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1, '{{BILL_CODE}}');
16
+
17
+ insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id, bill_code)
18
+ values ({{MENU_BASE_ID_PLUS_5}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_export', '1', null, '1', '0', null, '4', null, '导入导出', 1, '{{BILL_CODE}}');
@@ -2,6 +2,6 @@
2
2
  import { createCrudApi } from '/@/api/common/crudFactory';
3
3
  {{API_REQUEST_IMPORT}}
4
4
 
5
- // {{FEATURE_TITLE}}台账只读接口:分页查询、详情查询
6
- export const { fetchList, getObj } = createCrudApi('/{{API_PATH}}');
5
+ // {{FEATURE_TITLE}}台账只读接口:分页查询、详情查询、导出
6
+ export const { fetchList, getObj, exportObj } = createCrudApi('/{{API_PATH}}');
7
7
  {{EXTRA_API_FUNCTIONS}}
@@ -1,28 +1,32 @@
1
+ <!-- 功能名称:{{FEATURE_TITLE}} -->
1
2
  <template>
2
- <!-- 页面布局:{{FEATURE_TITLE}}台账列表页,只提供查询和查看能力 -->
3
+ <!-- 页面布局:{{FEATURE_TITLE}}台账列表页 -->
3
4
  <div class="layout-padding">
4
5
  <div class="layout-padding-auto layout-padding-view flex h-full flex-col">
5
- <!-- 台账工具栏:保留关键字查询、重置、高级查询和刷新,隐藏新增、导入、删除、导出 -->
6
+ <!-- 列表工具栏:搜索、重置、高级查询、导出和刷新,台账页不提供新增、导入、删除入口 -->
6
7
  <SchemaListToolbar
7
8
  v-bind="toolbarProps"
8
9
  @update:keyword="state.queryForm.smartVal = $event"
9
10
  @query="handleToolbarQuery"
10
11
  @reset="handleToolbarReset"
11
12
  @custom-query-confirm="handleToolbarCustomQueryConfirm"
13
+ @export="handleToolbarExport"
12
14
  @refresh="handleToolbarRefresh"
13
15
  />
14
16
 
15
- <!-- 台账列表表格:展示列表数据和行内查看按钮,不提供勾选批量操作 -->
17
+ <!-- 列表表格:展示{{FEATURE_TITLE}}台账数据、分页、排序和行内查看操作 -->
16
18
  <SchemaListTable
17
19
  v-bind="tableProps"
18
20
  row-id-key="{{PK_ATTR}}"
19
21
  :action-column-width="100"
22
+ @row-current-change="handleTableRowCurrentChange"
20
23
  @sort-change="handleTableSortChange"
21
24
  @size-change="handleTableSizeChange"
22
25
  @current-change="handleTableCurrentChange"
23
26
  >
24
- <!-- 行操作按钮:台账只允许查看详情 -->
27
+ <!-- 行操作按钮:台账页只允许查看详情 -->
25
28
  <template #actions="{ row }">
29
+ <!-- 查看{{FEATURE_TITLE}}台账详情 -->
26
30
  <el-button text type="primary" icon="view" v-auth="'{{PERMISSION_PREFIX}}_view'" @click="handleDetail(row.{{PK_ATTR}})">{{ t('common.viewBtn') }}</el-button>
27
31
  </template>
28
32
  </SchemaListTable>
@@ -31,13 +35,15 @@
31
35
  </template>
32
36
 
33
37
  <script setup lang="ts" name="system{{CLASS_NAME}}">
34
- // 台账列表接口:只消费分页查询能力
35
- import { fetchList } from '/@/api/{{API_MODULE_PATH}}';
38
+ // 列表接口:台账页只消费分页查询能力
39
+ import { fetchList, exportObj } from '/@/api/{{API_MODULE_PATH}}';
36
40
  // 字典数据加载
37
41
  import { useDict } from '/@/hooks/dict';
38
- // 列表字段元数据能力
42
+ // 列表页字段元数据能力
39
43
  import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
40
- // 列表页查询、分页、排序状态管理
44
+ // 列表当前行选中保持能力
45
+ import { useTableCurrentRow } from '/@/hooks/useTableCurrentRow';
46
+ // 列表页查询、分页和排序状态管理
41
47
  import { useSchemaListQuery } from '/@/hooks/useSchemaListQuery';
42
48
  // 统一列表工具栏组件
43
49
  import SchemaListToolbar from '/@/components/schema-list/SchemaListToolbar.vue';
@@ -54,19 +60,27 @@ const dictRefs = useDict(...allDictTypes);
54
60
  const { t } = useI18n();
55
61
  // 路由跳转能力
56
62
  const router = useRouter();
63
+ // 当前高亮行
57
64
 
58
- // 统一管理台账列表查询、分页和排序
59
- const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle, resetQueryForm } = useSchemaListQuery({
65
+ // 统一管理台账列表查询、分页、排序和导出
66
+ const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle, exportExcel: runExportExcel, resetQueryForm } = useSchemaListQuery({
60
67
  schema: crudSchema,
61
68
  pageList: fetchList,
62
- exportUrl: '/{{API_PATH}}/export',
63
- exportFileName: '{{FUNCTION_NAME}}.xlsx',
69
+ exportApi: exportObj,
70
+ exportFileName: '{{FEATURE_TITLE}}.xlsx',
71
+ exportColumns: () => tableColumns.value,
64
72
  });
65
73
 
74
+ // 统一处理台账列表加载后的首行选中,以及刷新后的当前行保持
75
+ const { currentRowKey, handleCurrentChange } = useTableCurrentRow(
76
+ computed(() => state.dataList),
77
+ '{{PK_ATTR}}'
78
+ );
79
+
66
80
  // 提供字段标签、字典选项和查询区描述
67
81
  const { resolveLabel, getDictOptions, visibleTableColumns, searchKeywordTooltip, queryableDictFields } = useCrudPageMeta(dataMasterEntity, dictRefs);
68
82
 
69
- // 高级查询字典字段配置
83
+ // 自定义查询区的字典字段配置
70
84
  const queryableDictOptions = computed(() =>
71
85
  queryableDictFields.value.map((field) => ({
72
86
  ...field,
@@ -85,7 +99,7 @@ const tableColumns = computed(() =>
85
99
  }))
86
100
  );
87
101
 
88
- // 台账工具栏透传属性:显式关闭新增、导入、删除和导出入口
102
+ // 工具栏透传属性:显式关闭新增、导入、删除入口,保留导出
89
103
  const toolbarProps = computed(() => ({
90
104
  keyword: state.queryForm.smartVal,
91
105
  searchPlaceholder: searchKeywordTooltip.value,
@@ -95,10 +109,10 @@ const toolbarProps = computed(() => ({
95
109
  showAdd: false,
96
110
  showImport: false,
97
111
  showDelete: false,
98
- exportPermission: false,
112
+ exportPermission: '{{PERMISSION_PREFIX}}_export',
99
113
  }));
100
114
 
101
- // 台账表格透传属性:关闭勾选列,仅保留查看操作列
115
+ // 表格透传属性:关闭勾选列,仅保留查看操作列和当前行高亮
102
116
  const tableProps = computed(() => ({
103
117
  data: state.dataList,
104
118
  loading: !!state.loading,
@@ -106,9 +120,12 @@ const tableProps = computed(() => ({
106
120
  pagination: state.pagination,
107
121
  tableStyle,
108
122
  showSelection: false,
123
+ currentRowKey: currentRowKey.value,
124
+ highlightCurrentRow: true,
109
125
  }));
110
126
 
111
- // 详情页路由路径
127
+ // 列表刷新后恢复当前行:优先恢复原选中行,不存在时选中第一行
128
+ // 表单页路由路径
112
129
  const getFormPath = () => '/{{VIEW_MODULE_PATH}}/form';
113
130
 
114
131
  // 跳转台账详情页,固定 detail=1
@@ -116,6 +133,11 @@ const handleDetail = (id: string) => {
116
133
  router.push({ path: getFormPath(), query: { id, detail: '1', tagsViewName: t('common.viewBtn') } });
117
134
  };
118
135
 
136
+ // 导出台账数据
137
+ const exportExcel = () => {
138
+ runExportExcel([]);
139
+ };
140
+
119
141
  // 应用高级查询条件
120
142
  const handleQueryFilterConfirm = (values: Record<string, any>) => {
121
143
  queryableDictFields.value.forEach((field) => {
@@ -150,11 +172,21 @@ const handleToolbarCustomQueryConfirm = (payload: { values: Record<string, any>
150
172
  handleQueryFilterConfirm(payload.values);
151
173
  };
152
174
 
175
+ // 工具栏导出事件
176
+ const handleToolbarExport = () => {
177
+ exportExcel();
178
+ };
179
+
153
180
  // 工具栏刷新事件
154
181
  const handleToolbarRefresh = () => {
155
182
  getDataList();
156
183
  };
157
184
 
185
+ // 表格当前行变化事件
186
+ const handleTableRowCurrentChange = (payload: { row: any }) => {
187
+ handleCurrentChange(payload);
188
+ };
189
+
158
190
  // 表格排序事件
159
191
  const handleTableSortChange = (payload: { raw: any }) => {
160
192
  sortChangeHandle(payload.raw);
@@ -3,13 +3,16 @@
3
3
  -- Generated by worsoft-codegen-local on {{GENERATED_AT}}
4
4
 
5
5
  -- 台账列表菜单:只读台账入口
6
- insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
7
- values ({{MENU_BASE_ID}}, '-1', '/{{MENU_ROUTE_PATH}}/index', '', '0', 'icon-bangzhushouji', '0', null, '8', null, '{{TABLE_COMMENT}}管理', 1);
6
+ insert into sys_menu (menu_id, parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name, tenant_id, bill_code)
7
+ values ({{MENU_BASE_ID}}, '-1', '/{{MENU_ROUTE_PATH}}/index', '', '0', 'icon-bangzhushouji', '0', null, '8', null, '{{TABLE_COMMENT}}管理', 1, '{{BILL_CODE}}');
8
8
 
9
9
  -- 台账详情路由:隐藏菜单,用于列表页查看跳转
10
- 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)
11
- values ({{MENU_BASE_ID_PLUS_6}}, '-1', '/{{MENU_ROUTE_PATH}}/form', '', '0', 'icon-biaodan', '0', '0', null, '9', null, '{{TABLE_COMMENT}}详情', 1);
10
+ 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, bill_code)
11
+ values ({{MENU_BASE_ID_PLUS_6}}, '-1', '/{{MENU_ROUTE_PATH}}/form', '', '0', 'icon-biaodan', '0', '0', null, '9', null, '{{TABLE_COMMENT}}详情', 1, '{{BILL_CODE}}');
12
12
 
13
- -- 台账查看权限:不生成新增、编辑、删除等操作权限
14
- insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
15
- values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1);
13
+ -- 台账操作权限:只生成查看、导出权限,不生成新增、编辑、删除等操作权限
14
+ insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id, bill_code)
15
+ values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1, '{{BILL_CODE}}');
16
+
17
+ insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id, bill_code)
18
+ values ({{MENU_BASE_ID_PLUS_5}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_export', '1', null, '1', '0', null, '4', null, '导入导出', 1, '{{BILL_CODE}}');
@@ -131,6 +131,10 @@ const resetFormState = () => {
131
131
  nextTick(() => {
132
132
  dataFormRef.value?.resetFields();
133
133
  {{CHILD_RESET_LISTS}}
134
+ Object.keys(selectedChildRowsMap).forEach((key) => {
135
+ selectedChildRowsMap[key] = [];
136
+ childTableRefMap[key]?.clearSelection?.();
137
+ });
134
138
  });
135
139
  };
136
140
 
@@ -188,6 +192,43 @@ const deleteChild = (_obj: Record<string, any>, _childPkAttr: string) => {
188
192
  };
189
193
 
190
194
  // 页面挂载后初始化数据
195
+ // 子表表格引用集合,用于外部新增按钮调用表格新增方法
196
+ const childTableRefMap = reactive<Record<string, any>>({});
197
+ // 子表勾选行集合,用于外部删除按钮删除已选明细
198
+ const selectedChildRowsMap = reactive<Record<string, Record<string, any>[]>>({});
199
+
200
+ const setChildTableRef = (childKey: string, el: any) => {
201
+ if (el) childTableRefMap[childKey] = el;
202
+ };
203
+
204
+ const getSelectedChildRows = (childKey: string) => selectedChildRowsMap[childKey] || [];
205
+
206
+ const handleChildSelectionChange = (childKey: string, rows: Record<string, any>[]) => {
207
+ selectedChildRowsMap[childKey] = rows || [];
208
+ };
209
+
210
+ // 通过子表外部工具按钮新增一行,表格内部不显示新增入口
211
+ const handleAddChild = (childKey: string) => {
212
+ if (detail.value) return;
213
+ childTableRefMap[childKey]?.pushRow?.();
214
+ };
215
+
216
+ // 通过子表外部工具按钮删除已勾选行,表格内部不显示删除入口
217
+ const handleDeleteSelectedChild = (childKey: string, childPkAttr: string) => {
218
+ if (detail.value) return;
219
+
220
+ const selectedRows = getSelectedChildRows(childKey);
221
+ if (!selectedRows.length) return;
222
+
223
+ const selectedSet = new Set(selectedRows);
224
+ const childRows = ((form as Record<string, any>)[childKey] || []) as Record<string, any>[];
225
+ const deletedRows = childRows.filter((row) => selectedSet.has(row));
226
+ (form as Record<string, any>)[childKey] = childRows.filter((row) => !selectedSet.has(row));
227
+ selectedChildRowsMap[childKey] = [];
228
+ childTableRefMap[childKey]?.clearSelection?.();
229
+ deletedRows.forEach((row) => deleteChild(row, childPkAttr));
230
+ };
231
+
191
232
  onMounted(() => {
192
233
  initPage();
193
234
  });