worsoft-frontend-codegen-local-mcp 0.1.4 → 0.1.6

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,8 +1,20 @@
1
1
  <template>
2
2
  <div class="layout-padding-auto layout-padding-view">
3
- <el-card shadow="never">
3
+ <el-card shadow="never" class="w100">
4
4
  <template #header>
5
- <span>{{ form.{{PK_ATTR}} ? (detail ? '查看' : '编辑') : '新增' }}</span>
5
+ <div style="display: flex; justify-content: space-between; align-items: center;">
6
+ <span style="font-size: 16px; font-weight: bold;">{{ form.{{PK_ATTR}} ? (detail ? t('common.viewBtn') : t('common.editBtn')) : t('common.addBtn') }}</span>
7
+ <div v-if="!detail">
8
+ <el-button @click="onSubmit('save')" :disabled="loading" icon="document">{{ actionLabel('save') }}</el-button>
9
+ <el-button @click="onSubmit('flow')" :disabled="loading" plain type="success" icon="position">{{ actionLabel('flow') }}</el-button>
10
+ <el-button @click="onSubmit('submit')" :disabled="loading" type="primary" icon="check">{{ actionLabel('submit') }}</el-button>
11
+ <el-divider direction="vertical" />
12
+ <el-button @click="handleBack" icon="close">{{ t('common.cancelButtonText') }}</el-button>
13
+ </div>
14
+ <div v-else>
15
+ <el-button @click="handleBack" icon="back">{{ actionLabel('back') }}</el-button>
16
+ </div>
17
+ </div>
6
18
  </template>
7
19
  <el-form ref="dataFormRef" :model="form" :rules="dataRules" :disabled="detail" v-loading="loading">
8
20
  <el-row :gutter="24">
@@ -12,10 +24,6 @@
12
24
  {{CHILD_SECTIONS}}
13
25
  </el-row>
14
26
  </el-form>
15
- <div class="dialog-footer" style="text-align: right; margin-top: 18px;">
16
- <el-button @click="handleBack">取消</el-button>
17
- <el-button type="primary" @click="onSubmit" :disabled="loading">确认</el-button>
18
- </div>
19
27
  </el-card>
20
28
  </div>
21
29
  </template>
@@ -24,16 +32,50 @@
24
32
  import mittBus from '/@/utils/mitt';
25
33
  import { useMessage } from '/@/hooks/message';
26
34
  import { getObj, addObj, putObj, delChildObj } from '/@/api/{{API_MODULE_PATH}}';
27
- {{DICT_IMPORT_BLOCK}}
35
+ import { useDict } from '/@/hooks/dict';
36
+ import { useI18n } from 'vue-i18n';
37
+ import { allDictTypes, childFieldGroups, dataMasterEntity } from './options';
38
+
39
+ const dictRefs = useDict(...allDictTypes);
40
+ const { t } = useI18n();
28
41
 
29
42
  const scFormTable = defineAsyncComponent(() => import('/@/components/FormTable/index.vue'));
30
43
  const route = useRoute();
31
44
  const router = useRouter();
45
+ const pageI18nKey = '{{I18N_NAMESPACE}}';
32
46
 
33
47
  const dataFormRef = ref();
34
48
  const loading = ref(false);
35
49
  const detail = ref(false);
36
50
 
51
+ const masterFields = dataMasterEntity;
52
+
53
+ const getMasterFieldMeta = (prop: string) => masterFields[prop];
54
+ const isMasterFieldVisible = (prop: string) => {
55
+ const config = getMasterFieldMeta(prop);
56
+ return Boolean(config) && !config.alwaysHide && config.show !== false;
57
+ };
58
+ const getChildFieldMeta = (groupName: string, prop: string) => childFieldGroups[groupName]?.[prop];
59
+ const resolveLabel = (labelKey?: string, fallback = '') => {
60
+ if (!labelKey) return fallback;
61
+ const translated = t(labelKey);
62
+ return translated === labelKey ? fallback : translated;
63
+ };
64
+ const getMasterFieldLabel = (prop: string) => {
65
+ const config = getMasterFieldMeta(prop);
66
+ return resolveLabel(config?.labelKey, config?.label || prop);
67
+ };
68
+ const getChildFieldLabel = (groupName: string, prop: string) => {
69
+ const config = getChildFieldMeta(groupName, prop);
70
+ return resolveLabel(config?.labelKey, config?.label || prop);
71
+ };
72
+ const childSectionTitle = (groupName: string) => t(`${pageI18nKey}.children.${groupName}.title`);
73
+ const getDictOptions = (dictType?: string) => (dictType ? dictRefs[dictType]?.value || [] : []);
74
+ const inputPlaceholder = (label: string) => t(`${pageI18nKey}.placeholders.input`, { label });
75
+ const selectPlaceholder = (label: string) => t(`${pageI18nKey}.placeholders.select`, { label });
76
+ const fieldRequiredMessage = (prop: string) => t(`${pageI18nKey}.messages.required`, { label: getMasterFieldLabel(prop) });
77
+ const actionLabel = (action: string) => t(`${pageI18nKey}.actions.${action}`);
78
+
37
79
  const form = reactive({
38
80
  {{FORM_DEFAULTS}}
39
81
  {{CHILD_FORM_LIST_DEFAULTS}}
@@ -51,7 +93,7 @@ const get{{CLASS_NAME}}Data = async (id: string) => {
51
93
  const { data } = await getObj({ {{PK_ATTR}}: id });
52
94
  Object.assign(form, data[0] || {});
53
95
  } catch (error) {
54
- useMessage().error('获取数据失败');
96
+ useMessage().error(t(`${pageI18nKey}.messages.fetchError`));
55
97
  } finally {
56
98
  loading.value = false;
57
99
  }
@@ -89,7 +131,7 @@ const handleBack = () => {
89
131
  router.back();
90
132
  };
91
133
 
92
- const onSubmit = async () => {
134
+ const onSubmit = async (actionType?: string) => {
93
135
  loading.value = true;
94
136
 
95
137
  const valid = await dataFormRef.value.validate().catch(() => {});
@@ -100,10 +142,15 @@ const onSubmit = async () => {
100
142
 
101
143
  try {
102
144
  form.{{PK_ATTR}} ? await putObj(form) : await addObj(form);
103
- useMessage().success(form.{{PK_ATTR}} ? '修改成功' : '添加成功');
145
+
146
+ let msg = form.{{PK_ATTR}} ? t(`${pageI18nKey}.messages.updateSuccess`) : t(`${pageI18nKey}.messages.createSuccess`);
147
+ if (actionType === 'submit') msg = t(`${pageI18nKey}.messages.quickSubmitSuccess`);
148
+ if (actionType === 'flow') msg = t(`${pageI18nKey}.messages.quickFlowSuccess`);
149
+
150
+ useMessage().success(msg);
104
151
  closeCurrentPage();
105
152
  } catch (err: any) {
106
- useMessage().error(err.msg || '提交失败');
153
+ useMessage().error(err.msg || t(`${pageI18nKey}.messages.submitError`));
107
154
  } finally {
108
155
  loading.value = false;
109
156
  }
@@ -113,9 +160,9 @@ const deleteChild = async (obj: Record<string, any>, childPkAttr: string, childT
113
160
  if (obj[childPkAttr]) {
114
161
  try {
115
162
  await delChildObj([obj[childPkAttr]], childTableName);
116
- useMessage().success('删除成功');
163
+ useMessage().success(t('common.delSuccessText'));
117
164
  } catch (err: any) {
118
- useMessage().error(err.msg || '删除失败');
165
+ useMessage().error(err.msg || t('common.delBtn'));
119
166
  }
120
167
  }
121
168
  };
@@ -1,40 +1,58 @@
1
1
  <template>
2
2
  <div class="layout-padding">
3
- <div class="layout-padding-auto layout-padding-view">
4
- <el-row>
5
- <div class="mb8" style="width: 100%">
6
- <el-button icon="folder-add" type="primary" class="ml10" v-auth="'{{PERMISSION_PREFIX}}_add'" @click="handleCreate">新增</el-button>
7
- <el-button plain icon="upload-filled" type="primary" class="ml10" v-auth="'{{PERMISSION_PREFIX}}_add'" @click="excelUploadRef.show()">导入</el-button>
8
- <el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'{{PERMISSION_PREFIX}}_del'" @click="handleDelete(selectObjs)">删除</el-button>
9
- <right-toolbar v-model:showSearch="showSearch" :export="'{{PERMISSION_PREFIX}}_export'" @exportExcel="exportExcel" @queryTable="getDataList" class="ml10 mr20" style="float: right;" />
10
- </div>
11
- </el-row>
12
-
13
- <el-table
14
- :data="state.dataList"
15
- v-loading="state.loading"
16
- border
17
- :cell-style="tableStyle.cellStyle"
18
- :header-cell-style="tableStyle.headerCellStyle"
19
- @selection-change="selectionChangeHandle"
20
- @sort-change="sortChangeHandle"
21
- >
3
+ <div class="layout-padding-auto layout-padding-view flex-column" style="display: flex; flex-direction: column; height: 100%;">
4
+ <div class="mb8" style="width: 100%; flex-shrink: 0;">
5
+ <el-button icon="folder-add" type="primary" class="ml10" @click="handleCreate">{{ t('common.addBtn') }}</el-button>
6
+ <el-button plain icon="upload-filled" type="primary" class="ml10" @click="excelUploadRef.show()">{{ t('common.importBtn') }}</el-button>
7
+ <el-button plain :disabled="multiple" icon="Delete" type="primary" @click="handleDelete(selectObjs)">{{ t('common.delBtn') }}</el-button>
8
+ <right-toolbar v-model:showSearch="showSearch" :export="'{{PERMISSION_PREFIX}}_export'" @exportExcel="exportExcel" @queryTable="getDataList" class="ml10 mr20" style="float: right;" />
9
+ </div>
10
+
11
+ <div style="flex: 1; overflow: hidden; display: flex; flex-direction: column;">
12
+ <el-table
13
+ :data="state.dataList"
14
+ v-loading="state.loading"
15
+ border
16
+ height="100%"
17
+ style="width: 100%;"
18
+ :cell-style="tableStyle.cellStyle"
19
+ :header-cell-style="tableStyle.headerCellStyle"
20
+ @selection-change="selectionChangeHandle"
21
+ @sort-change="sortChangeHandle"
22
+ >
22
23
  <el-table-column type="selection" width="40" align="center" />
23
- <el-table-column type="index" label="#" width="40" />
24
- {{TABLE_COLUMNS}}
25
- <el-table-column label="操作" width="220">
24
+ <el-table-column type="index" :label="t('common.serial')" width="60" />
25
+ <el-table-column
26
+ v-for="column in visibleTableColumns"
27
+ :key="column.prop"
28
+ :prop="column.prop"
29
+ :label="resolveLabel(column.labelKey, column.fallbackLabel)"
30
+ :min-width="column.width"
31
+ show-overflow-tooltip
32
+ >
33
+ <template #default="scope">
34
+ <dict-tag v-if="column.dictType" :options="getDictOptions(column.dictType)" :value="scope.row[column.prop]" />
35
+ <span v-else>{{ scope.row[column.prop] }}</span>
36
+ </template>
37
+ </el-table-column>
38
+ <el-table-column :label="t('common.action')" width="380" fixed="right">
26
39
  <template #default="scope">
27
- <el-button text type="primary" icon="view" v-auth="'{{PERMISSION_PREFIX}}_view'" @click="handleDetail(scope.row.{{PK_ATTR}})">查看</el-button>
28
- <el-button icon="edit-pen" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_edit'" @click="handleEdit(scope.row.{{PK_ATTR}})">编辑</el-button>
29
- <el-button icon="delete" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_del'" @click="handleDelete([scope.row.{{PK_ATTR}}])">删除</el-button>
40
+ <el-button text type="primary" icon="view" v-auth="'{{PERMISSION_PREFIX}}_view'" @click="handleDetail(scope.row.id)">{{ t('common.viewBtn') }}</el-button>
41
+ <el-button icon="edit-pen" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_edit'" @click="handleEdit(scope.row.id)">{{ t('common.editBtn') }}</el-button>
42
+ <el-button text type="primary" icon="check" @click="handleQuickAction(scope.row, 'submit')">{{ actionLabel('submit') }}</el-button>
43
+ <el-button text type="success" icon="position" @click="handleQuickAction(scope.row, 'flow')">{{ actionLabel('flow') }}</el-button>
44
+ <el-button icon="delete" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_del'" @click="handleDelete([scope.row.id])">{{ t('common.delBtn') }}</el-button>
30
45
  </template>
31
46
  </el-table-column>
32
- </el-table>
47
+ </el-table>
48
+ </div>
33
49
 
34
- <pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
50
+ <div style="flex-shrink: 0; margin-top: 10px; display: flex; justify-content: flex-end;">
51
+ <pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
52
+ </div>
35
53
  </div>
36
54
 
37
- <upload-excel ref="excelUploadRef" title="导入" url="/{{API_PATH}}/import" temp-url="/admin/sys-file/local/file/{{FUNCTION_NAME}}.xlsx" @refreshDataList="getDataList" />
55
+ <upload-excel ref="excelUploadRef" :title="t('common.importBtn')" url="/{{API_PATH}}/import" temp-url="/admin/sys-file/local/file/{{FUNCTION_NAME}}.xlsx" @refreshDataList="getDataList" />
38
56
  </div>
39
57
  </template>
40
58
 
@@ -42,11 +60,16 @@
42
60
  import { BasicTableProps, useTable } from '/@/hooks/table';
43
61
  import { fetchList, delObjs } from '/@/api/{{API_MODULE_PATH}}';
44
62
  import { useMessage, useMessageBox } from '/@/hooks/message';
45
- {{DICT_IMPORT_BLOCK}}
63
+ import { useDict } from '/@/hooks/dict';
64
+ import { useI18n } from 'vue-i18n';
65
+ import { allDictTypes, dataMasterEntity } from './options';
66
+
67
+ const dictRefs = useDict(...allDictTypes);
68
+ const { t } = useI18n();
46
69
  const router = useRouter();
70
+ const pageI18nKey = '{{I18N_NAMESPACE}}';
47
71
 
48
72
  const excelUploadRef = ref();
49
- const queryRef = ref();
50
73
  const showSearch = ref(true);
51
74
  const selectObjs = ref([]) as any;
52
75
  const multiple = ref(true);
@@ -56,34 +79,69 @@ const state: BasicTableProps = reactive<BasicTableProps>({
56
79
  pageList: fetchList,
57
80
  });
58
81
 
82
+ const resolveLabel = (labelKey?: string, fallback = '') => {
83
+ if (!labelKey) return fallback;
84
+ const translated = t(labelKey);
85
+ return translated === labelKey ? fallback : translated;
86
+ };
87
+
88
+ const actionLabel = (action: string) => t(`${pageI18nKey}.actions.${action}`);
89
+
90
+ const visibleTableColumns = computed(() =>
91
+ Object.entries(dataMasterEntity)
92
+ .filter(([, config]) => !config.alwaysHide && config.show !== false)
93
+ .map(([prop, config]) => ({
94
+ prop,
95
+ labelKey: config.labelKey,
96
+ fallbackLabel: config.label || prop,
97
+ width: config.width,
98
+ dictType: config.dictType || '',
99
+ }))
100
+ );
101
+
102
+ const getDictOptions = (dictType?: string) => (dictType ? dictRefs[dictType]?.value || [] : []);
103
+
59
104
  const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, downBlobFile, tableStyle } = useTable(state);
60
105
 
61
106
  const getFormPath = () => '/{{VIEW_MODULE_PATH}}/form';
62
107
 
63
108
  const handleCreate = () => {
64
- router.push({ path: getFormPath(), query: { tagsViewName: '新增' } });
109
+ router.push({ path: getFormPath(), query: { tagsViewName: t('common.addBtn') } });
65
110
  };
66
111
 
67
112
  const handleDetail = (id: string) => {
68
- router.push({ path: getFormPath(), query: { id, detail: '1', tagsViewName: '查看' } });
113
+ router.push({ path: getFormPath(), query: { id, detail: '1', tagsViewName: t('common.viewBtn') } });
69
114
  };
70
115
 
71
116
  const handleEdit = (id: string) => {
72
- router.push({ path: getFormPath(), query: { id, tagsViewName: '编辑' } });
117
+ router.push({ path: getFormPath(), query: { id, tagsViewName: t('common.editBtn') } });
118
+ };
119
+
120
+ const handleQuickAction = async (row: any, actionType: string) => {
121
+ const actionName = actionLabel(actionType);
122
+ try {
123
+ await useMessageBox().confirm(t(`${pageI18nKey}.messages.quickActionConfirm`, { action: actionName }));
124
+ useMessage().success(
125
+ actionType === 'submit'
126
+ ? t(`${pageI18nKey}.messages.quickSubmitSuccess`)
127
+ : t(`${pageI18nKey}.messages.quickFlowSuccess`)
128
+ );
129
+ getDataList();
130
+ } catch {}
73
131
  };
74
132
 
75
133
  const exportExcel = () => {
76
134
  downBlobFile('/{{API_PATH}}/export', { ...state.queryForm, ids: selectObjs.value }, '{{FUNCTION_NAME}}.xlsx');
77
135
  };
78
136
 
79
- const selectionChangeHandle = (objs: { {{PK_ATTR}}: string }[]) => {
80
- selectObjs.value = objs.map((item) => item.{{PK_ATTR}});
137
+ const selectionChangeHandle = (objs: { id: string }[]) => {
138
+ selectObjs.value = objs.map((item) => item.id);
81
139
  multiple.value = !objs.length;
82
140
  };
83
141
 
84
142
  const handleDelete = async (ids: string[]) => {
85
143
  try {
86
- await useMessageBox().confirm('此操作将永久删除');
144
+ await useMessageBox().confirm(t('common.delConfirmText'));
87
145
  } catch {
88
146
  return;
89
147
  }
@@ -91,9 +149,9 @@ const handleDelete = async (ids: string[]) => {
91
149
  try {
92
150
  await delObjs(ids);
93
151
  getDataList();
94
- useMessage().success('删除成功');
152
+ useMessage().success(t('common.delSuccessText'));
95
153
  } catch (err: any) {
96
- useMessage().error(err.msg || '删除失败');
154
+ useMessage().error(err.msg || t('common.delBtn'));
97
155
  }
98
156
  };
99
157
  </script>
@@ -1,10 +1,24 @@
1
+ {{DICT_REGISTRY_IMPORT_BLOCK}}
2
+ import { createCrudSchema } from '/@/utils/crudSchema';
3
+ import type { CrudSchemaDefinition } from '/@/utils/crudSchema';
4
+
1
5
  /**
2
- * Local codegen metadata for {{TABLE_NAME}}
6
+ * {{TABLE_NAME}} 页面字段声明。
7
+ * 这里只维护字段 key、双语 key、字典类型和显示元数据。
3
8
  */
4
- export const dataMasterEntity = {
5
- {{OPTIONS_FIELDS}}
9
+ const definition: CrudSchemaDefinition = {
10
+ master: [
11
+ {{MASTER_OPTION_FIELDS}}
12
+ ],
13
+ children: {
14
+ {{CHILD_OPTION_GROUPS}}
15
+ },
6
16
  };
7
17
 
8
- export const filterTypes = {
9
- {{FILTER_TYPES}}
10
- };
18
+ const schema = createCrudSchema(definition);
19
+
20
+ export const dataMasterEntity = schema.master;
21
+ export const childFieldGroups = schema.children;
22
+ export const filterTypes = schema.filterTypes;
23
+ export const childFilterTypes = schema.childFilterTypes;
24
+ export const allDictTypes = schema.allDictTypes;
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <el-dialog v-model="visible" :title="form.{{PK_ATTR}} ? '编辑' : '新增'" :close-on-click-modal="false" draggable>
2
+ <el-dialog v-model="visible" :title="form.{{PK_ATTR}} ? t('common.editBtn') : t('common.addBtn')" :close-on-click-modal="false" draggable>
3
3
  <el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
4
4
  <el-row :gutter="24">
5
5
  {{FORM_FIELDS}}
@@ -7,8 +7,8 @@
7
7
  </el-form>
8
8
  <template #footer>
9
9
  <span class="dialog-footer">
10
- <el-button @click="visible = false">取消</el-button>
11
- <el-button type="primary" @click="onSubmit" :disabled="loading">确认</el-button>
10
+ <el-button @click="visible = false">{{ t('common.cancelButtonText') }}</el-button>
11
+ <el-button type="primary" @click="onSubmit" :disabled="loading">{{ t('common.confirmButtonText') }}</el-button>
12
12
  </span>
13
13
  </template>
14
14
  </el-dialog>
@@ -17,14 +17,40 @@
17
17
  <script setup lang="ts" name="{{CLASS_NAME}}Dialog">
18
18
  import { useMessage } from '/@/hooks/message';
19
19
  import { getObj, addObj, putObj } from '/@/api/{{API_MODULE_PATH}}';
20
- {{FORM_DICT_IMPORT_BLOCK}}
20
+ import { useDict } from '/@/hooks/dict';
21
+ import { useI18n } from 'vue-i18n';
22
+ import { allDictTypes, dataMasterEntity } from './options';
21
23
 
24
+ const dictRefs = useDict(...allDictTypes);
25
+ const { t } = useI18n();
26
+ const pageI18nKey = '{{I18N_NAMESPACE}}';
22
27
  const emit = defineEmits(['refresh']);
23
28
 
24
29
  const dataFormRef = ref();
25
30
  const visible = ref(false);
26
31
  const loading = ref(false);
27
32
 
33
+ const masterFields = dataMasterEntity;
34
+
35
+ const getMasterFieldMeta = (prop: string) => masterFields[prop];
36
+ const isMasterFieldVisible = (prop: string) => {
37
+ const config = getMasterFieldMeta(prop);
38
+ return Boolean(config) && !config.alwaysHide && config.show !== false;
39
+ };
40
+ const resolveLabel = (labelKey?: string, fallback = '') => {
41
+ if (!labelKey) return fallback;
42
+ const translated = t(labelKey);
43
+ return translated === labelKey ? fallback : translated;
44
+ };
45
+ const getMasterFieldLabel = (prop: string) => {
46
+ const config = getMasterFieldMeta(prop);
47
+ return resolveLabel(config?.labelKey, config?.label || prop);
48
+ };
49
+ const getDictOptions = (dictType?: string) => (dictType ? dictRefs[dictType]?.value || [] : []);
50
+ const inputPlaceholder = (label: string) => t(`${pageI18nKey}.placeholders.input`, { label });
51
+ const selectPlaceholder = (label: string) => t(`${pageI18nKey}.placeholders.select`, { label });
52
+ const fieldRequiredMessage = (prop: string) => t(`${pageI18nKey}.messages.required`, { label: getMasterFieldLabel(prop) });
53
+
28
54
  const form = reactive({
29
55
  {{FORM_DEFAULTS}}
30
56
  });
@@ -39,7 +65,7 @@ const get{{CLASS_NAME}}Data = async (id: string) => {
39
65
  const { data } = await getObj({ {{PK_ATTR}}: id });
40
66
  Object.assign(form, data[0] || {});
41
67
  } catch (error) {
42
- useMessage().error('获取数据失败');
68
+ useMessage().error(t(`${pageI18nKey}.messages.fetchError`));
43
69
  } finally {
44
70
  loading.value = false;
45
71
  }
@@ -75,11 +101,11 @@ const onSubmit = async () => {
75
101
 
76
102
  try {
77
103
  form.{{PK_ATTR}} ? await putObj(form) : await addObj(form);
78
- useMessage().success(form.{{PK_ATTR}} ? '修改成功' : '添加成功');
104
+ useMessage().success(form.{{PK_ATTR}} ? t(`${pageI18nKey}.messages.updateSuccess`) : t(`${pageI18nKey}.messages.createSuccess`));
79
105
  visible.value = false;
80
106
  emit('refresh');
81
107
  } catch (err: any) {
82
- useMessage().error(err.msg || '提交失败');
108
+ useMessage().error(err.msg || t(`${pageI18nKey}.messages.submitError`));
83
109
  } finally {
84
110
  loading.value = false;
85
111
  }
@@ -88,4 +114,4 @@ const onSubmit = async () => {
88
114
  defineExpose({
89
115
  openDialog,
90
116
  });
91
- </script>
117
+ </script>
@@ -3,9 +3,9 @@
3
3
  <div class="layout-padding-auto layout-padding-view">
4
4
  <el-row>
5
5
  <div class="mb8" style="width: 100%">
6
- <el-button icon="folder-add" type="primary" class="ml10" v-auth="'{{PERMISSION_PREFIX}}_add'" @click="formDialogRef.openDialog()">新增</el-button>
7
- <el-button plain icon="upload-filled" type="primary" class="ml10" v-auth="'{{PERMISSION_PREFIX}}_add'" @click="excelUploadRef.show()">导入</el-button>
8
- <el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'{{PERMISSION_PREFIX}}_del'" @click="handleDelete(selectObjs)">删除</el-button>
6
+ <el-button icon="folder-add" type="primary" class="ml10" v-auth="'{{PERMISSION_PREFIX}}_add'" @click="formDialogRef.openDialog()">{{ t('common.addBtn') }}</el-button>
7
+ <el-button plain icon="upload-filled" type="primary" class="ml10" v-auth="'{{PERMISSION_PREFIX}}_add'" @click="excelUploadRef.show()">{{ t('common.importBtn') }}</el-button>
8
+ <el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'{{PERMISSION_PREFIX}}_del'" @click="handleDelete(selectObjs)">{{ t('common.delBtn') }}</el-button>
9
9
  <right-toolbar v-model:showSearch="showSearch" :export="'{{PERMISSION_PREFIX}}_export'" @exportExcel="exportExcel" @queryTable="getDataList" class="ml10 mr20" style="float: right;" />
10
10
  </div>
11
11
  </el-row>
@@ -20,12 +20,24 @@
20
20
  @sort-change="sortChangeHandle"
21
21
  >
22
22
  <el-table-column type="selection" width="40" align="center" />
23
- <el-table-column type="index" label="#" width="40" />
24
- {{TABLE_COLUMNS}}
25
- <el-table-column label="操作" width="180">
23
+ <el-table-column type="index" :label="t('common.serial')" width="60" />
24
+ <el-table-column
25
+ v-for="column in visibleTableColumns"
26
+ :key="column.prop"
27
+ :prop="column.prop"
28
+ :label="resolveLabel(column.labelKey, column.fallbackLabel)"
29
+ :min-width="column.width"
30
+ show-overflow-tooltip
31
+ >
26
32
  <template #default="scope">
27
- <el-button icon="edit-pen" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_edit'" @click="formDialogRef.openDialog(scope.row.{{PK_ATTR}})">编辑</el-button>
28
- <el-button icon="delete" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_del'" @click="handleDelete([scope.row.{{PK_ATTR}}])">删除</el-button>
33
+ <dict-tag v-if="column.dictType" :options="getDictOptions(column.dictType)" :value="scope.row[column.prop]" />
34
+ <span v-else>{{ scope.row[column.prop] }}</span>
35
+ </template>
36
+ </el-table-column>
37
+ <el-table-column :label="t('common.action')" width="180">
38
+ <template #default="scope">
39
+ <el-button icon="edit-pen" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_edit'" @click="formDialogRef.openDialog(scope.row.{{PK_ATTR}})">{{ t('common.editBtn') }}</el-button>
40
+ <el-button icon="delete" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_del'" @click="handleDelete([scope.row.{{PK_ATTR}}])">{{ t('common.delBtn') }}</el-button>
29
41
  </template>
30
42
  </el-table-column>
31
43
  </el-table>
@@ -34,7 +46,7 @@
34
46
  </div>
35
47
 
36
48
  <form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
37
- <upload-excel ref="excelUploadRef" title="导入" url="/{{API_PATH}}/import" temp-url="/admin/sys-file/local/file/{{FUNCTION_NAME}}.xlsx" @refreshDataList="getDataList" />
49
+ <upload-excel ref="excelUploadRef" :title="t('common.importBtn')" url="/{{API_PATH}}/import" temp-url="/admin/sys-file/local/file/{{FUNCTION_NAME}}.xlsx" @refreshDataList="getDataList" />
38
50
  </div>
39
51
  </template>
40
52
 
@@ -42,7 +54,12 @@
42
54
  import { BasicTableProps, useTable } from '/@/hooks/table';
43
55
  import { fetchList, delObjs } from '/@/api/{{API_MODULE_PATH}}';
44
56
  import { useMessage, useMessageBox } from '/@/hooks/message';
45
- {{TABLE_DICT_IMPORT_BLOCK}}
57
+ import { useDict } from '/@/hooks/dict';
58
+ import { useI18n } from 'vue-i18n';
59
+ import { allDictTypes, dataMasterEntity } from './options';
60
+
61
+ const dictRefs = useDict(...allDictTypes);
62
+ const { t } = useI18n();
46
63
 
47
64
  const FormDialog = defineAsyncComponent(() => import('./form.vue'));
48
65
 
@@ -57,6 +74,26 @@ const state: BasicTableProps = reactive<BasicTableProps>({
57
74
  pageList: fetchList,
58
75
  });
59
76
 
77
+ const resolveLabel = (labelKey?: string, fallback = '') => {
78
+ if (!labelKey) return fallback;
79
+ const translated = t(labelKey);
80
+ return translated === labelKey ? fallback : translated;
81
+ };
82
+
83
+ const visibleTableColumns = computed(() =>
84
+ Object.entries(dataMasterEntity)
85
+ .filter(([, config]) => !config.alwaysHide && config.show !== false)
86
+ .map(([prop, config]) => ({
87
+ prop,
88
+ labelKey: config.labelKey,
89
+ fallbackLabel: config.label || prop,
90
+ width: config.width,
91
+ dictType: config.dictType || '',
92
+ }))
93
+ );
94
+
95
+ const getDictOptions = (dictType?: string) => (dictType ? dictRefs[dictType]?.value || [] : []);
96
+
60
97
  const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, downBlobFile, tableStyle } = useTable(state);
61
98
 
62
99
  const exportExcel = () => {
@@ -70,7 +107,7 @@ const selectionChangeHandle = (objs: { {{PK_ATTR}}: string }[]) => {
70
107
 
71
108
  const handleDelete = async (ids: string[]) => {
72
109
  try {
73
- await useMessageBox().confirm('此操作将永久删除');
110
+ await useMessageBox().confirm(t('common.delConfirmText'));
74
111
  } catch {
75
112
  return;
76
113
  }
@@ -78,9 +115,9 @@ const handleDelete = async (ids: string[]) => {
78
115
  try {
79
116
  await delObjs(ids);
80
117
  getDataList();
81
- useMessage().success('删除成功');
118
+ useMessage().success(t('common.delSuccessText'));
82
119
  } catch (err: any) {
83
- useMessage().error(err.msg || '删除失败');
120
+ useMessage().error(err.msg || t('common.delBtn'));
84
121
  }
85
122
  };
86
- </script>
123
+ </script>
@@ -1,10 +1,24 @@
1
+ {{DICT_REGISTRY_IMPORT_BLOCK}}
2
+ import { createCrudSchema } from '/@/utils/crudSchema';
3
+ import type { CrudSchemaDefinition } from '/@/utils/crudSchema';
4
+
1
5
  /**
2
- * Local codegen metadata for {{TABLE_NAME}}
6
+ * {{TABLE_NAME}} 页面字段声明。
7
+ * 这里只维护字段 key、双语 key、字典类型和显示元数据。
3
8
  */
4
- export const dataMasterEntity = {
5
- {{OPTIONS_FIELDS}}
9
+ const definition: CrudSchemaDefinition = {
10
+ master: [
11
+ {{MASTER_OPTION_FIELDS}}
12
+ ],
13
+ children: {
14
+ {{CHILD_OPTION_GROUPS}}
15
+ },
6
16
  };
7
17
 
8
- export const filterTypes = {
9
- {{FILTER_TYPES}}
10
- };
18
+ const schema = createCrudSchema(definition);
19
+
20
+ export const dataMasterEntity = schema.master;
21
+ export const childFieldGroups = schema.children;
22
+ export const filterTypes = schema.filterTypes;
23
+ export const childFilterTypes = schema.childFilterTypes;
24
+ export const allDictTypes = schema.allDictTypes;