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
@@ -2,20 +2,20 @@
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
- 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
- values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1);
8
+ 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)
9
+ values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1, '{{BILL_CODE}}');
10
10
 
11
- insert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name, tenant_id)
12
- values ({{MENU_BASE_ID_PLUS_2}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_add', '1', null, '1', '0', null, '1', null, '{{TABLE_COMMENT}}新增', 1);
11
+ 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)
12
+ values ({{MENU_BASE_ID_PLUS_2}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_add', '1', null, '1', '0', null, '1', null, '{{TABLE_COMMENT}}新增', 1, '{{BILL_CODE}}');
13
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_3}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_edit', '1', null, '1', '0', null, '2', null, '{{TABLE_COMMENT}}修改', 1);
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_3}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_edit', '1', null, '1', '0', null, '2', null, '{{TABLE_COMMENT}}修改', 1, '{{BILL_CODE}}');
16
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)
18
- values ({{MENU_BASE_ID_PLUS_4}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_del', '1', null, '1', '0', null, '3', null, '{{TABLE_COMMENT}}删除', 1);
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_4}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_del', '1', null, '1', '0', null, '3', null, '{{TABLE_COMMENT}}删除', 1, '{{BILL_CODE}}');
19
19
 
20
- 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);
20
+ 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)
21
+ 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="320"
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"
@@ -33,10 +34,10 @@
33
34
  <el-button text type="primary" icon="view" v-auth="'{{PERMISSION_PREFIX}}_view'" @click="handleDetail(row.{{PK_ATTR}})">{{ t('common.viewBtn') }}</el-button>
34
35
  <!-- 编辑{{FEATURE_TITLE}}:业务单据编辑态控制按需生效 -->
35
36
  <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}} -->
37
- <el-button text type="primary" icon="check" @click="handleQuickAction(row, 'submit')">{{ commonActionLabel('submit') }}</el-button>
38
- <!-- 流转{{FEATURE_TITLE}} -->
39
- <el-button text type="success" icon="position" @click="handleQuickAction(row, 'flow')">{{ commonActionLabel('flow') }}</el-button>
37
+ <!-- 提交{{FEATURE_TITLE}}:仅在编辑中状态时可用 -->
38
+ <el-button{{BUSINESS_SUBMIT_IF}} text type="primary" icon="check" @click="handleQuickAction(row, 'submit')">{{ commonActionLabel('submit') }}</el-button>
39
+ <!-- 流转{{FEATURE_TITLE}}:仅在编辑中状态时可用 -->
40
+ <el-button{{BUSINESS_FLOW_IF}} text type="success" icon="position" @click="handleQuickAction(row, 'flow')">{{ commonActionLabel('flow') }}</el-button>
40
41
  <!-- 删除{{FEATURE_TITLE}} -->
41
42
  <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>
42
43
  </template>
@@ -57,6 +58,8 @@ import { useMessage, useMessageBox } from '/@/hooks/message';
57
58
  import { useDict } from '/@/hooks/dict';
58
59
  // 列表页字段元数据能力
59
60
  import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
61
+ // 列表当前行选中保持能力
62
+ import { useTableCurrentRow } from '/@/hooks/useTableCurrentRow';
60
63
  // 列表页查询与分页状态
61
64
  import { useSchemaListQuery } from '/@/hooks/useSchemaListQuery';
62
65
  // 统一列表工具栏组件
@@ -93,6 +96,12 @@ const { state, getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHan
93
96
  exportFileName: '{{FUNCTION_NAME}}.xlsx',
94
97
  });
95
98
 
99
+ // 统一处理列表加载后的首行选中,以及刷新后的当前行保持
100
+ const { currentRowKey, handleCurrentChange: handleTableRowCurrentChange } = useTableCurrentRow(
101
+ computed(() => state.dataList),
102
+ '{{PK_ATTR}}'
103
+ );
104
+
96
105
  // 提供字段标签、字典选项和查询区描述
97
106
  const { resolveLabel, getDictOptions, commonActionLabel, visibleTableColumns, searchKeywordTooltip, queryableDictFields } = useCrudPageMeta(dataMasterEntity, dictRefs);
98
107
 
@@ -136,6 +145,8 @@ const tableProps = computed(() => ({
136
145
  columns: tableColumns.value,
137
146
  pagination: state.pagination,
138
147
  tableStyle,
148
+ currentRowKey: currentRowKey.value,
149
+ highlightCurrentRow: true,
139
150
  }));
140
151
 
141
152
  // 表单页路由路径
@@ -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}}');
@@ -0,0 +1,17 @@
1
+ import request from '/@/utils/request';
2
+ import { createCrudApi } from '/@/api/common/crudFactory';
3
+
4
+ // {{FEATURE_TITLE}} 通用列表、新增、详情、删除、更新、导出接口
5
+ export const { fetchList, addObj, getObj, delObjs, putObj, exportObj } = createCrudApi('/{{API_PATH}}');
6
+
7
+ // {{FEATURE_TITLE}} 树形子节点列表
8
+ export function fetchTreeList(payload?: any) {
9
+ return request({
10
+ url: '/{{API_PATH}}/listByParentId',
11
+ method: 'get',
12
+ params: payload,
13
+ });
14
+ }
15
+
16
+ {{DICT_API_FUNCTIONS}}
17
+ {{EXTRA_API_FUNCTIONS}}
@@ -0,0 +1,139 @@
1
+ <template>
2
+ <!-- 表单弹窗:新增或编辑{{FEATURE_TITLE}}数据 -->
3
+ <el-dialog v-model="visible" :title="form.{{PK_ATTR}} ? t('common.editBtn') : t('common.addBtn')" :close-on-click-modal="false" draggable>
4
+ <!-- 弹窗表单:按 PRD 表单显隐和顺序渲染字段 -->
5
+ <el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
6
+ <!-- 主表字段区:字段级注释由 MCP 根据字段名称生成 -->
7
+ <el-row :gutter="24">
8
+ {{FORM_FIELDS}}
9
+ </el-row>
10
+ </el-form>
11
+ <!-- 弹窗底部操作按钮:取消和确认提交 -->
12
+ <template #footer>
13
+ <span class="dialog-footer">
14
+ <el-button @click="visible = false">{{ t('common.cancelButtonText') }}</el-button>
15
+ <el-button type="primary" @click="onSubmit" :disabled="loading">{{ t('common.confirmButtonText') }}</el-button>
16
+ </span>
17
+ </template>
18
+ </el-dialog>
19
+ </template>
20
+
21
+ <script setup lang="ts" name="{{CLASS_NAME}}Dialog">
22
+ // 通用消息提示
23
+ import { useMessage } from '/@/hooks/message';
24
+ // 本地会话存储
25
+ import { Session } from '/@/utils/storage';
26
+ // 表单数据接口
27
+ import { getObj, addObj, putObj } from '/@/api/{{API_MODULE_PATH}}';
28
+ // 字典数据加载
29
+ import { useDict } from '/@/hooks/dict';
30
+ // 表单字段元数据能力
31
+ import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
32
+ // 国际化能力
33
+ import { useI18n } from 'vue-i18n';
34
+ // 当前页面的字段配置
35
+ import { allDictTypes, dataMasterEntity } from './options';
36
+
37
+ // 页面所需的字典引用
38
+ const dictRefs = useDict(...allDictTypes);
39
+ // 国际化方法
40
+ const { t } = useI18n();
41
+ // 弹窗刷新事件
42
+ const emit = defineEmits(['refresh']);
43
+
44
+ // 表单引用
45
+ const dataFormRef = ref();
46
+ // 弹窗显示状态
47
+ const visible = ref(false);
48
+ // 提交加载状态
49
+ const loading = ref(false);
50
+
51
+ // 主表字段的双语、字典和校验提示统一由公共 hook 提供
52
+ const {
53
+ getFieldMeta: getMasterFieldMeta,
54
+ getFieldLabel: getMasterFieldLabel,
55
+ getDictOptions,
56
+ inputPlaceholder,
57
+ selectPlaceholder,
58
+ fieldRequiredMessage,
59
+ } = useCrudPageMeta(dataMasterEntity, dictRefs);
60
+
61
+ const formInputPlaceholder = (label: string, disabled = false) => (disabled ? '' : inputPlaceholder(label));
62
+ const formSelectPlaceholder = (label: string, disabled = false) => (disabled ? '' : selectPlaceholder(label));
63
+
64
+ // 统一维护表单默认值
65
+ const createDefaultFormState = () => ({
66
+ {{FORM_DEFAULTS}}
67
+ });
68
+
69
+ // 表单响应式数据
70
+ const form = reactive(createDefaultFormState());
71
+
72
+ // 表单校验规则
73
+ const dataRules = ref({
74
+ {{FORM_RULES}}
75
+ });
76
+
77
+ // 根据主键加载详情数据
78
+ const get{{CLASS_NAME}}Data = async (id: string) => {
79
+ try {
80
+ loading.value = true;
81
+ const { data } = await getObj({ {{PK_ATTR}}: id });
82
+ Object.assign(form, Array.isArray(data) ? data[0] || {} : data || {});
83
+ } catch (error) {
84
+ useMessage().error(t('common.messages.fetchError'));
85
+ } finally {
86
+ loading.value = false;
87
+ }
88
+ };
89
+
90
+ // 重置表单为初始状态
91
+ const resetFormState = () => {
92
+ Object.assign(form, createDefaultFormState());
93
+ nextTick(() => {
94
+ dataFormRef.value?.resetFields();
95
+ });
96
+ };
97
+
98
+ // 打开弹窗并按需加载详情
99
+ const openDialog = async (id?: string, parentId?: string | number) => {
100
+ visible.value = true;
101
+ resetFormState();
102
+
103
+ if (parentId !== undefined && parentId !== null && parentId !== '') {
104
+ form.{{TREE_PARENT_ATTR}} = parentId;
105
+ }
106
+
107
+ if (id) {
108
+ form.{{PK_ATTR}} = id;
109
+ await get{{CLASS_NAME}}Data(id);
110
+ }
111
+ };
112
+
113
+ // 提交弹窗表单
114
+ const onSubmit = async () => {
115
+ loading.value = true;
116
+
117
+ const valid = await dataFormRef.value.validate().catch(() => {});
118
+ if (!valid) {
119
+ loading.value = false;
120
+ return false;
121
+ }
122
+
123
+ try {
124
+ form.{{PK_ATTR}} ? await putObj(form) : await addObj(form);
125
+ useMessage().success(form.{{PK_ATTR}} ? t('common.editSuccessText') : t('common.addSuccessText'));
126
+ visible.value = false;
127
+ emit('refresh');
128
+ } catch (err: any) {
129
+ useMessage().error(err.msg || t('common.messages.submitError'));
130
+ } finally {
131
+ loading.value = false;
132
+ }
133
+ };
134
+
135
+ // 向父组件暴露打开弹窗方法
136
+ defineExpose({
137
+ openDialog,
138
+ });
139
+ </script>
@@ -0,0 +1,203 @@
1
+ <!-- 功能名称:{{FEATURE_TITLE}} -->
2
+ <template>
3
+ <div class="layout-padding">
4
+ <div class="layout-padding-auto layout-padding-view flex h-full flex-col">
5
+ <SchemaListToolbar
6
+ v-bind="toolbarProps"
7
+ :show-add="false"
8
+ @update:keyword="state.queryForm.smartVal = $event"
9
+ @query="handleToolbarQuery"
10
+ @reset="handleToolbarReset"
11
+ @refresh="handleToolbarRefresh"
12
+ >
13
+ <template #prepend-actions>
14
+ <el-dropdown trigger="click" @command="handleAddTreeNode">
15
+ <el-button type="primary">
16
+ {{ t('common.addBtn') }}
17
+ <el-icon class="ml-1"><ArrowDown /></el-icon>
18
+ </el-button>
19
+ <template #dropdown>
20
+ <el-dropdown-menu>
21
+ <el-dropdown-item command="root">{{ resolveText('common.treeActions.addRoot', '新增根节点') }}</el-dropdown-item>
22
+ <el-dropdown-item command="sibling" :disabled="!currentRow">{{ resolveText('common.treeActions.addSibling', '新增同级节点') }}</el-dropdown-item>
23
+ <el-dropdown-item command="child" :disabled="!currentRow">{{ resolveText('common.treeActions.addChild', '新增子节点') }}</el-dropdown-item>
24
+ </el-dropdown-menu>
25
+ </template>
26
+ </el-dropdown>
27
+ </template>
28
+ </SchemaListToolbar>
29
+
30
+ <SchemaTreeTable
31
+ v-bind="tableProps"
32
+ row-id-key="{{PK_ATTR}}"
33
+ :load="loadTreeChildren"
34
+ :action-column-width="{{ACTION_COLUMN_WIDTH}}"
35
+ @row-current-change="handleTableRowCurrentChange"
36
+ >
37
+ <template #actions="{ row }">
38
+ {{LIST_ACTIONS}}
39
+ </template>
40
+ </SchemaTreeTable>
41
+ </div>
42
+
43
+ <form-dialog ref="formDialogRef" @refresh="getDataList" />
44
+ </div>
45
+ </template>
46
+
47
+ <script setup lang="ts" name="system{{CLASS_NAME}}">
48
+ import { ArrowDown } from '@element-plus/icons-vue';
49
+ import { computed, defineAsyncComponent, onMounted, reactive, ref, watch } from 'vue';
50
+ import { useI18n } from 'vue-i18n';
51
+ import { fetchTreeList, delObjs{{DICT_API_IMPORTS}} } from '/@/api/{{API_MODULE_PATH}}';
52
+ import { useMessage, useMessageBox } from '/@/hooks/message';
53
+ import { useDict } from '/@/hooks/dict';
54
+ {{DICT_SEMANTIC_IMPORT_BLOCK}}
55
+ import { useTable } from '/@/hooks/table';
56
+ import { useCrudPageMeta } from '/@/hooks/useCrudPageMeta';
57
+ // 列表当前行选中保持能力
58
+ import { useTableCurrentRow } from '/@/hooks/useTableCurrentRow';
59
+ import { buildSchemaQueryParams } from '/@/utils/schemaQuery';
60
+ import SchemaListToolbar from '/@/components/schema-list/SchemaListToolbar.vue';
61
+ import SchemaTreeTable from '/@/components/schema-list/SchemaTreeTable.vue';
62
+ import { allDictTypes, crudSchema, dataMasterEntity } from './options';
63
+
64
+ const FormDialog = defineAsyncComponent(() => import('./form.vue'));
65
+ const formDialogRef = ref();
66
+ const { t } = useI18n();
67
+ const dictRefs = useDict(...allDictTypes);
68
+ const { tableStyle } = useTable({ createdIsNeed: false });
69
+
70
+ const state = reactive({
71
+ loading: false,
72
+ dataList: [] as any[],
73
+ queryForm: {
74
+ smartVal: '',
75
+ } as Record<string, any>,
76
+ });
77
+
78
+ // 统一处理树表加载后的首行选中、刷新后的当前行保持,并保留当前行供新增同级/子级使用
79
+ const { currentRow, currentRowKey, handleCurrentChange: handleTableRowCurrentChange } = useTableCurrentRow(
80
+ computed(() => state.dataList),
81
+ '{{PK_ATTR}}'
82
+ );
83
+
84
+ const { resolveLabel, getDictOptions, visibleTableColumns, searchKeywordTooltip } = useCrudPageMeta(dataMasterEntity, dictRefs);
85
+
86
+ const resolveText = (key: string, fallback: string) => {
87
+ const translated = t(key);
88
+ return translated === key ? fallback : translated;
89
+ };
90
+
91
+ const tableColumns = computed(() =>
92
+ visibleTableColumns.value.map((column) => ({
93
+ prop: column.prop,
94
+ label: resolveLabel(column.labelKey, column.fallbackLabel),
95
+ width: column.width,
96
+ dictType: column.dictType,
97
+ options: column.dictType ? getDictOptions(column.dictType) : [],
98
+ }))
99
+ );
100
+
101
+ const toolbarProps = computed(() => ({
102
+ keyword: state.queryForm.smartVal,
103
+ searchPlaceholder: searchKeywordTooltip.value,
104
+ queryModel: state.queryForm,
105
+ customQueryFields: [],
106
+ selectedIds: [],
107
+ showImport: false,
108
+ showDelete: false,
109
+ showRightTools: false,
110
+ }));
111
+
112
+ const tableProps = computed(() => ({
113
+ data: state.dataList,
114
+ loading: !!state.loading,
115
+ columns: tableColumns.value,
116
+ tableStyle,
117
+ currentRowKey: currentRowKey.value,
118
+ lazy: true,
119
+ highlightCurrentRow: true,
120
+ }));
121
+
122
+ const normalizeTreeRows = (rows: any[]) =>
123
+ rows.map((row) => ({
124
+ ...row,
125
+ hasChildren:
126
+ typeof row.hasChildren === 'boolean'
127
+ ? row.hasChildren
128
+ : typeof row.hasChild === 'boolean'
129
+ ? row.hasChild
130
+ : typeof row.childCount === 'number'
131
+ ? row.childCount > 0
132
+ : true,
133
+ }));
134
+
135
+ const getQueryParams = () => ({
136
+ {{TREE_PARENT_ATTR}}: {{TREE_ROOT_PARENT_VALUE}},
137
+ ...buildSchemaQueryParams(crudSchema.filterTypes, state.queryForm),
138
+ });
139
+
140
+ const getDataList = async () => {
141
+ state.loading = true;
142
+ try {
143
+ const { data } = await fetchTreeList(getQueryParams());
144
+ const rows = Array.isArray(data) ? data : data?.records || data?.list || [];
145
+ state.dataList = normalizeTreeRows(rows);
146
+ } finally {
147
+ state.loading = false;
148
+ }
149
+ };
150
+
151
+ {{DICT_LIST_HELPERS}}
152
+
153
+ const handleAddTreeNode = (command: 'root' | 'sibling' | 'child') => {
154
+ let parentId: string | number = {{TREE_ROOT_PARENT_VALUE}};
155
+ if (command === 'sibling' && currentRow.value) parentId = currentRow.value.{{TREE_PARENT_ATTR}} ?? {{TREE_ROOT_PARENT_VALUE}};
156
+ if (command === 'child' && currentRow.value) parentId = currentRow.value.{{PK_ATTR}};
157
+ formDialogRef.value?.openDialog(undefined, parentId);
158
+ };
159
+
160
+ const handleDelete = async (ids: string[]) => {
161
+ try {
162
+ await useMessageBox().confirm(t('common.delConfirmText'));
163
+ } catch {
164
+ return;
165
+ }
166
+
167
+ try {
168
+ await delObjs(ids);
169
+ await getDataList();
170
+ useMessage().success(t('common.delSuccessText'));
171
+ } catch (err: any) {
172
+ useMessage().error(err.msg || t('common.delBtn'));
173
+ }
174
+ };
175
+
176
+ const handleToolbarQuery = () => {
177
+ getDataList();
178
+ };
179
+
180
+ const handleToolbarReset = () => {
181
+ Object.keys(state.queryForm).forEach((key) => delete state.queryForm[key]);
182
+ state.queryForm.smartVal = '';
183
+ getDataList();
184
+ };
185
+
186
+ const handleToolbarRefresh = () => {
187
+ getDataList();
188
+ };
189
+
190
+ const loadTreeChildren = async (row: any, _treeNode: any, resolve: (data: any[]) => void) => {
191
+ try {
192
+ const { data } = await fetchTreeList({ {{TREE_PARENT_ATTR}}: row?.{{PK_ATTR}} });
193
+ const rows = Array.isArray(data) ? data : data?.records || data?.list || [];
194
+ resolve(normalizeTreeRows(rows));
195
+ } catch {
196
+ resolve([]);
197
+ }
198
+ };
199
+
200
+ onMounted(() => {
201
+ getDataList();
202
+ });
203
+ </script>
@@ -0,0 +1,21 @@
1
+ -- Do not execute directly. Review parent menu id and tenant settings before import.
2
+ -- Suggested default parent_id: -1
3
+ -- Generated by worsoft-codegen-local on {{GENERATED_AT}}
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, 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
+
8
+ 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)
9
+ values ({{MENU_BASE_ID_PLUS_1}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_view', '1', null, '1', '0', null, '0', null, '{{TABLE_COMMENT}}查看', 1, '{{BILL_CODE}}');
10
+
11
+ 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)
12
+ values ({{MENU_BASE_ID_PLUS_2}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_add', '1', null, '1', '0', null, '1', null, '{{TABLE_COMMENT}}新增', 1, '{{BILL_CODE}}');
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_3}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_edit', '1', null, '1', '0', null, '2', 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_4}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_del', '1', null, '1', '0', null, '3', null, '{{TABLE_COMMENT}}删除', 1, '{{BILL_CODE}}');
19
+
20
+ 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)
21
+ values ({{MENU_BASE_ID_PLUS_5}}, {{MENU_BASE_ID}}, '{{PERMISSION_PREFIX}}_export', '1', null, '1', '0', null, '4', null, '导入导出', 1, '{{BILL_CODE}}');
@@ -0,0 +1,35 @@
1
+ // 字典注册表
2
+ {{DICT_REGISTRY_IMPORT_BLOCK}}
3
+ // schema 构建工具
4
+ import { createCrudSchema } from '/@/utils/crudSchema';
5
+ // schema 类型定义
6
+ import type { CrudSchemaDefinition } from '/@/utils/crudSchema';
7
+
8
+ /**
9
+ * {{TABLE_NAME}} 页面字段声明
10
+ * 这里维护字段 key、双语 key、字典类型和显示元数据
11
+ */
12
+ const definition: CrudSchemaDefinition = {
13
+ master: [
14
+ {{MASTER_OPTION_FIELDS}}
15
+ ],
16
+ children: {
17
+ {{CHILD_OPTION_GROUPS}}
18
+ },
19
+ };
20
+
21
+ // 将页面字段声明转换为 index/form 共用的 schema 结果
22
+ const schema = createCrudSchema(definition);
23
+
24
+ // 完整 CRUD schema
25
+ export const crudSchema = schema;
26
+ // 主表字段配置,供列表页和表单页使用
27
+ export const dataMasterEntity = schema.master;
28
+ // 子表字段配置,当前无子表时保留统一出口
29
+ export const childFieldGroups = schema.children;
30
+ // 列表/查询使用的主表字段类型映射
31
+ export const filterTypes = schema.filterTypes;
32
+ // 子表字段类型映射
33
+ export const childFilterTypes = schema.childFilterTypes;
34
+ // 当前页面需要一次性加载的全部字典类型
35
+ export const allDictTypes = schema.allDictTypes;
@@ -0,0 +1 @@
1
+ tree_left_list 由 mcp_server.js 的多层字典模型专用渲染函数生成,目录用于样式目录注册和模板资产归档。
@@ -0,0 +1,4 @@
1
+ /*
2
+ * 左树右列表 API 模板由 mcp_server.js 的 renderTreeLeftListApiTs 渲染。
3
+ * 该模板会为左侧树形模块额外生成 <apiPath>/listByParentId 懒加载接口。
4
+ */
@@ -0,0 +1,3 @@
1
+ <!--
2
+ 左树右列表 form 模板复用多层字典 form 渲染逻辑。
3
+ -->
@@ -0,0 +1,4 @@
1
+ <!--
2
+ 左树右列表模板由 mcp_server.js 的 renderTreeLeftListIndexVue 渲染。
3
+ 该样式依赖 levels[]:level 1 为左侧树形模块,level 2 为右侧列表模块。
4
+ -->
@@ -0,0 +1 @@
1
+ -- 左树右列表菜单 SQL 模板复用多层字典菜单 SQL 渲染逻辑。
@@ -0,0 +1,3 @@
1
+ /*
2
+ * 左树右列表 options 模板复用多层字典 options 渲染逻辑。
3
+ */