From b4d91ef9c65f8a30b328f5e6198978d97ef8769e Mon Sep 17 00:00:00 2001 From: caiyuchao Date: Tue, 20 May 2025 18:16:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=8F=9C=E5=8D=95=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E5=9B=BD=E9=99=85=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-antd/src/api/system/menu/index.ts | 1 + .../src/locales/langs/en-US/menu.json | 85 +++++++++++++++++++ .../src/locales/langs/zh-CN/menu.json | 85 +++++++++++++++++++ apps/web-antd/src/router/access.ts | 26 +++++- apps/web-antd/src/views/system/menu/data.ts | 21 ++++- apps/web-antd/src/views/system/menu/index.vue | 4 +- .../src/views/system/menu/modules/form.vue | 9 +- .../@core/base/typings/src/menu-record.ts | 1 + 8 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 apps/web-antd/src/locales/langs/en-US/menu.json create mode 100644 apps/web-antd/src/locales/langs/zh-CN/menu.json diff --git a/apps/web-antd/src/api/system/menu/index.ts b/apps/web-antd/src/api/system/menu/index.ts index 5d23e52..8bc539c 100644 --- a/apps/web-antd/src/api/system/menu/index.ts +++ b/apps/web-antd/src/api/system/menu/index.ts @@ -5,6 +5,7 @@ export namespace SystemMenuApi { export interface Menu { id: number; name: string; + i18nKey: string; permission: string; type: number; sort: number; diff --git a/apps/web-antd/src/locales/langs/en-US/menu.json b/apps/web-antd/src/locales/langs/en-US/menu.json new file mode 100644 index 0000000..85a062a --- /dev/null +++ b/apps/web-antd/src/locales/langs/en-US/menu.json @@ -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" + } + } +} diff --git a/apps/web-antd/src/locales/langs/zh-CN/menu.json b/apps/web-antd/src/locales/langs/zh-CN/menu.json new file mode 100644 index 0000000..99ac0b8 --- /dev/null +++ b/apps/web-antd/src/locales/langs/zh-CN/menu.json @@ -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": "链路追踪" + } + } +} diff --git a/apps/web-antd/src/router/access.ts b/apps/web-antd/src/router/access.ts index c4b161a..e4d64bd 100644 --- a/apps/web-antd/src/router/access.ts +++ b/apps/web-antd/src/router/access.ts @@ -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 }; diff --git a/apps/web-antd/src/views/system/menu/data.ts b/apps/web-antd/src/views/system/menu/data.ts index d4c6d97..22d8f0b 100644 --- a/apps/web-antd/src/views/system/menu/data.ts +++ b/apps/web-antd/src/views/system/menu/data.ts @@ -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(); + /** 新增/修改的表单 */ 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: '菜单类型', diff --git a/apps/web-antd/src/views/system/menu/index.vue b/apps/web-antd/src/views/system/menu/index.vue index 6ae33c5..24a4827 100644 --- a/apps/web-antd/src/views/system/menu/index.vue +++ b/apps/web-antd/src/views/system/menu/index.vue @@ -157,7 +157,9 @@ const [Grid, gridApi] = useVbenVxeGrid({ class="size-full" /> - {{ $t(row.name) }} + {{ + $te(row.i18nKey) ? $t(row.i18nKey) : $t(row.name) + }}
diff --git a/apps/web-antd/src/views/system/menu/modules/form.vue b/apps/web-antd/src/views/system/menu/modules/form.vue index e2b02bb..c1fd9e0 100644 --- a/apps/web-antd/src/views/system/menu/modules/form.vue +++ b/apps/web-antd/src/views/system/menu/modules/form.vue @@ -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(); @@ -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(); } diff --git a/packages/@core/base/typings/src/menu-record.ts b/packages/@core/base/typings/src/menu-record.ts index 50922ed..1255000 100644 --- a/packages/@core/base/typings/src/menu-record.ts +++ b/packages/@core/base/typings/src/menu-record.ts @@ -8,6 +8,7 @@ interface AppRouteRecordRaw extends Omit { componentName?: string; components?: any; fullPath?: string; + i18nKey: string; icon?: string; id?: any; keepAlive?: boolean;