worsoft-frontend-codegen-local-mcp 0.1.83 → 0.1.85

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 (27) hide show
  1. package/README.md +10 -0
  2. package/assets/style-catalog.json +46 -0
  3. package/assets/templates/ledger_master_child_jump/form.tpl +1 -1
  4. package/assets/templates/ledger_master_child_jump/index.tpl +46 -15
  5. package/assets/templates/ledger_master_child_jump/menu.sql.tpl +10 -7
  6. package/assets/templates/ledger_single_table_jump/index.tpl +45 -14
  7. package/assets/templates/ledger_single_table_jump/menu.sql.tpl +10 -7
  8. package/assets/templates/master_child_jump/form.tpl +41 -0
  9. package/assets/templates/master_child_jump/index.tpl +15 -4
  10. package/assets/templates/master_child_jump/menu.sql.tpl +14 -14
  11. package/assets/templates/single_table_dialog/index.tpl +11 -0
  12. package/assets/templates/single_table_dialog/menu.sql.tpl +12 -12
  13. package/assets/templates/single_table_jump/index.tpl +15 -4
  14. package/assets/templates/single_table_jump/menu.sql.tpl +14 -14
  15. package/assets/templates/single_tree_table/api.tpl +17 -0
  16. package/assets/templates/single_tree_table/form.tpl +139 -0
  17. package/assets/templates/single_tree_table/index.tpl +203 -0
  18. package/assets/templates/single_tree_table/menu.sql.tpl +21 -0
  19. package/assets/templates/single_tree_table/options.tpl +35 -0
  20. package/assets/templates/tree_left_list/README.md +1 -0
  21. package/assets/templates/tree_left_list/api.tpl +4 -0
  22. package/assets/templates/tree_left_list/form.tpl +3 -0
  23. package/assets/templates/tree_left_list/index.tpl +4 -0
  24. package/assets/templates/tree_left_list/menu.sql.tpl +1 -0
  25. package/assets/templates/tree_left_list/options.tpl +3 -0
  26. package/mcp_server.js +1078 -464
  27. package/package.json +1 -1
package/README.md CHANGED
@@ -9,6 +9,8 @@ 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
@@ -107,6 +109,8 @@ node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario single_dialog
107
109
  node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario dict_single
108
110
  node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario master
109
111
  node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario dict_multi
112
+ node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario single_tree
113
+ node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario tree_left_list
110
114
  ```
111
115
 
112
116
  Optional overrides:
@@ -160,6 +164,10 @@ Each module must include:
160
164
  - `apiPath`
161
165
  - `fields`
162
166
 
167
+ For `tree_left_list`, level 1 is rendered as the left lazy tree and level 2 is rendered as the right linked list.
168
+ The left module may pass `treeParentField` when the parent field is not `parentId`.
169
+ When the same physical table is split into different logical regions, pass `moduleKey` to keep the UI state independent.
170
+
163
171
  ## Current Outputs
164
172
 
165
173
  - `src/views/<module>/<function>/index.vue`
@@ -180,6 +188,8 @@ Declared styles:
180
188
  - `single_table_dialog`
181
189
  - `master_child_jump`
182
190
  - `multi_level_dict`
191
+ - `single_tree_table`
192
+ - `tree_left_list`
183
193
 
184
194
  ## Notes
185
195
 
@@ -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
  }
@@ -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
- // 台账列表接口:只消费分页查询能力
38
+ // 列表接口:台账页只消费分页查询能力
35
39
  import { fetchList } 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,26 @@ 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
69
  exportUrl: '/{{API_PATH}}/export',
63
70
  exportFileName: '{{FUNCTION_NAME}}.xlsx',
64
71
  });
65
72
 
73
+ // 统一处理台账列表加载后的首行选中,以及刷新后的当前行保持
74
+ const { currentRowKey, handleCurrentChange } = useTableCurrentRow(
75
+ computed(() => state.dataList),
76
+ '{{PK_ATTR}}'
77
+ );
78
+
66
79
  // 提供字段标签、字典选项和查询区描述
67
80
  const { resolveLabel, getDictOptions, visibleTableColumns, searchKeywordTooltip, queryableDictFields } = useCrudPageMeta(dataMasterEntity, dictRefs);
68
81
 
69
- // 高级查询字典字段配置
82
+ // 自定义查询区的字典字段配置
70
83
  const queryableDictOptions = computed(() =>
71
84
  queryableDictFields.value.map((field) => ({
72
85
  ...field,
@@ -85,7 +98,7 @@ const tableColumns = computed(() =>
85
98
  }))
86
99
  );
87
100
 
88
- // 台账工具栏透传属性:显式关闭新增、导入、删除和导出入口
101
+ // 工具栏透传属性:显式关闭新增、导入、删除入口,保留导出
89
102
  const toolbarProps = computed(() => ({
90
103
  keyword: state.queryForm.smartVal,
91
104
  searchPlaceholder: searchKeywordTooltip.value,
@@ -95,10 +108,10 @@ const toolbarProps = computed(() => ({
95
108
  showAdd: false,
96
109
  showImport: false,
97
110
  showDelete: false,
98
- exportPermission: false,
111
+ exportPermission: '{{PERMISSION_PREFIX}}_export',
99
112
  }));
100
113
 
101
- // 台账表格透传属性:关闭勾选列,仅保留查看操作列
114
+ // 表格透传属性:关闭勾选列,仅保留查看操作列和当前行高亮
102
115
  const tableProps = computed(() => ({
103
116
  data: state.dataList,
104
117
  loading: !!state.loading,
@@ -106,16 +119,24 @@ const tableProps = computed(() => ({
106
119
  pagination: state.pagination,
107
120
  tableStyle,
108
121
  showSelection: false,
122
+ currentRowKey: currentRowKey.value,
123
+ highlightCurrentRow: true,
109
124
  }));
110
125
 
111
- // 详情页路由路径
126
+ // 列表刷新后恢复当前行:优先恢复原选中行,不存在时选中第一行
127
+ // 表单页路由路径
112
128
  const getFormPath = () => '/{{VIEW_MODULE_PATH}}/form';
113
129
 
114
- // 跳转台账详情页,固定 detail=1
130
+ // 跳转主子表台账详情页,固定 detail=1
115
131
  const handleDetail = (id: string) => {
116
132
  router.push({ path: getFormPath(), query: { id, detail: '1', tagsViewName: t('common.viewBtn') } });
117
133
  };
118
134
 
135
+ // 导出台账数据
136
+ const exportExcel = () => {
137
+ runExportExcel([]);
138
+ };
139
+
119
140
  // 应用高级查询条件
120
141
  const handleQueryFilterConfirm = (values: Record<string, any>) => {
121
142
  queryableDictFields.value.forEach((field) => {
@@ -150,11 +171,21 @@ const handleToolbarCustomQueryConfirm = (payload: { values: Record<string, any>
150
171
  handleQueryFilterConfirm(payload.values);
151
172
  };
152
173
 
174
+ // 工具栏导出事件
175
+ const handleToolbarExport = () => {
176
+ exportExcel();
177
+ };
178
+
153
179
  // 工具栏刷新事件
154
180
  const handleToolbarRefresh = () => {
155
181
  getDataList();
156
182
  };
157
183
 
184
+ // 表格当前行变化事件
185
+ const handleTableRowCurrentChange = (payload: { row: any }) => {
186
+ handleCurrentChange(payload);
187
+ };
188
+
158
189
  // 表格排序事件
159
190
  const handleTableSortChange = (payload: { raw: any }) => {
160
191
  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}}');
@@ -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
- // 台账列表接口:只消费分页查询能力
38
+ // 列表接口:台账页只消费分页查询能力
35
39
  import { fetchList } 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,26 @@ 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
69
  exportUrl: '/{{API_PATH}}/export',
63
70
  exportFileName: '{{FUNCTION_NAME}}.xlsx',
64
71
  });
65
72
 
73
+ // 统一处理台账列表加载后的首行选中,以及刷新后的当前行保持
74
+ const { currentRowKey, handleCurrentChange } = useTableCurrentRow(
75
+ computed(() => state.dataList),
76
+ '{{PK_ATTR}}'
77
+ );
78
+
66
79
  // 提供字段标签、字典选项和查询区描述
67
80
  const { resolveLabel, getDictOptions, visibleTableColumns, searchKeywordTooltip, queryableDictFields } = useCrudPageMeta(dataMasterEntity, dictRefs);
68
81
 
69
- // 高级查询字典字段配置
82
+ // 自定义查询区的字典字段配置
70
83
  const queryableDictOptions = computed(() =>
71
84
  queryableDictFields.value.map((field) => ({
72
85
  ...field,
@@ -85,7 +98,7 @@ const tableColumns = computed(() =>
85
98
  }))
86
99
  );
87
100
 
88
- // 台账工具栏透传属性:显式关闭新增、导入、删除和导出入口
101
+ // 工具栏透传属性:显式关闭新增、导入、删除入口,保留导出
89
102
  const toolbarProps = computed(() => ({
90
103
  keyword: state.queryForm.smartVal,
91
104
  searchPlaceholder: searchKeywordTooltip.value,
@@ -95,10 +108,10 @@ const toolbarProps = computed(() => ({
95
108
  showAdd: false,
96
109
  showImport: false,
97
110
  showDelete: false,
98
- exportPermission: false,
111
+ exportPermission: '{{PERMISSION_PREFIX}}_export',
99
112
  }));
100
113
 
101
- // 台账表格透传属性:关闭勾选列,仅保留查看操作列
114
+ // 表格透传属性:关闭勾选列,仅保留查看操作列和当前行高亮
102
115
  const tableProps = computed(() => ({
103
116
  data: state.dataList,
104
117
  loading: !!state.loading,
@@ -106,9 +119,12 @@ const tableProps = computed(() => ({
106
119
  pagination: state.pagination,
107
120
  tableStyle,
108
121
  showSelection: false,
122
+ currentRowKey: currentRowKey.value,
123
+ highlightCurrentRow: true,
109
124
  }));
110
125
 
111
- // 详情页路由路径
126
+ // 列表刷新后恢复当前行:优先恢复原选中行,不存在时选中第一行
127
+ // 表单页路由路径
112
128
  const getFormPath = () => '/{{VIEW_MODULE_PATH}}/form';
113
129
 
114
130
  // 跳转台账详情页,固定 detail=1
@@ -116,6 +132,11 @@ const handleDetail = (id: string) => {
116
132
  router.push({ path: getFormPath(), query: { id, detail: '1', tagsViewName: t('common.viewBtn') } });
117
133
  };
118
134
 
135
+ // 导出台账数据
136
+ const exportExcel = () => {
137
+ runExportExcel([]);
138
+ };
139
+
119
140
  // 应用高级查询条件
120
141
  const handleQueryFilterConfirm = (values: Record<string, any>) => {
121
142
  queryableDictFields.value.forEach((field) => {
@@ -150,11 +171,21 @@ const handleToolbarCustomQueryConfirm = (payload: { values: Record<string, any>
150
171
  handleQueryFilterConfirm(payload.values);
151
172
  };
152
173
 
174
+ // 工具栏导出事件
175
+ const handleToolbarExport = () => {
176
+ exportExcel();
177
+ };
178
+
153
179
  // 工具栏刷新事件
154
180
  const handleToolbarRefresh = () => {
155
181
  getDataList();
156
182
  };
157
183
 
184
+ // 表格当前行变化事件
185
+ const handleTableRowCurrentChange = (payload: { row: any }) => {
186
+ handleCurrentChange(payload);
187
+ };
188
+
158
189
  // 表格排序事件
159
190
  const handleTableSortChange = (payload: { raw: any }) => {
160
191
  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
  });
@@ -22,6 +22,7 @@
22
22
  v-bind="tableProps"
23
23
  row-id-key="{{PK_ATTR}}"
24
24
  @selection-change="handleTableSelectionChange"
25
+ @row-current-change="handleTableRowCurrentChange"
25
26
  @sort-change="handleTableSortChange"
26
27
  @size-change="handleTableSizeChange"
27
28
  @current-change="handleTableCurrentChange"
@@ -32,10 +33,10 @@
32
33
  <el-button text type="primary" icon="view" v-auth="'{{PERMISSION_PREFIX}}_view'" @click="handleDetail(row.{{PK_ATTR}})">{{ t('common.viewBtn') }}</el-button>
33
34
  <!-- 编辑{{FEATURE_TITLE}}:业务单据编辑态控制按需生效 -->
34
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>
35
- <!-- 提交{{FEATURE_TITLE}} -->
36
- <el-button text type="primary" icon="check" @click="handleQuickAction(row, 'submit')">{{ commonActionLabel('submit') }}</el-button>
37
- <!-- 流转{{FEATURE_TITLE}} -->
38
- <el-button text type="success" icon="position" @click="handleQuickAction(row, 'flow')">{{ commonActionLabel('flow') }}</el-button>
36
+ <!-- 提交{{FEATURE_TITLE}}:仅在编辑中状态时可用 -->
37
+ <el-button{{BUSINESS_SUBMIT_IF}} text type="primary" icon="check" @click="handleQuickAction(row, 'submit')">{{ commonActionLabel('submit') }}</el-button>
38
+ <!-- 流转{{FEATURE_TITLE}}:仅在编辑中状态时可用 -->
39
+ <el-button{{BUSINESS_FLOW_IF}} text type="success" icon="position" @click="handleQuickAction(row, 'flow')">{{ commonActionLabel('flow') }}</el-button>
39
40
  <!-- 删除{{FEATURE_TITLE}} -->
40
41
  <el-button{{BUSINESS_DELETE_IF}} icon="delete" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_del'" @click="handleDelete([row.{{PK_ATTR}}]{{BUSINESS_DELETE_ROW_ARG}})">{{ t('common.delBtn') }}</el-button>
41
42
  </template>
@@ -56,6 +57,8 @@ import { useMessage, useMessageBox } from '/@/hooks/message';
56
57
  import { useDict } from '/@/hooks/dict';
57
58
  // 列表页字段元数据能力
58
59
  import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
60
+ // 列表当前行选中保持能力
61
+ import { useTableCurrentRow } from '/@/hooks/useTableCurrentRow';
59
62
  // 列表页查询与分页状态
60
63
  import { useSchemaListQuery } from '/@/hooks/useSchemaListQuery';
61
64
  // 统一列表工具栏组件
@@ -92,6 +95,12 @@ const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHan
92
95
  exportFileName: '{{FUNCTION_NAME}}.xlsx',
93
96
  });
94
97
 
98
+ // 统一处理列表加载后的首行选中,以及刷新后的当前行保持
99
+ const { currentRowKey, handleCurrentChange: handleTableRowCurrentChange } = useTableCurrentRow(
100
+ computed(() => state.dataList),
101
+ '{{PK_ATTR}}'
102
+ );
103
+
95
104
  // 提供字段标签、字典选项和查询区描述
96
105
  const { resolveLabel, getDictOptions, commonActionLabel, visibleTableColumns, searchKeywordTooltip, queryableDictFields } = useCrudPageMeta(dataMasterEntity, dictRefs);
97
106
 
@@ -132,6 +141,8 @@ const tableProps = computed(() => ({
132
141
  columns: tableColumns.value,
133
142
  pagination: state.pagination,
134
143
  tableStyle,
144
+ currentRowKey: currentRowKey.value,
145
+ highlightCurrentRow: true,
135
146
  }));
136
147
 
137
148
  // 表单页路由路径
@@ -2,25 +2,25 @@
2
2
  -- Suggested default parent_id: -1
3
3
  -- Generated by worsoft-codegen-local on {{GENERATED_AT}}
4
4
 
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
- values ({{MENU_BASE_ID}}, '-1', '/{{MENU_ROUTE_PATH}}/index', '', '0', 'icon-bangzhushouji', '0', null, '8', null, '{{TABLE_COMMENT}}管理', 1);
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, bill_code)
6
+ values ({{MENU_BASE_ID}}, '-1', '/{{MENU_ROUTE_PATH}}/index', '', '0', 'icon-bangzhushouji', '0', null, '8', null, '{{TABLE_COMMENT}}管理', 1, '{{BILL_CODE}}');
7
7
 
8
8
  -- Form route SQL for jump-based pages. Hidden from sidebar, used by list page add/edit/view navigation.
9
9
  -- Keep it as a sibling of the list page, not a child of the list page, because index.vue has no nested router-view.
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{{BUSINESS_FORM_KEEP_ALIVE_COLUMN}})
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}}'{{BUSINESS_FORM_KEEP_ALIVE_VALUE}});
12
12
 
13
- insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
14
- values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1);
13
+ 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)
14
+ values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1, '{{BILL_CODE}}');
15
15
 
16
- insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
17
- values ({{MENU_BASE_ID_PLUS_2}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_add', '1', null, '1', '0', null, '1', null, '{{TABLE_COMMENT}}新增', 1);
16
+ 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)
17
+ values ({{MENU_BASE_ID_PLUS_2}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_add', '1', null, '1', '0', null, '1', null, '{{TABLE_COMMENT}}新增', 1, '{{BILL_CODE}}');
18
18
 
19
- insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
20
- values ({{MENU_BASE_ID_PLUS_3}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_edit', '1', null, '1', '0', null, '2', null, '{{TABLE_COMMENT}}修改', 1);
19
+ 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)
20
+ values ({{MENU_BASE_ID_PLUS_3}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_edit', '1', null, '1', '0', null, '2', null, '{{TABLE_COMMENT}}修改', 1, '{{BILL_CODE}}');
21
21
 
22
- insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
23
- values ({{MENU_BASE_ID_PLUS_4}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_del', '1', null, '1', '0', null, '3', null, '{{TABLE_COMMENT}}删除', 1);
22
+ 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)
23
+ values ({{MENU_BASE_ID_PLUS_4}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_del', '1', null, '1', '0', null, '3', null, '{{TABLE_COMMENT}}删除', 1, '{{BILL_CODE}}');
24
24
 
25
- insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
26
- values ({{MENU_BASE_ID_PLUS_5}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_export', '1', null, '1', '0', null, '4', null, '导入导出', 1);
25
+ 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)
26
+ values ({{MENU_BASE_ID_PLUS_5}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_export', '1', null, '1', '0', null, '4', null, '导入导出', 1, '{{BILL_CODE}}');
@@ -23,6 +23,7 @@
23
23
  row-id-key="{{PK_ATTR}}"
24
24
  :action-column-width="{{ACTION_COLUMN_WIDTH}}"
25
25
  @selection-change="handleTableSelectionChange"
26
+ @row-current-change="handleTableRowCurrentChange"
26
27
  @sort-change="handleTableSortChange"
27
28
  @size-change="handleTableSizeChange"
28
29
  @current-change="handleTableCurrentChange"
@@ -51,6 +52,8 @@ import { useDict } from '/@/hooks/dict';
51
52
  {{DICT_SEMANTIC_IMPORT_BLOCK}}
52
53
  // 列表页字段元数据能力
53
54
  import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
55
+ // 列表当前行选中保持能力
56
+ import { useTableCurrentRow } from '/@/hooks/useTableCurrentRow';
54
57
  // 列表页查询与分页状态
55
58
  import { useSchemaListQuery } from '/@/hooks/useSchemaListQuery';
56
59
  // 统一列表工具栏组件
@@ -86,6 +89,12 @@ const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHan
86
89
  exportFileName: '{{FUNCTION_NAME}}.xlsx',
87
90
  });
88
91
 
92
+ // 统一处理列表加载后的首行选中,以及刷新后的当前行保持
93
+ const { currentRowKey, handleCurrentChange: handleTableRowCurrentChange } = useTableCurrentRow(
94
+ computed(() => state.dataList),
95
+ '{{PK_ATTR}}'
96
+ );
97
+
89
98
  // 提供字段标签、字典选项和查询区描述
90
99
  const { resolveLabel, getDictOptions, visibleTableColumns, searchKeywordTooltip, queryableDictFields } = useCrudPageMeta(dataMasterEntity, dictRefs);
91
100
 
@@ -130,6 +139,8 @@ const tableProps = computed(() => ({
130
139
  columns: tableColumns.value,
131
140
  pagination: state.pagination,
132
141
  tableStyle,
142
+ currentRowKey: currentRowKey.value,
143
+ highlightCurrentRow: true,
133
144
  }));
134
145
 
135
146
  // 字典页动作按钮控制(按需生成)