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.
- package/assets/templates/master_child_jump/form.tpl +60 -13
- package/assets/templates/master_child_jump/index.tpl +96 -38
- package/assets/templates/master_child_jump/options.tpl +20 -6
- package/assets/templates/single_table_dialog/form.tpl +34 -8
- package/assets/templates/single_table_dialog/index.tpl +51 -14
- package/assets/templates/single_table_dialog/options.tpl +20 -6
- package/assets/templates/single_table_jump/form.tpl +34 -7
- package/assets/templates/single_table_jump/index.tpl +54 -18
- package/assets/templates/single_table_jump/options.tpl +20 -6
- package/mcp_server.js +652 -26
- package/package.json +1 -1
|
@@ -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
|
-
<
|
|
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
|
-
{
|
|
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
|
-
|
|
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
|
-
<
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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="
|
|
24
|
-
|
|
25
|
-
|
|
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.{{
|
|
28
|
-
<el-button icon="edit-pen" text type="primary" v-auth="'{{PERMISSION_PREFIX}}_edit'" @click="handleEdit(scope.row.{{
|
|
29
|
-
<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
|
-
|
|
47
|
+
</el-table>
|
|
48
|
+
</div>
|
|
33
49
|
|
|
34
|
-
<
|
|
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="
|
|
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
|
-
{
|
|
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: {
|
|
80
|
-
selectObjs.value = objs.map((item) => item.
|
|
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
|
-
*
|
|
6
|
+
* {{TABLE_NAME}} 页面字段声明。
|
|
7
|
+
* 这里只维护字段 key、双语 key、字典类型和显示元数据。
|
|
3
8
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
9
|
+
const definition: CrudSchemaDefinition = {
|
|
10
|
+
master: [
|
|
11
|
+
{{MASTER_OPTION_FIELDS}}
|
|
12
|
+
],
|
|
13
|
+
children: {
|
|
14
|
+
{{CHILD_OPTION_GROUPS}}
|
|
15
|
+
},
|
|
6
16
|
};
|
|
7
17
|
|
|
8
|
-
|
|
9
|
-
|
|
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}} ? '
|
|
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"
|
|
11
|
-
<el-button type="primary" @click="onSubmit" :disabled="loading"
|
|
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
|
-
{
|
|
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()"
|
|
7
|
-
<el-button plain icon="upload-filled" type="primary" class="ml10" v-auth="'{{PERMISSION_PREFIX}}_add'" @click="excelUploadRef.show()"
|
|
8
|
-
<el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'{{PERMISSION_PREFIX}}_del'" @click="handleDelete(selectObjs)"
|
|
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="
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
<
|
|
28
|
-
<
|
|
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="
|
|
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
|
-
{
|
|
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
|
-
*
|
|
6
|
+
* {{TABLE_NAME}} 页面字段声明。
|
|
7
|
+
* 这里只维护字段 key、双语 key、字典类型和显示元数据。
|
|
3
8
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
9
|
+
const definition: CrudSchemaDefinition = {
|
|
10
|
+
master: [
|
|
11
|
+
{{MASTER_OPTION_FIELDS}}
|
|
12
|
+
],
|
|
13
|
+
children: {
|
|
14
|
+
{{CHILD_OPTION_GROUPS}}
|
|
15
|
+
},
|
|
6
16
|
};
|
|
7
17
|
|
|
8
|
-
|
|
9
|
-
|
|
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;
|