feat: 菜单名称国际化

This commit is contained in:
caiyuchao
2025-05-20 18:16:38 +08:00
parent 403eb9181c
commit b4d91ef9c6
8 changed files with 228 additions and 4 deletions

View File

@@ -5,6 +5,7 @@ export namespace SystemMenuApi {
export interface Menu {
id: number;
name: string;
i18nKey: string;
permission: string;
type: number;
sort: number;

View File

@@ -0,0 +1,85 @@
{
"system": {
"base": "System Management",
"tenant": {
"base": "Tenant Management",
"list": "Tenant List",
"package": "Tenant Package"
},
"user": "User Management",
"role": "Role Management",
"menu": "Menu Management",
"dept": "Department Management",
"post": "Post Management",
"dict": "Dictionary Management",
"msg": {
"base": "Message Center",
"sms": {
"base": "SMS Management",
"channel": "SMS Channel",
"template": "SMS Template",
"log": "SMS Log"
},
"mail": {
"base": "Email Management",
"account": "Email Account",
"template": "Email Template",
"log": "Email Log"
},
"notify": {
"base": "Internal Message Management",
"template": "Template Management",
"log": "Message Log"
},
"notice": "Notice Announcement"
},
"notice": {
"base": "Notice Announcement",
"list": "Notice List",
"template": "Notice Template"
},
"log": {
"base": "Audit Log",
"operation": "Operation Log",
"login": "Login Log"
},
"oauth": {
"base": "OAuth 2.0",
"client": "Application Management",
"token": "Token Management"
},
"social": {
"base": "Third Party Login",
"client": "Third Party Application",
"user": "Third Party User"
},
"area": "Area Management"
},
"infra": {
"base": "Infrastructure",
"codegen": "Code Generation",
"datasource": "Data Source Configuration",
"formConstruction": "Form Construction",
"api": "API Management",
"apiLog": {
"base": "API Log",
"access": "Access Log",
"error": "Error Log"
},
"webSocket": "WebSocket",
"file": {
"base": "File Management",
"config": "File Configuration",
"list": "File List"
},
"job": "Scheduled Tasks",
"config": "Config Management",
"monitor": {
"base": "Monitoring Center",
"mysql": "MySQL Monitoring",
"redis": "Redis Monitoring",
"java": "Java Monitoring",
"track": "Link Tracking"
}
}
}

View File

@@ -0,0 +1,85 @@
{
"system": {
"base": "系统管理",
"tenant": {
"base": "租户管理",
"list": "租户列表",
"package": "租户套餐"
},
"user": "用户管理",
"role": "角色管理",
"menu": "菜单管理",
"dept": "部门管理",
"post": "岗位管理",
"dict": "字典管理",
"msg": {
"base": "消息中心",
"sms": {
"base": "短信管理",
"channel": "短信渠道",
"template": "短信模板",
"log": "短信日志"
},
"mail": {
"base": "邮件管理",
"account": "邮箱账号",
"template": "邮件模板",
"log": "邮件记录"
},
"notify": {
"base": "站内信管理",
"template": "模板管理",
"log": "消息记录"
},
"notice": "通知公告"
},
"notice": {
"base": "通知公告",
"list": "通知公告列表",
"template": "通知公告模板"
},
"log": {
"base": "审计日志",
"operation": "操作日志",
"login": "登录日志"
},
"oauth": {
"base": "OAuth 2.0",
"client": "应用管理",
"token": "令牌管理"
},
"social": {
"base": "三方登录",
"client": "三方应用",
"user": "三方用户"
},
"area": "地区管理"
},
"infra": {
"base": "基础设施",
"codegen": "代码生成",
"datasource": "数据源配置",
"formConstruction": "表单构建",
"api": "API接口",
"apiLog": {
"base": "API日志",
"access": "访问日志",
"error": "错误日志"
},
"webSocket": "WebSocket",
"file": {
"base": "文件管理",
"config": "文件配置",
"list": "文件列表"
},
"job": "定时任务",
"config": "配置管理",
"monitor": {
"base": "监控中心",
"mysql": "MySQL 监控",
"redis": "Redis 监控",
"java": "Java 监控",
"track": "链路追踪"
}
}
}

View File

@@ -5,6 +5,7 @@ import type {
} from '@vben/types';
import { generateAccessible } from '@vben/access';
import { $te } from '@vben/locales';
import { preferences } from '@vben/preferences';
import { useAccessStore } from '@vben/stores';
import { convertServerMenuToRouteRecordStringComponent } from '@vben/utils';
@@ -28,7 +29,10 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
// 由于 yudao 通过 accessStore 读取,所以不在进行 message.loading 提示
// 补充说明accessStore.accessMenus 一开始是 AppRouteRecordRaw 类型(后端加载),后面被赋值成 MenuRecordRaw 类型(前端转换)
const accessMenus = accessStore.accessMenus as AppRouteRecordRaw[];
return convertServerMenuToRouteRecordStringComponent(accessMenus);
return convertServerMenuToRouteRecordStringComponent(
convertMenuToI18nKey(accessMenus),
);
},
// 可以指定没有权限跳转403页面
forbiddenComponent,
@@ -38,4 +42,24 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
});
}
/**
* 将菜单名称转换为国际化键
* @param menuList 菜单列表
* @returns 转换后的菜单列表
*/
function convertMenuToI18nKey(
menuList: AppRouteRecordRaw[],
): AppRouteRecordRaw[] {
return menuList.map((menu) => {
const { i18nKey } = menu;
if (i18nKey) {
menu.name = $te(i18nKey) ? i18nKey : menu.name;
}
if (menu.children) {
menu.children = convertMenuToI18nKey(menu.children);
}
return menu;
});
}
export { generateAccess };

View File

@@ -1,13 +1,16 @@
import type { ChangeEvent } from 'ant-design-vue/es/_util/EventInterface';
import type { Recordable } from '@vben/types';
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemMenuApi } from '#/api/system/menu';
import { h } from 'vue';
import { h, ref } from 'vue';
import { useAccess } from '@vben/access';
import { IconifyIcon } from '@vben/icons';
import { $te } from '@vben/locales';
import { handleTree, isHttpUrl } from '@vben/utils';
import { z } from '#/adapter/form';
@@ -23,6 +26,8 @@ import {
const { hasAccessByCodes } = useAccess();
export const titleSuffix = ref<string>();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -87,6 +92,20 @@ export function useFormSchema(): VbenFormSchema[] {
},
rules: 'required',
},
{
fieldName: 'i18nKey',
label: $t('common.i18nKey'),
component: 'Input',
componentProps() {
// 不需要处理多语言时就无需这么做
return {
addonAfter: titleSuffix.value,
onChange({ target: { value } }: ChangeEvent) {
titleSuffix.value = value && $te(value) ? $t(value) : undefined;
},
};
},
},
{
fieldName: 'type',
label: '菜单类型',

View File

@@ -157,7 +157,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
class="size-full"
/>
</div>
<span class="flex-auto">{{ $t(row.name) }}</span>
<span class="flex-auto">{{
$te(row.i18nKey) ? $t(row.i18nKey) : $t(row.name)
}}</span>
<div class="items-center justify-end"></div>
</div>
</template>

View File

@@ -4,6 +4,7 @@ import type { SystemMenuApi } from '#/api/system/menu';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { $te } from '@vben/locales';
import { message } from 'ant-design-vue';
@@ -11,7 +12,7 @@ import { useVbenForm } from '#/adapter/form';
import { createMenu, getMenu, updateMenu } from '#/api/system/menu';
import { $t } from '#/locales';
import { useFormSchema } from '../data';
import { titleSuffix, useFormSchema } from '../data';
const emit = defineEmits(['success']);
const formData = ref<SystemMenuApi.Menu>();
@@ -67,6 +68,12 @@ const [Modal, modalApi] = useVbenModal({
modalApi.lock();
try {
data = await getMenu(data.id as number);
if (data) {
titleSuffix.value =
data?.i18nKey && $te(data.i18nKey) ? $t(data.i18nKey) : '';
} else {
titleSuffix.value = '';
}
} finally {
modalApi.unlock();
}

View File

@@ -8,6 +8,7 @@ interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
componentName?: string;
components?: any;
fullPath?: string;
i18nKey: string;
icon?: string;
id?: any;
keepAlive?: boolean;