@liujitcn/kratos-admin-system 0.0.20 → 0.0.21
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/README.md +51 -50
- package/dist/package/declarations/src/api/system/base_language.d.ts +51 -0
- package/dist/package/declarations/src/api/system/base_translation.d.ts +7 -0
- package/dist/package/declarations/src/api/system/code_gen_i18n.d.ts +8 -0
- package/dist/package/declarations/src/components/DynamicTranslationCell.vue.d.ts +39 -0
- package/dist/package/declarations/src/components/DynamicTranslationEditor.vue.d.ts +23 -0
- package/dist/package/declarations/src/components/dynamicTranslation.d.ts +44 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_config.d.ts +5 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +79 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_language.d.ts +114 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_menu.d.ts +40 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_translation.d.ts +102 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/code_gen.d.ts +2 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_column.d.ts +7 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_table.d.ts +13 -0
- package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue.d.ts +23 -0
- package/dist/package/declarations/src/views/tool/code-gen/config.d.ts +7 -7
- package/dist/package/declarations/src/views/tool/language/index.vue.d.ts +3 -0
- package/dist/package/src/api/base/ai_message.ts +7 -12
- package/dist/package/src/api/base/sse.ts +3 -1
- package/dist/package/src/api/system/base_language.ts +137 -0
- package/dist/package/src/api/system/base_translation.ts +22 -0
- package/dist/package/src/api/system/code_gen_column.ts +17 -3
- package/dist/package/src/api/system/code_gen_i18n.ts +18 -0
- package/dist/package/src/api/system/code_gen_table.ts +12 -4
- package/dist/package/src/components/DynamicTranslationCell.vue +176 -0
- package/dist/package/src/components/DynamicTranslationEditor.vue +166 -0
- package/dist/package/src/components/dynamicTranslation.ts +86 -0
- package/dist/package/src/locales/README.md +28 -0
- package/dist/package/src/locales/en-US.json +929 -0
- package/dist/package/src/locales/es-ES.json +928 -0
- package/dist/package/src/locales/fr-FR.json +928 -0
- package/dist/package/src/locales/ja-JP.json +929 -0
- package/dist/package/src/locales/ko-KR.json +928 -0
- package/dist/package/src/locales/zh-CN.json +929 -0
- package/dist/package/src/locales/zh-TW.json +928 -0
- package/dist/package/src/module.ts +19 -1
- package/dist/package/src/rpc/system/admin/v1/base_config.ts +5 -0
- package/dist/package/src/rpc/system/admin/v1/base_dict.ts +81 -0
- package/dist/package/src/rpc/system/admin/v1/base_language.ts +135 -0
- package/dist/package/src/rpc/system/admin/v1/base_menu.ts +41 -0
- package/dist/package/src/rpc/system/admin/v1/base_translation.ts +117 -0
- package/dist/package/src/rpc/system/admin/v1/code_gen.ts +5 -1
- package/dist/package/src/rpc/system/admin/v1/code_gen_column.ts +8 -0
- package/dist/package/src/rpc/system/admin/v1/code_gen_table.ts +15 -0
- package/dist/package/src/views/ai/chat/components/AiMarkdown.vue +1 -6
- package/dist/package/src/views/ai/chat/components/ChatPanel.vue +70 -53
- package/dist/package/src/views/ai/chat/components/SessionPanel.vue +20 -9
- package/dist/package/src/views/ai/chat/components/XSender.vue +36 -23
- package/dist/package/src/views/ai/chat/index.vue +55 -39
- package/dist/package/src/views/ai/chat/message.ts +18 -17
- package/dist/package/src/views/base/api/index.vue +121 -83
- package/dist/package/src/views/base/area/index.vue +81 -31
- package/dist/package/src/views/base/config/index.vue +272 -78
- package/dist/package/src/views/base/dept/index.vue +110 -61
- package/dist/package/src/views/base/dict/index.vue +137 -58
- package/dist/package/src/views/base/dict/item.vue +147 -61
- package/dist/package/src/views/base/job/index.vue +134 -81
- package/dist/package/src/views/base/job/log.vue +21 -22
- package/dist/package/src/views/base/log/index.vue +68 -46
- package/dist/package/src/views/base/menu/index.vue +257 -156
- package/dist/package/src/views/base/migration/index.vue +49 -29
- package/dist/package/src/views/base/post/index.vue +128 -59
- package/dist/package/src/views/base/role/index.vue +145 -78
- package/dist/package/src/views/base/tenant/index.vue +132 -63
- package/dist/package/src/views/base/user/components/dept-tree.vue +2 -1
- package/dist/package/src/views/base/user/index.vue +192 -112
- package/dist/package/src/views/profile/components/base.vue +44 -27
- package/dist/package/src/views/profile/components/password.vue +43 -27
- package/dist/package/src/views/profile/components/security.vue +89 -53
- package/dist/package/src/views/profile/index.vue +10 -9
- package/dist/package/src/views/tool/api-doc/index.vue +6 -2
- package/dist/package/src/views/tool/code-gen/code-preview/index.vue +30 -14
- package/dist/package/src/views/tool/code-gen/columns/index.vue +382 -273
- package/dist/package/src/views/tool/code-gen/columns/option-copy.ts +1 -6
- package/dist/package/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue +118 -0
- package/dist/package/src/views/tool/code-gen/components/CodeGenProgressDialog.vue +65 -36
- package/dist/package/src/views/tool/code-gen/components/CodePreviewPane.vue +8 -5
- package/dist/package/src/views/tool/code-gen/config.ts +114 -77
- package/dist/package/src/views/tool/code-gen/preview/data.ts +19 -17
- package/dist/package/src/views/tool/code-gen/preview/index.vue +96 -58
- package/dist/package/src/views/tool/code-gen/proto/index.vue +110 -62
- package/dist/package/src/views/tool/code-gen/table/index.vue +197 -117
- package/dist/package/src/views/tool/language/index.vue +256 -0
- package/dist/package/src/views/tool/project-doc/index.vue +17 -15
- package/package.json +6 -2
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="table-box">
|
|
3
3
|
<ProTable
|
|
4
4
|
ref="proTable"
|
|
5
|
-
title="
|
|
5
|
+
:title="t('system.menu.title.list')"
|
|
6
6
|
row-key="id"
|
|
7
7
|
:indent="20"
|
|
8
8
|
:columns="columns"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<FormDialog
|
|
17
17
|
v-model="dialog.visible"
|
|
18
18
|
ref="formDialogRef"
|
|
19
|
-
:title="dialog.
|
|
19
|
+
:title="t(dialog.editing ? 'system.menu.action.edit' : 'system.menu.action.create')"
|
|
20
20
|
width="1180px"
|
|
21
21
|
:model="formData"
|
|
22
22
|
:fields="formFields"
|
|
@@ -27,15 +27,25 @@
|
|
|
27
27
|
@close="handleCloseDialog"
|
|
28
28
|
>
|
|
29
29
|
<template #menuIcon>
|
|
30
|
-
<SelectIcon v-model:icon-value="menuIconValue" placeholder="
|
|
30
|
+
<SelectIcon v-model:icon-value="menuIconValue" :placeholder="t('system.menu.placeholder.icon')" />
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<template #translations>
|
|
34
|
+
<DynamicTranslationEditor
|
|
35
|
+
v-model="translationValues"
|
|
36
|
+
:resource-id="formData.id"
|
|
37
|
+
:resource-type="TranslationResourceType.TRANSLATION_RESOURCE_TYPE_MENU"
|
|
38
|
+
:draft-enabled="configStore.translationDraftEnabled"
|
|
39
|
+
:maxlength="100"
|
|
40
|
+
/>
|
|
31
41
|
</template>
|
|
32
42
|
|
|
33
43
|
<template #apiTransferItem="slotScope">
|
|
34
44
|
<el-popover effect="light" trigger="hover" placement="top" width="auto">
|
|
35
45
|
<template #default>
|
|
36
|
-
<div
|
|
37
|
-
<div
|
|
38
|
-
<div
|
|
46
|
+
<div>{{ t("system.api.field.operation") }}:{{ slotScope.option.operation }}</div>
|
|
47
|
+
<div>{{ t("system.api.field.method") }}:{{ slotScope.option.method }}</div>
|
|
48
|
+
<div>{{ t("system.api.field.path") }}:{{ slotScope.option.path }}</div>
|
|
39
49
|
</template>
|
|
40
50
|
<template #reference>{{ slotScope.option.label }}</template>
|
|
41
51
|
</el-popover>
|
|
@@ -49,10 +59,16 @@ import { computed, h, reactive, ref, resolveComponent, resolveDynamicComponent,
|
|
|
49
59
|
import { ElIcon, ElTag, type FormRules } from "element-plus";
|
|
50
60
|
import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
|
|
51
61
|
import FormDialog from "@liujitcn/kratos-admin-core/components/Dialog/FormDialog.vue";
|
|
52
|
-
import type {
|
|
62
|
+
import type {
|
|
63
|
+
ColumnProps,
|
|
64
|
+
HeaderActionProps,
|
|
65
|
+
ProTableInstance,
|
|
66
|
+
RenderScope
|
|
67
|
+
} from "@liujitcn/kratos-admin-core/components/ProTable/interface";
|
|
53
68
|
import type { ProFormField, ProFormOption } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
|
|
54
69
|
import SelectIcon from "@liujitcn/kratos-admin-core/components/SelectIcon/index.vue";
|
|
55
70
|
import { defBaseMenuService } from "@liujitcn/kratos-admin-system/api/system/base_menu";
|
|
71
|
+
import { loadEnabledBaseLanguages } from "@liujitcn/kratos-admin-system/api/system/base_language";
|
|
56
72
|
import { defBaseApiService } from "@liujitcn/kratos-admin-system/api/system/base_api";
|
|
57
73
|
import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
|
|
58
74
|
import type { BaseApi } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_api";
|
|
@@ -65,6 +81,18 @@ import type {
|
|
|
65
81
|
import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
|
|
66
82
|
import { BaseMenuType } from "@liujitcn/kratos-admin-system/rpc/system/common/v1/enum";
|
|
67
83
|
import { normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
|
|
84
|
+
import { t } from "@liujitcn/kratos-admin-core";
|
|
85
|
+
import { useConfigStore } from "@liujitcn/kratos-admin-core/stores/runtime";
|
|
86
|
+
import DynamicTranslationEditor from "@liujitcn/kratos-admin-system/components/DynamicTranslationEditor.vue";
|
|
87
|
+
import DynamicTranslationCell from "@liujitcn/kratos-admin-system/components/DynamicTranslationCell.vue";
|
|
88
|
+
import {
|
|
89
|
+
normalizeDynamicTranslations,
|
|
90
|
+
serializeDynamicTranslations,
|
|
91
|
+
type DynamicTranslationRecord,
|
|
92
|
+
type DynamicTranslationValue
|
|
93
|
+
} from "@liujitcn/kratos-admin-system/components/dynamicTranslation";
|
|
94
|
+
import { TranslationResourceType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_translation";
|
|
95
|
+
import type { BaseMenuTranslation } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_menu";
|
|
68
96
|
|
|
69
97
|
defineOptions({
|
|
70
98
|
name: "BaseMenu",
|
|
@@ -82,15 +110,17 @@ type MenuFormState = Omit<BaseMenuForm, "meta"> & {
|
|
|
82
110
|
};
|
|
83
111
|
|
|
84
112
|
const { BUTTONS } = useAuthButtons();
|
|
113
|
+
const configStore = useConfigStore();
|
|
85
114
|
const proTable = ref<ProTableInstance>();
|
|
86
115
|
const formDialogRef = ref<InstanceType<typeof FormDialog>>();
|
|
87
116
|
const menuOptions = ref<ProFormOption[]>([]);
|
|
88
117
|
const parentMenuTypeMap = ref(new Map<number, BaseMenuType>());
|
|
89
118
|
const appMenuIds = ref(new Set<number>([APP_MENU_ROOT_ID]));
|
|
90
119
|
const apiList = ref<BaseApi[]>([]);
|
|
120
|
+
const translationValues = ref<DynamicTranslationValue[]>(normalizeDynamicTranslations(undefined, "title"));
|
|
91
121
|
|
|
92
122
|
const dialog = reactive({
|
|
93
|
-
|
|
123
|
+
editing: false,
|
|
94
124
|
visible: false,
|
|
95
125
|
parentType: BaseMenuType.UNKNOWN_MT,
|
|
96
126
|
parentLocked: true
|
|
@@ -133,6 +163,7 @@ function createDefaultMenuForm(): MenuFormState {
|
|
|
133
163
|
redirect: "",
|
|
134
164
|
meta: createDefaultMenuMeta(),
|
|
135
165
|
api: [],
|
|
166
|
+
translations: [],
|
|
136
167
|
sort: 1,
|
|
137
168
|
status: Status.ENABLE
|
|
138
169
|
};
|
|
@@ -148,12 +179,12 @@ const menuIconValue = computed({
|
|
|
148
179
|
}
|
|
149
180
|
});
|
|
150
181
|
|
|
151
|
-
const menuTypeOptions
|
|
152
|
-
{ label: "
|
|
153
|
-
{ label: "
|
|
154
|
-
{ label: "
|
|
155
|
-
{ label: "
|
|
156
|
-
];
|
|
182
|
+
const menuTypeOptions = computed<ProFormOption[]>(() => [
|
|
183
|
+
{ label: t("system.menu.type.folder"), value: BaseMenuType.FOLDER },
|
|
184
|
+
{ label: t("system.menu.type.menu"), value: BaseMenuType.MENU },
|
|
185
|
+
{ label: t("system.menu.type.button"), value: BaseMenuType.BUTTON },
|
|
186
|
+
{ label: t("system.menu.type.external"), value: BaseMenuType.EXT_LINK }
|
|
187
|
+
]);
|
|
157
188
|
|
|
158
189
|
/** 判断父节点是否属于固定移动端菜单树。 */
|
|
159
190
|
function isAppMenu(parentId?: number) {
|
|
@@ -187,17 +218,17 @@ function canCreateChild(menu: BaseMenu) {
|
|
|
187
218
|
const availableMenuTypeOptions = computed(() => {
|
|
188
219
|
const parentLevel = getMenuLevel(formData.parent_id ?? 0);
|
|
189
220
|
|
|
190
|
-
if (formData.id === APP_MENU_ROOT_ID) return menuTypeOptions.filter(item => item.value === BaseMenuType.FOLDER);
|
|
191
|
-
if (isAppMenu(formData.parent_id)) return menuTypeOptions.filter(item => item.value === BaseMenuType.MENU);
|
|
192
|
-
if (formData.id > 0 && parentLevel === 0) return menuTypeOptions.filter(item => item.value === BaseMenuType.FOLDER);
|
|
221
|
+
if (formData.id === APP_MENU_ROOT_ID) return menuTypeOptions.value.filter(item => item.value === BaseMenuType.FOLDER);
|
|
222
|
+
if (isAppMenu(formData.parent_id)) return menuTypeOptions.value.filter(item => item.value === BaseMenuType.MENU);
|
|
223
|
+
if (formData.id > 0 && parentLevel === 0) return menuTypeOptions.value.filter(item => item.value === BaseMenuType.FOLDER);
|
|
193
224
|
if (dialog.parentType === BaseMenuType.FOLDER && parentLevel === 1)
|
|
194
|
-
return menuTypeOptions.filter(
|
|
225
|
+
return menuTypeOptions.value.filter(
|
|
195
226
|
item => item.value === BaseMenuType.FOLDER || item.value === BaseMenuType.MENU || item.value === BaseMenuType.EXT_LINK
|
|
196
227
|
);
|
|
197
228
|
if (dialog.parentType === BaseMenuType.FOLDER && parentLevel === 2)
|
|
198
|
-
return menuTypeOptions.filter(item => item.value === BaseMenuType.MENU || item.value === BaseMenuType.EXT_LINK);
|
|
229
|
+
return menuTypeOptions.value.filter(item => item.value === BaseMenuType.MENU || item.value === BaseMenuType.EXT_LINK);
|
|
199
230
|
if (dialog.parentType === BaseMenuType.MENU && (parentLevel === 2 || parentLevel === 3))
|
|
200
|
-
return menuTypeOptions.filter(item => item.value === BaseMenuType.BUTTON);
|
|
231
|
+
return menuTypeOptions.value.filter(item => item.value === BaseMenuType.BUTTON);
|
|
201
232
|
return [];
|
|
202
233
|
});
|
|
203
234
|
|
|
@@ -216,16 +247,16 @@ watch(
|
|
|
216
247
|
}
|
|
217
248
|
);
|
|
218
249
|
|
|
219
|
-
const statusOptions
|
|
220
|
-
{ label: "
|
|
221
|
-
{ label: "
|
|
222
|
-
];
|
|
250
|
+
const statusOptions = computed<ProFormOption[]>(() => [
|
|
251
|
+
{ label: t("common.status.enabled"), value: Status.ENABLE },
|
|
252
|
+
{ label: t("common.status.disabled"), value: Status.DISABLE }
|
|
253
|
+
]);
|
|
223
254
|
|
|
224
|
-
const appAccessOptions
|
|
225
|
-
{ label: "
|
|
226
|
-
{ label: "
|
|
227
|
-
{ label: "
|
|
228
|
-
];
|
|
255
|
+
const appAccessOptions = computed<ProFormOption[]>(() => [
|
|
256
|
+
{ label: t("system.menu.access.public"), value: "PUBLIC" },
|
|
257
|
+
{ label: t("system.menu.access.guest"), value: "GUEST_ONLY" },
|
|
258
|
+
{ label: t("system.menu.access.authenticated"), value: "AUTHENTICATED" }
|
|
259
|
+
]);
|
|
229
260
|
|
|
230
261
|
/**
|
|
231
262
|
* 渲染菜单图标单元格,统一兼容 Element Plus 图标和本地 svg 图标。
|
|
@@ -252,7 +283,19 @@ function renderMenuIconCell(scope: RenderScope<BaseMenu>) {
|
|
|
252
283
|
*/
|
|
253
284
|
function renderHiddenCell(scope: RenderScope<BaseMenu>) {
|
|
254
285
|
const isHidden = Boolean(scope.row.meta?.hidden);
|
|
255
|
-
return h(ElTag, { type: isHidden ? "info" : "success" }, () =>
|
|
286
|
+
return h(ElTag, { type: isHidden ? "info" : "success" }, () =>
|
|
287
|
+
t(isHidden ? "system.menu.status.hidden" : "system.menu.status.visible")
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** 渲染菜单标题翻译预览,并复用当前页面的编辑弹窗。 */
|
|
292
|
+
function renderMenuTitleCell(scope: RenderScope<BaseMenu>) {
|
|
293
|
+
const row = scope.row;
|
|
294
|
+
return h(DynamicTranslationCell, {
|
|
295
|
+
source: row.meta?.title ?? "",
|
|
296
|
+
translations: row.translations,
|
|
297
|
+
textField: "title"
|
|
298
|
+
});
|
|
256
299
|
}
|
|
257
300
|
|
|
258
301
|
/** 菜单表格列配置。 */
|
|
@@ -267,26 +310,28 @@ const columns = computed<ColumnProps[]>(() => [
|
|
|
267
310
|
},
|
|
268
311
|
{
|
|
269
312
|
prop: "meta.title",
|
|
270
|
-
label: "
|
|
313
|
+
label: t("system.menu.field.name"),
|
|
271
314
|
minWidth: 220,
|
|
272
315
|
align: "left",
|
|
273
|
-
search: { el: "input", key: "title" }
|
|
316
|
+
search: { el: "input", key: "title" },
|
|
317
|
+
showOverflowTooltip: false,
|
|
318
|
+
render: scope => renderMenuTitleCell(scope as unknown as RenderScope<BaseMenu>)
|
|
274
319
|
},
|
|
275
|
-
{ prop: "type", label: "
|
|
320
|
+
{ prop: "type", label: t("system.menu.field.type"), minWidth: 120, dictCode: "base_menu_type", search: { el: "select" } },
|
|
276
321
|
{
|
|
277
322
|
prop: "meta.icon",
|
|
278
|
-
label: "
|
|
323
|
+
label: t("system.menu.field.icon"),
|
|
279
324
|
width: 90,
|
|
280
325
|
render: scope => renderMenuIconCell(scope as unknown as RenderScope<BaseMenu>)
|
|
281
326
|
},
|
|
282
|
-
{ prop: "path", label: "
|
|
283
|
-
{ prop: "name", label: "
|
|
284
|
-
{ prop: "component", label: "
|
|
285
|
-
{ prop: "redirect", label: "
|
|
286
|
-
{ prop: "sort", label: "
|
|
327
|
+
{ prop: "path", label: t("system.menu.field.pathOrPermission"), minWidth: 260, search: { el: "input" } },
|
|
328
|
+
{ prop: "name", label: t("system.menu.field.routeName"), minWidth: 180, search: { el: "input" } },
|
|
329
|
+
{ prop: "component", label: t("system.menu.field.component"), minWidth: 260 },
|
|
330
|
+
{ prop: "redirect", label: t("system.menu.field.redirect"), minWidth: 220 },
|
|
331
|
+
{ prop: "sort", label: t("system.common.field.sort"), minWidth: 80, align: "right" },
|
|
287
332
|
{
|
|
288
333
|
prop: "status",
|
|
289
|
-
label: "
|
|
334
|
+
label: t("system.common.field.status"),
|
|
290
335
|
width: 100,
|
|
291
336
|
dictCode: "status",
|
|
292
337
|
search: { el: "select" },
|
|
@@ -294,29 +339,29 @@ const columns = computed<ColumnProps[]>(() => [
|
|
|
294
339
|
statusProps: {
|
|
295
340
|
activeValue: Status.ENABLE,
|
|
296
341
|
inactiveValue: Status.DISABLE,
|
|
297
|
-
activeText: "
|
|
298
|
-
inactiveText: "
|
|
342
|
+
activeText: t("common.status.enabled"),
|
|
343
|
+
inactiveText: t("common.status.disabled"),
|
|
299
344
|
disabled: () => !BUTTONS.value["base:menu:status"],
|
|
300
345
|
beforeChange: scope => handleBeforeSetStatus(scope.row as BaseMenu)
|
|
301
346
|
}
|
|
302
347
|
},
|
|
303
348
|
{
|
|
304
349
|
prop: "meta.hidden",
|
|
305
|
-
label: "
|
|
350
|
+
label: t("system.menu.field.displayStatus"),
|
|
306
351
|
width: 100,
|
|
307
352
|
render: scope => renderHiddenCell(scope as unknown as RenderScope<BaseMenu>)
|
|
308
353
|
},
|
|
309
|
-
{ prop: "created_at", label: "
|
|
310
|
-
{ prop: "updated_at", label: "
|
|
354
|
+
{ prop: "created_at", label: t("system.common.field.createdAt"), minWidth: 180 },
|
|
355
|
+
{ prop: "updated_at", label: t("system.common.field.updatedAt"), minWidth: 180 },
|
|
311
356
|
{
|
|
312
357
|
prop: "operation",
|
|
313
|
-
label: "
|
|
358
|
+
label: t("system.common.field.action"),
|
|
314
359
|
width: 220,
|
|
315
360
|
fixed: "right",
|
|
316
361
|
cellType: "actions",
|
|
317
362
|
actions: [
|
|
318
363
|
{
|
|
319
|
-
label: "
|
|
364
|
+
label: t("common.action.create"),
|
|
320
365
|
type: "primary",
|
|
321
366
|
link: true,
|
|
322
367
|
icon: CirclePlus,
|
|
@@ -324,7 +369,7 @@ const columns = computed<ColumnProps[]>(() => [
|
|
|
324
369
|
onClick: scope => handleOpenDialog(scope.row as BaseMenu)
|
|
325
370
|
},
|
|
326
371
|
{
|
|
327
|
-
label: "
|
|
372
|
+
label: t("common.action.edit"),
|
|
328
373
|
type: "primary",
|
|
329
374
|
link: true,
|
|
330
375
|
icon: EditPen,
|
|
@@ -332,7 +377,7 @@ const columns = computed<ColumnProps[]>(() => [
|
|
|
332
377
|
onClick: scope => handleOpenDialog(undefined, (scope.row as BaseMenu).id)
|
|
333
378
|
},
|
|
334
379
|
{
|
|
335
|
-
label: "
|
|
380
|
+
label: t("common.action.delete"),
|
|
336
381
|
type: "danger",
|
|
337
382
|
link: true,
|
|
338
383
|
icon: Delete,
|
|
@@ -349,14 +394,14 @@ const columns = computed<ColumnProps[]>(() => [
|
|
|
349
394
|
/** 菜单表格顶部按钮配置。 */
|
|
350
395
|
const headerActions = computed<HeaderActionProps[]>(() => [
|
|
351
396
|
{
|
|
352
|
-
label: "
|
|
397
|
+
label: t("common.action.create"),
|
|
353
398
|
type: "success",
|
|
354
399
|
icon: CirclePlus,
|
|
355
400
|
hidden: () => !BUTTONS.value["base:menu:create"],
|
|
356
401
|
onClick: () => handleOpenDialog()
|
|
357
402
|
},
|
|
358
403
|
{
|
|
359
|
-
label: "
|
|
404
|
+
label: t("common.action.delete"),
|
|
360
405
|
type: "danger",
|
|
361
406
|
icon: Delete,
|
|
362
407
|
hidden: () => !BUTTONS.value["base:menu:delete"],
|
|
@@ -378,7 +423,7 @@ const transferData = computed<ProFormOption[]>(() => {
|
|
|
378
423
|
const formFields = computed<ProFormField[]>(() => [
|
|
379
424
|
{
|
|
380
425
|
prop: "parent_id",
|
|
381
|
-
label: "
|
|
426
|
+
label: t("system.menu.field.parent"),
|
|
382
427
|
component: "tree-select",
|
|
383
428
|
options: menuOptions.value,
|
|
384
429
|
props: () => ({
|
|
@@ -387,14 +432,14 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
387
432
|
checkStrictly: true,
|
|
388
433
|
clearable: false,
|
|
389
434
|
filterable: true,
|
|
390
|
-
placeholder: "
|
|
435
|
+
placeholder: t("system.menu.placeholder.parent"),
|
|
391
436
|
disabled: dialog.parentLocked,
|
|
392
437
|
style: { width: "100%" }
|
|
393
438
|
})
|
|
394
439
|
},
|
|
395
440
|
{
|
|
396
441
|
prop: "type",
|
|
397
|
-
label: "
|
|
442
|
+
label: t("system.menu.field.type"),
|
|
398
443
|
component: "radio-group",
|
|
399
444
|
options: availableMenuTypeOptions.value,
|
|
400
445
|
props: model => ({
|
|
@@ -403,15 +448,24 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
403
448
|
},
|
|
404
449
|
{
|
|
405
450
|
prop: "meta.title",
|
|
406
|
-
label: formData.type === BaseMenuType.BUTTON ? "
|
|
451
|
+
label: t(formData.type === BaseMenuType.BUTTON ? "system.menu.field.buttonName" : "system.menu.field.title"),
|
|
407
452
|
component: "input",
|
|
408
453
|
itemProps: {
|
|
409
454
|
required: true
|
|
410
455
|
},
|
|
411
456
|
props: () => ({
|
|
412
|
-
placeholder:
|
|
457
|
+
placeholder: t(
|
|
458
|
+
formData.type === BaseMenuType.BUTTON ? "system.menu.placeholder.buttonName" : "system.menu.placeholder.title"
|
|
459
|
+
)
|
|
413
460
|
})
|
|
414
461
|
},
|
|
462
|
+
{
|
|
463
|
+
prop: "translations",
|
|
464
|
+
label: t("system.translation.field.translations"),
|
|
465
|
+
component: "slot",
|
|
466
|
+
slotName: "translations",
|
|
467
|
+
colSpan: 24
|
|
468
|
+
},
|
|
415
469
|
{
|
|
416
470
|
prop: "path",
|
|
417
471
|
label: getPathFieldLabel(),
|
|
@@ -437,7 +491,7 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
437
491
|
},
|
|
438
492
|
{
|
|
439
493
|
prop: "meta.icon",
|
|
440
|
-
label: "
|
|
494
|
+
label: t("system.menu.field.icon"),
|
|
441
495
|
component: "slot",
|
|
442
496
|
slotName: "menuIcon",
|
|
443
497
|
itemProps: model => ({
|
|
@@ -447,65 +501,65 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
447
501
|
},
|
|
448
502
|
{
|
|
449
503
|
prop: "meta.icon",
|
|
450
|
-
label: "
|
|
504
|
+
label: t("system.menu.field.defaultIcon"),
|
|
451
505
|
component: "input",
|
|
452
|
-
labelTooltip: "
|
|
506
|
+
labelTooltip: t("system.menu.tooltip.mobileIcon"),
|
|
453
507
|
itemProps: model => ({
|
|
454
508
|
required: Boolean(model.meta?.app?.in_tab_bar)
|
|
455
509
|
}),
|
|
456
|
-
props: { placeholder: "
|
|
510
|
+
props: { placeholder: t("system.menu.placeholder.defaultIcon") },
|
|
457
511
|
visible: model => isAppMenu(model.parent_id)
|
|
458
512
|
},
|
|
459
513
|
{
|
|
460
514
|
prop: "name",
|
|
461
|
-
label: isAppMenu(formData.parent_id) ? "
|
|
515
|
+
label: t(isAppMenu(formData.parent_id) ? "system.menu.field.logicalName" : "system.menu.field.routeName"),
|
|
462
516
|
component: "input",
|
|
463
|
-
labelTooltip: isAppMenu(formData.parent_id)
|
|
464
|
-
? "移动端页面使用 App 前缀的唯一逻辑名称。"
|
|
465
|
-
: "如果需要开启缓存,需保证页面 defineOptions 中的 name 与此处一致,建议使用驼峰。",
|
|
517
|
+
labelTooltip: isAppMenu(formData.parent_id) ? t("system.menu.tooltip.logicalName") : t("system.menu.tooltip.routeName"),
|
|
466
518
|
itemProps: model => ({
|
|
467
519
|
required: model.type === BaseMenuType.MENU
|
|
468
520
|
}),
|
|
469
521
|
props: () => ({
|
|
470
|
-
placeholder: isAppMenu(formData.parent_id)
|
|
522
|
+
placeholder: isAppMenu(formData.parent_id)
|
|
523
|
+
? t("system.menu.placeholder.logicalName")
|
|
524
|
+
: t("system.menu.placeholder.routeName")
|
|
471
525
|
}),
|
|
472
526
|
visible: model => model.type === BaseMenuType.MENU
|
|
473
527
|
},
|
|
474
528
|
{
|
|
475
529
|
prop: "meta.app.view_key",
|
|
476
|
-
label: "
|
|
530
|
+
label: t("system.menu.field.viewKey"),
|
|
477
531
|
component: "input",
|
|
478
|
-
labelTooltip: "
|
|
532
|
+
labelTooltip: t("system.menu.tooltip.viewKey"),
|
|
479
533
|
itemProps: { required: true },
|
|
480
|
-
props: { placeholder: "
|
|
534
|
+
props: { placeholder: t("system.menu.placeholder.viewKey") },
|
|
481
535
|
visible: model => isAppMenu(model.parent_id)
|
|
482
536
|
},
|
|
483
537
|
{
|
|
484
538
|
prop: "meta.app.access",
|
|
485
|
-
label: "
|
|
539
|
+
label: t("system.menu.field.access"),
|
|
486
540
|
component: "select",
|
|
487
|
-
options: appAccessOptions,
|
|
541
|
+
options: appAccessOptions.value,
|
|
488
542
|
itemProps: { required: true },
|
|
489
543
|
props: {
|
|
490
544
|
clearable: false,
|
|
491
|
-
placeholder: "
|
|
545
|
+
placeholder: t("system.menu.placeholder.access")
|
|
492
546
|
},
|
|
493
547
|
visible: model => isAppMenu(model.parent_id)
|
|
494
548
|
},
|
|
495
549
|
{
|
|
496
550
|
prop: "meta.app.selected_icon",
|
|
497
|
-
label: "
|
|
551
|
+
label: t("system.menu.field.selectedIcon"),
|
|
498
552
|
component: "input",
|
|
499
|
-
labelTooltip: "
|
|
553
|
+
labelTooltip: t("system.menu.tooltip.mobileIcon"),
|
|
500
554
|
itemProps: { required: true },
|
|
501
|
-
props: { placeholder: "
|
|
555
|
+
props: { placeholder: t("system.menu.placeholder.selectedIcon") },
|
|
502
556
|
visible: model => isAppTab(model.parent_id)
|
|
503
557
|
},
|
|
504
558
|
{
|
|
505
559
|
prop: "component",
|
|
506
|
-
label: "
|
|
560
|
+
label: t("system.menu.field.component"),
|
|
507
561
|
component: "input",
|
|
508
|
-
labelTooltip: "
|
|
562
|
+
labelTooltip: t("system.menu.tooltip.component"),
|
|
509
563
|
itemProps: model => ({
|
|
510
564
|
required: model.type === BaseMenuType.MENU && !isAppMenu(model.parent_id)
|
|
511
565
|
}),
|
|
@@ -514,12 +568,12 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
514
568
|
},
|
|
515
569
|
{
|
|
516
570
|
prop: "meta.hidden",
|
|
517
|
-
label: "
|
|
571
|
+
label: t("system.menu.field.hidden"),
|
|
518
572
|
component: "switch",
|
|
519
573
|
props: {
|
|
520
574
|
inlinePrompt: true,
|
|
521
|
-
activeText: "
|
|
522
|
-
inactiveText: "
|
|
575
|
+
activeText: t("system.common.value.yes"),
|
|
576
|
+
inactiveText: t("system.common.value.no"),
|
|
523
577
|
activeValue: true,
|
|
524
578
|
inactiveValue: false
|
|
525
579
|
},
|
|
@@ -527,13 +581,13 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
527
581
|
},
|
|
528
582
|
{
|
|
529
583
|
prop: "meta.always_show",
|
|
530
|
-
label: "
|
|
584
|
+
label: t("system.menu.field.alwaysShow"),
|
|
531
585
|
component: "switch",
|
|
532
|
-
labelTooltip: "
|
|
586
|
+
labelTooltip: t("system.menu.tooltip.alwaysShow"),
|
|
533
587
|
props: {
|
|
534
588
|
inlinePrompt: true,
|
|
535
|
-
activeText: "
|
|
536
|
-
inactiveText: "
|
|
589
|
+
activeText: t("system.common.value.yes"),
|
|
590
|
+
inactiveText: t("system.common.value.no"),
|
|
537
591
|
activeValue: true,
|
|
538
592
|
inactiveValue: false
|
|
539
593
|
},
|
|
@@ -541,12 +595,12 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
541
595
|
},
|
|
542
596
|
{
|
|
543
597
|
prop: "meta.keep_alive",
|
|
544
|
-
label: "
|
|
598
|
+
label: t("system.menu.field.keepAlive"),
|
|
545
599
|
component: "switch",
|
|
546
600
|
props: {
|
|
547
601
|
inlinePrompt: true,
|
|
548
|
-
activeText: "
|
|
549
|
-
inactiveText: "
|
|
602
|
+
activeText: t("common.status.enabled"),
|
|
603
|
+
inactiveText: t("common.status.disabled"),
|
|
550
604
|
activeValue: true,
|
|
551
605
|
inactiveValue: false
|
|
552
606
|
},
|
|
@@ -554,12 +608,12 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
554
608
|
},
|
|
555
609
|
{
|
|
556
610
|
prop: "meta.full",
|
|
557
|
-
label: "
|
|
611
|
+
label: t("system.menu.field.fullscreen"),
|
|
558
612
|
component: "switch",
|
|
559
613
|
props: {
|
|
560
614
|
inlinePrompt: true,
|
|
561
|
-
activeText: "
|
|
562
|
-
inactiveText: "
|
|
615
|
+
activeText: t("common.status.enabled"),
|
|
616
|
+
inactiveText: t("common.status.disabled"),
|
|
563
617
|
activeValue: true,
|
|
564
618
|
inactiveValue: false
|
|
565
619
|
},
|
|
@@ -567,12 +621,12 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
567
621
|
},
|
|
568
622
|
{
|
|
569
623
|
prop: "meta.affix",
|
|
570
|
-
label: "
|
|
624
|
+
label: t("system.menu.field.affix"),
|
|
571
625
|
component: "switch",
|
|
572
626
|
props: {
|
|
573
627
|
inlinePrompt: true,
|
|
574
|
-
activeText: "
|
|
575
|
-
inactiveText: "
|
|
628
|
+
activeText: t("common.status.enabled"),
|
|
629
|
+
inactiveText: t("common.status.disabled"),
|
|
576
630
|
activeValue: true,
|
|
577
631
|
inactiveValue: false
|
|
578
632
|
},
|
|
@@ -580,13 +634,13 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
580
634
|
},
|
|
581
635
|
{
|
|
582
636
|
prop: "meta.params",
|
|
583
|
-
label: "
|
|
637
|
+
label: t("system.menu.field.routeParams"),
|
|
584
638
|
component: "kv-list",
|
|
585
|
-
labelTooltip: "
|
|
639
|
+
labelTooltip: t("system.menu.tooltip.routeParams"),
|
|
586
640
|
props: {
|
|
587
|
-
addText: "
|
|
588
|
-
keyInputProps: { placeholder: "
|
|
589
|
-
valueInputProps: { placeholder: "
|
|
641
|
+
addText: t("system.menu.action.addRouteParam"),
|
|
642
|
+
keyInputProps: { placeholder: t("common.field.parameterName") },
|
|
643
|
+
valueInputProps: { placeholder: t("common.field.parameterValue") }
|
|
590
644
|
},
|
|
591
645
|
itemProps: {
|
|
592
646
|
class: "menu-form__params"
|
|
@@ -595,20 +649,20 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
595
649
|
},
|
|
596
650
|
{
|
|
597
651
|
prop: "api",
|
|
598
|
-
label: "
|
|
652
|
+
label: t("system.menu.field.apiList"),
|
|
599
653
|
component: "transfer",
|
|
600
654
|
slotName: "apiTransferItem",
|
|
601
655
|
options: transferData.value,
|
|
602
656
|
props: {
|
|
603
657
|
filterable: true,
|
|
604
|
-
titles: ["
|
|
658
|
+
titles: [t("system.menu.value.availableApi"), t("system.menu.value.selectedApi")]
|
|
605
659
|
},
|
|
606
660
|
visible: model => model.id === APP_MENU_ROOT_ID || model.type === BaseMenuType.MENU || model.type === BaseMenuType.BUTTON,
|
|
607
661
|
colSpan: 24
|
|
608
662
|
},
|
|
609
663
|
{
|
|
610
664
|
prop: "sort",
|
|
611
|
-
label: "
|
|
665
|
+
label: t("system.common.field.sort"),
|
|
612
666
|
component: "input-number",
|
|
613
667
|
itemProps: {
|
|
614
668
|
required: true
|
|
@@ -623,23 +677,35 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
623
677
|
},
|
|
624
678
|
{
|
|
625
679
|
prop: "status",
|
|
626
|
-
label: "
|
|
680
|
+
label: t("system.common.field.status"),
|
|
627
681
|
component: "radio-group",
|
|
628
682
|
itemProps: {
|
|
629
683
|
required: true
|
|
630
684
|
},
|
|
631
|
-
options: statusOptions
|
|
685
|
+
options: statusOptions.value
|
|
632
686
|
}
|
|
633
687
|
]);
|
|
634
688
|
|
|
635
689
|
const rules = computed<FormRules>(() => ({
|
|
636
|
-
parent_id: formData.id
|
|
637
|
-
|
|
690
|
+
parent_id: formData.id
|
|
691
|
+
? []
|
|
692
|
+
: [{ required: true, type: "number", min: 1, message: t("system.menu.placeholder.parent"), trigger: "change" }],
|
|
693
|
+
type: [
|
|
694
|
+
{
|
|
695
|
+
required: true,
|
|
696
|
+
message: t("system.common.validation.requiredSelect", { field: t("system.menu.field.type") }),
|
|
697
|
+
trigger: "change"
|
|
698
|
+
}
|
|
699
|
+
],
|
|
638
700
|
"meta.title": [
|
|
639
701
|
{
|
|
640
702
|
validator: (_rule, value, callback) => {
|
|
641
703
|
if (value) return callback();
|
|
642
|
-
callback(
|
|
704
|
+
callback(
|
|
705
|
+
new Error(
|
|
706
|
+
t(formData.type === BaseMenuType.BUTTON ? "system.menu.placeholder.buttonName" : "system.menu.placeholder.title")
|
|
707
|
+
)
|
|
708
|
+
);
|
|
643
709
|
},
|
|
644
710
|
trigger: "blur"
|
|
645
711
|
}
|
|
@@ -650,7 +716,9 @@ const rules = computed<FormRules>(() => ({
|
|
|
650
716
|
if (formData.type === BaseMenuType.BUTTON) return callback();
|
|
651
717
|
if (isAppMenu(formData.parent_id) && !isAppTab(formData.parent_id)) return callback();
|
|
652
718
|
if (value) return callback();
|
|
653
|
-
callback(
|
|
719
|
+
callback(
|
|
720
|
+
new Error(t(isAppMenu(formData.parent_id) ? "system.menu.validation.defaultIcon" : "system.menu.placeholder.icon"))
|
|
721
|
+
);
|
|
654
722
|
},
|
|
655
723
|
trigger: "change"
|
|
656
724
|
}
|
|
@@ -659,7 +727,7 @@ const rules = computed<FormRules>(() => ({
|
|
|
659
727
|
{
|
|
660
728
|
validator: (_rule, value, callback) => {
|
|
661
729
|
if (!isAppMenu(formData.parent_id) || value) return callback();
|
|
662
|
-
callback(new Error("
|
|
730
|
+
callback(new Error(t("system.menu.validation.viewKey")));
|
|
663
731
|
},
|
|
664
732
|
trigger: "blur"
|
|
665
733
|
}
|
|
@@ -668,7 +736,7 @@ const rules = computed<FormRules>(() => ({
|
|
|
668
736
|
{
|
|
669
737
|
validator: (_rule, value, callback) => {
|
|
670
738
|
if (!isAppMenu(formData.parent_id) || value) return callback();
|
|
671
|
-
callback(new Error("
|
|
739
|
+
callback(new Error(t("system.menu.placeholder.access")));
|
|
672
740
|
},
|
|
673
741
|
trigger: "change"
|
|
674
742
|
}
|
|
@@ -677,23 +745,31 @@ const rules = computed<FormRules>(() => ({
|
|
|
677
745
|
{
|
|
678
746
|
validator: (_rule, value, callback) => {
|
|
679
747
|
if (!isAppTab(formData.parent_id) || value) return callback();
|
|
680
|
-
callback(new Error("
|
|
748
|
+
callback(new Error(t("system.menu.validation.selectedIcon")));
|
|
681
749
|
},
|
|
682
750
|
trigger: "blur"
|
|
683
751
|
}
|
|
684
752
|
],
|
|
685
753
|
path: [
|
|
686
|
-
{
|
|
754
|
+
{
|
|
755
|
+
max: 1024,
|
|
756
|
+
message: t("system.common.validation.maxLength", { field: t("system.menu.field.path"), max: 1024 }),
|
|
757
|
+
trigger: "blur"
|
|
758
|
+
},
|
|
687
759
|
{
|
|
688
760
|
validator: (_rule, value, callback) => {
|
|
689
761
|
if (formData.type === BaseMenuType.FOLDER) return callback();
|
|
690
762
|
if (!value) {
|
|
691
|
-
if (formData.type === BaseMenuType.BUTTON) return callback(new Error("
|
|
692
|
-
if (formData.type === BaseMenuType.EXT_LINK) return callback(new Error("
|
|
693
|
-
return callback(
|
|
763
|
+
if (formData.type === BaseMenuType.BUTTON) return callback(new Error(t("system.menu.validation.permission")));
|
|
764
|
+
if (formData.type === BaseMenuType.EXT_LINK) return callback(new Error(t("system.menu.validation.externalUrl")));
|
|
765
|
+
return callback(
|
|
766
|
+
new Error(
|
|
767
|
+
t(isAppMenu(formData.parent_id) ? "system.menu.validation.logicalPath" : "system.menu.validation.routePath")
|
|
768
|
+
)
|
|
769
|
+
);
|
|
694
770
|
}
|
|
695
771
|
if (isAppMenu(formData.parent_id) && !String(value).startsWith("app/")) {
|
|
696
|
-
return callback(new Error("
|
|
772
|
+
return callback(new Error(t("system.menu.validation.appPathPrefix")));
|
|
697
773
|
}
|
|
698
774
|
callback();
|
|
699
775
|
},
|
|
@@ -701,24 +777,37 @@ const rules = computed<FormRules>(() => ({
|
|
|
701
777
|
}
|
|
702
778
|
],
|
|
703
779
|
redirect: [
|
|
704
|
-
{
|
|
780
|
+
{
|
|
781
|
+
max: 1024,
|
|
782
|
+
message: t("system.common.validation.maxLength", { field: t("system.menu.field.redirect"), max: 1024 }),
|
|
783
|
+
trigger: "blur"
|
|
784
|
+
},
|
|
705
785
|
{
|
|
706
786
|
validator: (_rule, value, callback) => {
|
|
707
787
|
if (formData.type !== BaseMenuType.EXT_LINK) return callback();
|
|
708
788
|
if (/^https?:\/\/\S+$/.test(value)) return callback();
|
|
709
|
-
callback(new Error("
|
|
789
|
+
callback(new Error(t("system.menu.validation.httpUrl")));
|
|
710
790
|
},
|
|
711
791
|
trigger: "blur"
|
|
712
792
|
}
|
|
713
793
|
],
|
|
714
794
|
name: [
|
|
715
|
-
{
|
|
795
|
+
{
|
|
796
|
+
max: 255,
|
|
797
|
+
message: t("system.common.validation.maxLength", { field: t("system.menu.field.routeName"), max: 255 }),
|
|
798
|
+
trigger: "blur"
|
|
799
|
+
},
|
|
716
800
|
{
|
|
717
801
|
validator: (_rule, value, callback) => {
|
|
718
802
|
if (formData.type !== BaseMenuType.MENU) return callback();
|
|
719
|
-
if (!value)
|
|
803
|
+
if (!value)
|
|
804
|
+
return callback(
|
|
805
|
+
new Error(
|
|
806
|
+
t(isAppMenu(formData.parent_id) ? "system.menu.validation.logicalName" : "system.menu.placeholder.routeName")
|
|
807
|
+
)
|
|
808
|
+
);
|
|
720
809
|
if (isAppMenu(formData.parent_id) && !String(value).startsWith("App")) {
|
|
721
|
-
return callback(new Error("
|
|
810
|
+
return callback(new Error(t("system.menu.validation.appNamePrefix")));
|
|
722
811
|
}
|
|
723
812
|
callback();
|
|
724
813
|
},
|
|
@@ -726,32 +815,42 @@ const rules = computed<FormRules>(() => ({
|
|
|
726
815
|
}
|
|
727
816
|
],
|
|
728
817
|
component: [
|
|
729
|
-
{
|
|
818
|
+
{
|
|
819
|
+
max: 255,
|
|
820
|
+
message: t("system.common.validation.maxLength", { field: t("system.menu.field.component"), max: 255 }),
|
|
821
|
+
trigger: "blur"
|
|
822
|
+
},
|
|
730
823
|
{
|
|
731
824
|
validator: (_rule, value, callback) => {
|
|
732
825
|
if (formData.type !== BaseMenuType.MENU || isAppMenu(formData.parent_id)) return callback();
|
|
733
826
|
if (value) return callback();
|
|
734
|
-
callback(new Error("
|
|
827
|
+
callback(new Error(t("system.menu.validation.component")));
|
|
735
828
|
},
|
|
736
829
|
trigger: "blur"
|
|
737
830
|
}
|
|
738
831
|
],
|
|
739
|
-
sort: [{ required: true, type: "number", min: 1, message: "
|
|
740
|
-
status: [
|
|
832
|
+
sort: [{ required: true, type: "number", min: 1, message: t("system.common.validation.sortPositive"), trigger: "blur" }],
|
|
833
|
+
status: [
|
|
834
|
+
{
|
|
835
|
+
required: true,
|
|
836
|
+
message: t("system.common.validation.requiredSelect", { field: t("system.common.field.status") }),
|
|
837
|
+
trigger: "change"
|
|
838
|
+
}
|
|
839
|
+
]
|
|
741
840
|
}));
|
|
742
841
|
|
|
743
842
|
/** 计算当前路径字段文案。 */
|
|
744
843
|
function getPathFieldLabel() {
|
|
745
|
-
if (isAppMenu(formData.parent_id)) return "
|
|
746
|
-
if (formData.type === BaseMenuType.BUTTON) return "
|
|
747
|
-
if (formData.type === BaseMenuType.EXT_LINK) return "
|
|
748
|
-
return "
|
|
844
|
+
if (isAppMenu(formData.parent_id)) return t("system.menu.field.logicalPath");
|
|
845
|
+
if (formData.type === BaseMenuType.BUTTON) return t("system.menu.field.permission");
|
|
846
|
+
if (formData.type === BaseMenuType.EXT_LINK) return t("system.menu.field.internalPath");
|
|
847
|
+
return t("system.menu.field.path");
|
|
749
848
|
}
|
|
750
849
|
|
|
751
850
|
/** 计算当前路径字段占位文案。 */
|
|
752
851
|
function getPathFieldPlaceholder() {
|
|
753
|
-
if (isAppMenu(formData.parent_id)) return "
|
|
754
|
-
if (formData.type === BaseMenuType.BUTTON) return "
|
|
852
|
+
if (isAppMenu(formData.parent_id)) return t("system.menu.placeholder.logicalPath");
|
|
853
|
+
if (formData.type === BaseMenuType.BUTTON) return t("system.menu.placeholder.permission");
|
|
755
854
|
if (formData.type === BaseMenuType.EXT_LINK) return "external/baidu";
|
|
756
855
|
if (formData.type === BaseMenuType.FOLDER) return "base";
|
|
757
856
|
return "user";
|
|
@@ -759,21 +858,21 @@ function getPathFieldPlaceholder() {
|
|
|
759
858
|
|
|
760
859
|
/** 计算当前路径字段提示文案。 */
|
|
761
860
|
function getPathFieldTooltip() {
|
|
762
|
-
if (isAppMenu(formData.parent_id)) return "
|
|
763
|
-
if (formData.type === BaseMenuType.BUTTON) return "
|
|
764
|
-
if (formData.type === BaseMenuType.EXT_LINK) return "
|
|
765
|
-
if (formData.type === BaseMenuType.FOLDER) return "
|
|
766
|
-
return "
|
|
861
|
+
if (isAppMenu(formData.parent_id)) return t("system.menu.tooltip.logicalPath");
|
|
862
|
+
if (formData.type === BaseMenuType.BUTTON) return t("system.menu.tooltip.permission");
|
|
863
|
+
if (formData.type === BaseMenuType.EXT_LINK) return t("system.menu.tooltip.internalPath");
|
|
864
|
+
if (formData.type === BaseMenuType.FOLDER) return t("system.menu.tooltip.folderPath");
|
|
865
|
+
return t("system.menu.tooltip.routePath");
|
|
767
866
|
}
|
|
768
867
|
|
|
769
868
|
/** 计算重定向字段文案。 */
|
|
770
869
|
function getRedirectFieldLabel() {
|
|
771
|
-
return formData.type === BaseMenuType.EXT_LINK ? "
|
|
870
|
+
return t(formData.type === BaseMenuType.EXT_LINK ? "system.menu.field.externalUrl" : "system.menu.field.redirectRoute");
|
|
772
871
|
}
|
|
773
872
|
|
|
774
873
|
/** 计算重定向字段占位文案。 */
|
|
775
874
|
function getRedirectFieldPlaceholder() {
|
|
776
|
-
return formData.type === BaseMenuType.EXT_LINK ? "https://www.
|
|
875
|
+
return formData.type === BaseMenuType.EXT_LINK ? "https://www.example.com/" : t("system.menu.placeholder.redirectRoute");
|
|
777
876
|
}
|
|
778
877
|
|
|
779
878
|
/** 判断当前图标是否为 Element Plus 图标。 */
|
|
@@ -863,6 +962,7 @@ function buildMenuOptions(menuList: BaseMenu[] = []) {
|
|
|
863
962
|
/** 根据菜单类型清理无效字段,避免提交脏数据。 */
|
|
864
963
|
function buildSubmitPayload(): BaseMenuForm {
|
|
865
964
|
const payload = normalizeMenuForm(formData);
|
|
965
|
+
payload.translations = serializeDynamicTranslations(translationValues.value, "title") as unknown as BaseMenuTranslation[];
|
|
866
966
|
// 一级菜单在表单中保持空白,提交时仍按接口约定传回根节点标识。
|
|
867
967
|
if (payload.id > 0 && payload.parent_id === undefined) payload.parent_id = 0;
|
|
868
968
|
payload.meta.params = (payload.meta.params ?? []).filter(item => item.key || item.value);
|
|
@@ -953,6 +1053,7 @@ function filterMenuTree(menuList: BaseMenu[], keywordMap: Record<string, string>
|
|
|
953
1053
|
|
|
954
1054
|
/** 请求菜单表格数据,并按搜索条件过滤树形结构。 */
|
|
955
1055
|
async function requestMenuTable(params: Record<string, string>) {
|
|
1056
|
+
await loadEnabledBaseLanguages();
|
|
956
1057
|
const data = await defBaseMenuService.TreeBaseMenu({});
|
|
957
1058
|
const keywordMap = {
|
|
958
1059
|
title: params.title ?? "",
|
|
@@ -975,20 +1076,19 @@ function refreshTable() {
|
|
|
975
1076
|
* parentMenu 为新增时的固定父节点,menuId 为编辑时的菜单 ID。
|
|
976
1077
|
*/
|
|
977
1078
|
async function handleOpenDialog(parentMenu?: BaseMenu, menuId?: number) {
|
|
1079
|
+
await loadEnabledBaseLanguages();
|
|
978
1080
|
await loadDialogResources();
|
|
979
1081
|
dialog.parentLocked = Boolean(parentMenu || menuId);
|
|
1082
|
+
dialog.editing = Boolean(menuId);
|
|
980
1083
|
dialog.parentType = parentMenu?.type ?? BaseMenuType.UNKNOWN_MT;
|
|
981
1084
|
resetForm(menuId ? undefined : { parent_id: parentMenu?.id });
|
|
982
1085
|
dialog.visible = true;
|
|
983
1086
|
|
|
984
1087
|
if (menuId) {
|
|
985
|
-
dialog.title = "修改菜单";
|
|
986
1088
|
const data = await defBaseMenuService.GetBaseMenu({ id: menuId });
|
|
987
1089
|
resetForm(data);
|
|
988
1090
|
return;
|
|
989
1091
|
}
|
|
990
|
-
|
|
991
|
-
dialog.title = "新增菜单";
|
|
992
1092
|
}
|
|
993
1093
|
|
|
994
1094
|
/** 关闭菜单弹窗并显式重置表单与校验状态。 */
|
|
@@ -1004,6 +1104,7 @@ function resetForm(data?: Partial<BaseMenuForm>) {
|
|
|
1004
1104
|
formDialogRef.value?.resetFields();
|
|
1005
1105
|
formDialogRef.value?.clearValidate();
|
|
1006
1106
|
Object.assign(formData, normalizeMenuForm(data));
|
|
1107
|
+
translationValues.value = normalizeDynamicTranslations(data?.translations as DynamicTranslationRecord[] | undefined, "title");
|
|
1007
1108
|
}
|
|
1008
1109
|
|
|
1009
1110
|
/** 提交菜单表单,并在成功后关闭弹窗、刷新表格。 */
|
|
@@ -1014,10 +1115,10 @@ async function handleSubmit() {
|
|
|
1014
1115
|
const payload = buildSubmitPayload();
|
|
1015
1116
|
if (payload.id > 0) {
|
|
1016
1117
|
await defBaseMenuService.UpdateBaseMenu({ base_menu: payload });
|
|
1017
|
-
ElMessage.success("
|
|
1118
|
+
ElMessage.success(t("system.menu.message.updateSuccess"));
|
|
1018
1119
|
} else {
|
|
1019
1120
|
await defBaseMenuService.CreateBaseMenu({ base_menu: payload });
|
|
1020
|
-
ElMessage.success("
|
|
1121
|
+
ElMessage.success(t("system.menu.message.createSuccess"));
|
|
1021
1122
|
}
|
|
1022
1123
|
|
|
1023
1124
|
handleCloseDialog();
|
|
@@ -1029,16 +1130,16 @@ async function handleSubmit() {
|
|
|
1029
1130
|
*/
|
|
1030
1131
|
async function handleBeforeSetStatus(row: BaseMenu) {
|
|
1031
1132
|
const nextStatus = row.status === Status.ENABLE ? Status.DISABLE : Status.ENABLE;
|
|
1032
|
-
const
|
|
1133
|
+
const action = t(nextStatus === Status.ENABLE ? "common.status.enabled" : "common.status.disabled");
|
|
1033
1134
|
const menuName = row.meta?.title || row.name || row.path || `ID:${row.id}`;
|
|
1034
1135
|
try {
|
|
1035
|
-
await ElMessageBox.confirm(
|
|
1036
|
-
confirmButtonText: "
|
|
1037
|
-
cancelButtonText: "
|
|
1136
|
+
await ElMessageBox.confirm(t("system.menu.message.confirmStatus", { action, name: menuName }), t("common.title.notice"), {
|
|
1137
|
+
confirmButtonText: t("common.action.confirm"),
|
|
1138
|
+
cancelButtonText: t("common.action.cancel"),
|
|
1038
1139
|
type: "warning"
|
|
1039
1140
|
});
|
|
1040
1141
|
await defBaseMenuService.SetBaseMenuStatus({ id: row.id, status: nextStatus });
|
|
1041
|
-
ElMessage.success(
|
|
1142
|
+
ElMessage.success(t("system.common.message.statusSuccess", { action }));
|
|
1042
1143
|
refreshTable();
|
|
1043
1144
|
return true;
|
|
1044
1145
|
} catch {
|
|
@@ -1056,37 +1157,37 @@ function handleDeleteMenu(selected?: number | string | Array<number | string> |
|
|
|
1056
1157
|
? [selected as BaseMenu]
|
|
1057
1158
|
: [];
|
|
1058
1159
|
if (menuList.some(item => item.parent_id === 0 || item.id === APP_MENU_ROOT_ID)) {
|
|
1059
|
-
ElMessage.warning("
|
|
1160
|
+
ElMessage.warning(t("system.menu.message.protectedDelete"));
|
|
1060
1161
|
return;
|
|
1061
1162
|
}
|
|
1062
1163
|
const menuIds = (
|
|
1063
1164
|
menuList.length ? menuList.map(item => item.id) : normalizeSelectedIds(selected as number | string | Array<number | string>)
|
|
1064
1165
|
).join(",");
|
|
1065
1166
|
if (!menuIds) {
|
|
1066
|
-
ElMessage.warning("
|
|
1167
|
+
ElMessage.warning(t("system.common.message.selectDeleteItem"));
|
|
1067
1168
|
return;
|
|
1068
1169
|
}
|
|
1069
1170
|
|
|
1070
1171
|
const singleMenuName = menuList[0]?.meta?.title || menuList[0]?.name || menuList[0]?.path || `ID:${menuList[0]?.id ?? ""}`;
|
|
1071
1172
|
const confirmMessage = menuList.length
|
|
1072
1173
|
? menuList.length === 1
|
|
1073
|
-
?
|
|
1074
|
-
:
|
|
1075
|
-
: "
|
|
1174
|
+
? t("system.menu.message.confirmDeleteSingle", { name: singleMenuName })
|
|
1175
|
+
: t("system.menu.message.confirmDeleteBatch", { count: menuList.length })
|
|
1176
|
+
: t("system.menu.message.confirmDeleteSelected");
|
|
1076
1177
|
|
|
1077
|
-
ElMessageBox.confirm(confirmMessage, "
|
|
1078
|
-
confirmButtonText: "
|
|
1079
|
-
cancelButtonText: "
|
|
1178
|
+
ElMessageBox.confirm(confirmMessage, t("common.title.warning"), {
|
|
1179
|
+
confirmButtonText: t("common.action.confirm"),
|
|
1180
|
+
cancelButtonText: t("common.action.cancel"),
|
|
1080
1181
|
type: "warning"
|
|
1081
1182
|
}).then(
|
|
1082
1183
|
() => {
|
|
1083
1184
|
defBaseMenuService.DeleteBaseMenu({ id: menuIds }).then(() => {
|
|
1084
|
-
ElMessage.success("
|
|
1185
|
+
ElMessage.success(t("system.menu.message.deleteSuccess"));
|
|
1085
1186
|
refreshTable();
|
|
1086
1187
|
});
|
|
1087
1188
|
},
|
|
1088
1189
|
() => {
|
|
1089
|
-
ElMessage.info("
|
|
1190
|
+
ElMessage.info(t("system.menu.message.deleteCanceled"));
|
|
1090
1191
|
}
|
|
1091
1192
|
);
|
|
1092
1193
|
}
|