From 055cac97393a26a0a1b192d3e5fc4f00a3b822af Mon Sep 17 00:00:00 2001
From: koushenhai <2413176044@qq.com>
Date: Thu, 5 Mar 2026 18:48:04 +0800
Subject: [PATCH 1/8] =?UTF-8?q?feat:=20UI=E5=A2=9E=E5=8A=A0=E5=89=8D?=
=?UTF-8?q?=E7=AB=AF=E5=9B=BD=E9=99=85=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/config/config.ts | 6 +++
ui/config/routes.ts | 45 ++++++++++-----------
ui/src/app.tsx | 33 +++++++++++-----
ui/src/pages/Login/index.tsx | 76 +++++++++++++++++++-----------------
4 files changed, 90 insertions(+), 70 deletions(-)
diff --git a/ui/config/config.ts b/ui/config/config.ts
index d07df62a90..ca07fa740d 100644
--- a/ui/config/config.ts
+++ b/ui/config/config.ts
@@ -36,6 +36,12 @@ export default defineConfig({
layout: {
title: '老寇IoT云平台',
},
+ locale: {
+ // umi/max 内置 i18n
+ default: 'zh-CN',
+ antd: true,
+ baseNavigator: true,
+ },
/**
* 路由的配置,不在路由中引入的文件不会编译.
* @description 只支持 path,component,routes,redirect,wrappers,title 的配置
diff --git a/ui/config/routes.ts b/ui/config/routes.ts
index 51c241d36e..d5268a21ef 100644
--- a/ui/config/routes.ts
+++ b/ui/config/routes.ts
@@ -17,91 +17,86 @@ export default [
redirect: '/home',
},
{
- name: '首页',
+ name: 'menu.home',
path: '/home',
component: './Home',
icon: 'home'
},
{
- name: 'Login',
+ name: 'menu.login',
path: '/login',
component: './Login',
layout: false,
},
{
- name: '系统管理',
+ name: 'menu.sys',
path: '/sys',
icon: 'setting',
routes: [
{
- name: '权限管理',
+ name: 'menu.sys.permission',
path: '/sys/permission',
routes: [
{
- name: '菜单',
- path: '/sys/permission/menu',
- component: './Sys/Permission/menu'
- },
- {
- name: '部门',
+ name: 'menu.sys.permission.dept',
path: '/sys/permission/dept',
component: './Sys/Permission/dept'
},
{
- name: '角色',
+ name: 'menu.sys.permission.role',
path: '/sys/permission/role',
component: './Sys/Permission/role'
},
{
- name: '用户',
+ name: 'menu.sys.permission.user',
path: '/sys/permission/user',
component: './Sys/Permission/user'
},
]
},
{
- name: '日志管理',
+ name: 'menu.sys.log',
path: '/sys/log',
routes: [
{
- name: '登录日志',
+ name: 'menu.sys.log.login',
path: '/sys/log/login',
component: './Sys/Log/login'
},
{
- name: '通知日志',
+ name: 'menu.sys.log.notice',
path: '/sys/log/notice',
component: './Sys/Log/notice'
},
{
- name: '操作日志',
+ name: 'menu.sys.log.operate',
path: '/sys/log/operate',
component: './Sys/Log/operate'
}
]
},
{
- name: '对象存储',
+ name: 'menu.sys.oss',
path: '/sys/oss',
routes: [
{
- name: '对象存储配置',
+ name: 'menu.sys.oss.config',
path: '/sys/oss/config',
component: './Sys/Oss/config'
},
{
- name: '对象存储日志',
+ name: 'menu.sys.oss.log',
path: '/sys/oss/log',
component: './Sys/Oss/log'
}
]
},
{
- name: '系统配置',
+ name: 'menu.sys.config',
path: '/sys/config',
routes: [
{
- name: '代码生成器',
+ name: 'menu.sys.config.generator',
path: '/sys/config/generator',
component: './Sys/Config/generator'
}
@@ -110,21 +105,21 @@ export default [
]
},
{
- name: '物联管理',
+ name: 'menu.iot',
path: '/iot',
icon: 'robot',
routes: [
{
- name: '设备管理',
+ name: 'menu.iot.device',
path: '/iot/device',
routes: [
{
- name: '物模型',
+ name: 'menu.iot.device.thingModel',
path: '/iot/device/thingModel',
component: './IoT/Device/thingModel'
},
{
- name: '产品类别',
+ name: 'menu.iot.device.productCategory',
path: '/iot/device/productCategory',
component: './IoT/Device/productCategory'
},
diff --git a/ui/src/app.tsx b/ui/src/app.tsx
index 070b15eede..46723f64ac 100644
--- a/ui/src/app.tsx
+++ b/ui/src/app.tsx
@@ -3,7 +3,7 @@
// 全局初始化数据配置,用于 Layout 用户信息和权限初始化
// 更多信息见文档:https://umijs.org/docs/api/runtime-config#getinitialstate
import {Dropdown, message, theme} from "antd";
-import {history} from "@umijs/max";
+import {history, SelectLang, useIntl} from "@@/exports";
import {HomeOutlined, LogoutOutlined, RobotOutlined, SettingOutlined} from "@ant-design/icons";
import {ReactElement, ReactNode, ReactPortal} from "react";
import {logout, refresh} from '@/services/auth/auth';
@@ -28,7 +28,7 @@ const getIcon = (icon: string) => {
const getRouters = (menus: any[]) => {
const routers = [{
- name: '首页',
+ name: 'menu.home',
path: '/home',
icon:
}]
@@ -111,6 +111,9 @@ export async function getInitialState(): Promise<{
}
export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
+ // 新写法:使用 intl.formatMessage,替代 formatMessage()
+ const intl = useIntl();
+ const t = (id: string, values?: Record) => intl.formatMessage({id}, values);
return {
// 面包屑配置
headerContentRender: () => ,
@@ -131,6 +134,10 @@ export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
colorPrimary: "#1677ff",
fixedHeader: true,
siderMenuType: "sub",
+ actionsRender: () => {
+ // Ant Design Pro 风格的语言切换组件(来自 umi plugin-locale)
+ return [];
+ },
avatarProps: {
src: initialState?.avatar,
size: 'small',
@@ -143,7 +150,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
{
key: 'logout',
icon: ,
- label: '注销',
+ label: t('user.logout'),
onClick: async () => {
if (refreshTimeoutRef) {
clearTimeout(refreshTimeoutRef);
@@ -203,6 +210,14 @@ export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
};
};
+const t = (id: string, values?: Record) => {
+ // 新写法:在非 React 组件/Hook 环境下(如 request errorHandler)使用 getIntl()
+ // getIntl 来自 umi plugin-locale(@@/exports 导出)
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
+ const {getIntl} = require('@@/exports');
+ return getIntl().formatMessage({id}, values);
+};
+
export const request: {
responseInterceptors: ((response: any) => any)[];
requestInterceptors: (((config: any) => any) | ((error: any) => any))[];
@@ -211,7 +226,7 @@ export const request: {
} = {
timeout: 60000,
// other axios options you want
- errorConfig: {
+ errorConfig: {
errorHandler(error: any) {
const {request, response, code} = error;
let errorMessage;
@@ -219,19 +234,19 @@ export const request: {
errorMessage = response.data.error_description
}
if (response && response.status === 500) {
- errorMessage = '服务器内部错误,无法完成请求'
+ errorMessage = t('error.serverInternal')
}
if (code === 'ERR_BAD_RESPONSE') {
- errorMessage = '网络请求错误,请稍后再试'
+ errorMessage = t('error.network')
}
if (response && response.status === 400 && response.data.error === "invalid_grant") {
- errorMessage = "令牌续期失败,请重新登录"
+ errorMessage = t('error.refreshTokenFailed')
}
if (response && response.status === 404) {
- errorMessage = "无法找到 " + request.responseURL + " 请求的资源"
+ errorMessage = t('error.resourceNotFound', {url: request?.responseURL})
}
if (response && response.status === 401 && response.data.error === "invalid_client") {
- errorMessage = "无效客户端,请检查认证服务器配置"
+ errorMessage = t('error.invalidClient')
}
message.error(errorMessage).then();
},
diff --git a/ui/src/pages/Login/index.tsx b/ui/src/pages/Login/index.tsx
index c540b50a9b..240146b241 100644
--- a/ui/src/pages/Login/index.tsx
+++ b/ui/src/pages/Login/index.tsx
@@ -20,6 +20,7 @@ import {JSEncrypt} from 'jsencrypt';
import {v7 as uuidV7} from 'uuid';
import {clearToken, setToken} from "@/access"
import {history} from "@umijs/max";
+import {SelectLang, useIntl} from "@@/exports";
const USERNAME_PASSWORD = {key: 'username_password', label: '用户名密码登录'};
const MOBILE = {key: 'mobile', label: '手机号登录'};
@@ -34,6 +35,8 @@ const iconStyles: CSSProperties = {
};
export default () => {
+ const intl = useIntl();
+ const t = (id: string, values?: Record) => intl.formatMessage({id}, values);
const items = [
USERNAME_PASSWORD,
MOBILE,
@@ -196,13 +199,16 @@ export default () => {
width: '100vw',
}}
>
+
+
+
}
- title="老寇IoT云平台"
- subTitle="企业级微服务架构云服务多租户IoT平台"
+ title={t('app.title')}
+ subTitle={t('login.subtitle')}
loading={loading}
actions={
{
}}
>
-
- 其他登录方式
-
+
+ {t('login.otherWays')}
+
{
prefix:
,
autoComplete: 'new-password',
}}
- placeholder={'请输入租户编码'}
+ placeholder={t('login.tenantCode.placeholder')}
rules={[
{
required: true,
- message: '请输入租户编码',
+ message: t('login.tenantCode.required'),
},
]}
/>
@@ -315,11 +319,11 @@ export default () => {
prefix: ,
autoComplete: 'new-password',
}}
- placeholder={'请输入用户名'}
+ placeholder={t('login.username.placeholder')}
rules={[
{
required: true,
- message: '请输入用户名',
+ message: t('login.username.required'),
},
]}
/>
@@ -331,11 +335,11 @@ export default () => {
prefix: ,
autoComplete: 'new-password',
}}
- placeholder={'请输入密码'}
+ placeholder={t('login.password.placeholder')}
rules={[
{
required: true,
- message: '请输入密码',
+ message: t('login.password.required'),
},
]}
/>
@@ -350,16 +354,16 @@ export default () => {
autoComplete: 'new-password',
}}
name="captcha"
- placeholder={'请输入验证码'}
+ placeholder={t('login.captcha.placeholder')}
rules={[
{
required: true,
- message: '请输入验证码',
+ message: t('login.captcha.required'),
},
{
pattern: /^[A-Za-z0-9]{4}$/,
- message: '请输入4位验证码(数字和字母)'
- }
+ message: t('login.captcha.invalid'),
+ },
]}
/>
@@ -388,16 +392,16 @@ export default () => {
autoComplete: 'new-password',
}}
name="mobile"
- placeholder={'请输入手机号'}
+ placeholder={t('login.mobile.placeholder')}
rules={[
{
required: true,
- message: '请输入手机号',
+ message: t('login.mobile.required'),
},
{
pattern: /^1\d{10}$/,
- message: '手机号格式错误',
- }
+ message: t('login.mobile.invalid'),
+ },
]}
/>
{
captchaProps={{
size: 'large',
}}
- placeholder={'请输入验证码'}
+ placeholder={t('login.smsCaptcha.placeholder')}
captchaTextRender={(timing, count) => {
if (timing) {
- return `${count} 获取验证码`;
+ return t('login.captcha.countdown', {count});
}
- return '获取验证码';
+ return t('login.captcha.get');
}}
name="mobile_captcha"
rules={[
{
required: true,
- message: '请输入验证码',
+ message: t('login.smsCaptcha.required'),
},
{
pattern: /^\d{6}$/,
- message: '请输入6位数字的验证码'
- }
+ message: t('login.smsCaptcha.invalid'),
+ },
]}
onGetCaptcha={sendMobileCaptcha}
/>
@@ -443,15 +447,15 @@ export default () => {
autoComplete: 'new-password',
}}
name="mail"
- placeholder={'请输入邮箱'}
+ placeholder={t('login.mail.placeholder')}
rules={[
{
required: true,
- message: '请输入邮箱',
+ message: t('login.mail.required'),
},
{
pattern: /^\w+(-+.\w+)*@\w+(-.\w+)*.\w+(-.\w+)*$/,
- message: '邮箱格式错误',
+ message: t('login.mail.invalid'),
},
]}
/>
@@ -466,23 +470,23 @@ export default () => {
captchaProps={{
size: 'large',
}}
- placeholder={'请输入验证码'}
+ placeholder={t('login.mailCaptcha.placeholder')}
captchaTextRender={(timing, count) => {
if (timing) {
- return `${count} 获取验证码`;
+ return t('login.captcha.countdown', {count});
}
- return '获取验证码';
+ return t('login.captcha.get');
}}
name="mail_captcha"
rules={[
{
required: true,
- message: '请输入验证码',
+ message: t('login.mailCaptcha.required'),
},
{
pattern: /^\d{6}$/,
- message: '请输入6位数字的验证码'
- }
+ message: t('login.mailCaptcha.invalid'),
+ },
]}
onGetCaptcha={sendMailCaptcha}
/>
From f237e46bf141cf2de896a8f3049cfe0f7ef904d4 Mon Sep 17 00:00:00 2001
From: koushenhai <2413176044@qq.com>
Date: Thu, 5 Mar 2026 18:49:28 +0800
Subject: [PATCH 2/8] =?UTF-8?q?feat:=20UI=E5=A2=9E=E5=8A=A0=E5=89=8D?=
=?UTF-8?q?=E7=AB=AF=E5=9B=BD=E9=99=85=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/src/locales/en-US.ts | 75 +++++++++++++++++++++++++++++++++++++++++
ui/src/locales/zh-CN.ts | 75 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 150 insertions(+)
create mode 100644 ui/src/locales/en-US.ts
create mode 100644 ui/src/locales/zh-CN.ts
diff --git a/ui/src/locales/en-US.ts b/ui/src/locales/en-US.ts
new file mode 100644
index 0000000000..cb5a9ab8d0
--- /dev/null
+++ b/ui/src/locales/en-US.ts
@@ -0,0 +1,75 @@
+export default {
+ // app
+ 'app.title': 'KCloud IoT Platform',
+
+ // common
+ 'common.ok': 'OK',
+ 'common.cancel': 'Cancel',
+ 'common.search': 'Search',
+ 'common.reset': 'Reset',
+ 'common.add': 'Add',
+ 'common.edit': 'Edit',
+ 'common.delete': 'Delete',
+ 'common.submit': 'Submit',
+ 'common.close': 'Close',
+
+ // auth/user
+ 'user.logout': 'Logout',
+
+ // error
+ 'error.serverInternal': 'Internal server error. Unable to complete the request.',
+ 'error.network': 'Network request error. Please try again later.',
+ 'error.refreshTokenFailed': 'Session refresh failed. Please log in again.',
+ 'error.resourceNotFound': 'Resource not found: {url}',
+ 'error.invalidClient': 'Invalid client. Please check the auth server configuration.',
+
+ // login
+ 'login.subtitle': 'Enterprise-grade multi-tenant IoT platform based on microservice architecture',
+ 'login.otherWays': 'Other sign-in methods',
+ 'login.tenantCode.placeholder': 'Tenant code',
+ 'login.tenantCode.required': 'Please enter tenant code',
+ 'login.username.placeholder': 'Username',
+ 'login.username.required': 'Please enter username',
+ 'login.password.placeholder': 'Password',
+ 'login.password.required': 'Please enter password',
+ 'login.captcha.placeholder': 'Captcha',
+ 'login.captcha.required': 'Please enter captcha',
+ 'login.captcha.invalid': 'Captcha must be 4 characters (letters or numbers)',
+ 'login.mobile.placeholder': 'Mobile number',
+ 'login.mobile.required': 'Please enter mobile number',
+ 'login.mobile.invalid': 'Invalid mobile number',
+ 'login.smsCaptcha.placeholder': 'Verification code',
+ 'login.smsCaptcha.required': 'Please enter verification code',
+ 'login.smsCaptcha.invalid': 'Code must be 6 digits',
+ 'login.mail.placeholder': 'Email',
+ 'login.mail.required': 'Please enter email',
+ 'login.mail.invalid': 'Invalid email address',
+ 'login.mailCaptcha.placeholder': 'Verification code',
+ 'login.mailCaptcha.required': 'Please enter verification code',
+ 'login.mailCaptcha.invalid': 'Code must be 6 digits',
+ 'login.captcha.get': 'Get code',
+ 'login.captcha.countdown': '{count}s',
+
+ // menu (routes)
+ 'menu.home': 'Home',
+ 'menu.login': 'Login',
+ 'menu.sys': 'System',
+ 'menu.sys.permission': 'Permission',
+ 'menu.sys.permission.menu': 'Menus',
+ 'menu.sys.permission.dept': 'Departments',
+ 'menu.sys.permission.role': 'Roles',
+ 'menu.sys.permission.user': 'Users',
+ 'menu.sys.log': 'Logs',
+ 'menu.sys.log.login': 'Login Logs',
+ 'menu.sys.log.notice': 'Notice Logs',
+ 'menu.sys.log.operate': 'Operation Logs',
+ 'menu.sys.oss': 'Object Storage',
+ 'menu.sys.oss.config': 'OSS Config',
+ 'menu.sys.oss.log': 'OSS Logs',
+ 'menu.sys.config': 'System Config',
+ 'menu.sys.config.generator': 'Code Generator',
+ 'menu.iot': 'IoT',
+ 'menu.iot.device': 'Devices',
+ 'menu.iot.device.thingModel': 'Thing Model',
+ 'menu.iot.device.productCategory': 'Product Category',
+};
diff --git a/ui/src/locales/zh-CN.ts b/ui/src/locales/zh-CN.ts
new file mode 100644
index 0000000000..d7b1b20501
--- /dev/null
+++ b/ui/src/locales/zh-CN.ts
@@ -0,0 +1,75 @@
+export default {
+ // app
+ 'app.title': '老寇IoT云平台',
+
+ // common
+ 'common.ok': '确定',
+ 'common.cancel': '取消',
+ 'common.search': '查询',
+ 'common.reset': '重置',
+ 'common.add': '新增',
+ 'common.edit': '编辑',
+ 'common.delete': '删除',
+ 'common.submit': '提交',
+ 'common.close': '关闭',
+
+ // auth/user
+ 'user.logout': '注销',
+
+ // error
+ 'error.serverInternal': '服务器内部错误,无法完成请求',
+ 'error.network': '网络请求错误,请稍后再试',
+ 'error.refreshTokenFailed': '令牌续期失败,请重新登录',
+ 'error.resourceNotFound': '无法找到 {url} 请求的资源',
+ 'error.invalidClient': '无效客户端,请检查认证服务器配置',
+
+ // login
+ 'login.subtitle': '企业级多租户IoT云平台',
+ 'login.otherWays': '其他登录方式',
+ 'login.tenantCode.placeholder': '请输入租户编码',
+ 'login.tenantCode.required': '请输入租户编码',
+ 'login.username.placeholder': '请输入用户名',
+ 'login.username.required': '请输入用户名',
+ 'login.password.placeholder': '请输入密码',
+ 'login.password.required': '请输入密码',
+ 'login.captcha.placeholder': '请输入验证码',
+ 'login.captcha.required': '请输入验证码',
+ 'login.captcha.invalid': '请输入4位验证码(数字和字母)',
+ 'login.mobile.placeholder': '请输入手机号',
+ 'login.mobile.required': '请输入手机号',
+ 'login.mobile.invalid': '手机号格式错误',
+ 'login.smsCaptcha.placeholder': '请输入验证码',
+ 'login.smsCaptcha.required': '请输入验证码',
+ 'login.smsCaptcha.invalid': '请输入6位数字的验证码',
+ 'login.mail.placeholder': '请输入邮箱',
+ 'login.mail.required': '请输入邮箱',
+ 'login.mail.invalid': '邮箱格式错误',
+ 'login.mailCaptcha.placeholder': '请输入验证码',
+ 'login.mailCaptcha.required': '请输入验证码',
+ 'login.mailCaptcha.invalid': '请输入6位数字的验证码',
+ 'login.captcha.get': '获取验证码',
+ 'login.captcha.countdown': '{count} 获取验证码',
+
+ // menu (routes)
+ 'menu.home': '首页',
+ 'menu.login': '登录',
+ 'menu.sys': '系统管理',
+ 'menu.sys.permission': '权限管理',
+ 'menu.sys.permission.menu': '菜单',
+ 'menu.sys.permission.dept': '部门',
+ 'menu.sys.permission.role': '角色',
+ 'menu.sys.permission.user': '用户',
+ 'menu.sys.log': '日志管理',
+ 'menu.sys.log.login': '登录日志',
+ 'menu.sys.log.notice': '通知日志',
+ 'menu.sys.log.operate': '操作日志',
+ 'menu.sys.oss': '对象存储',
+ 'menu.sys.oss.config': '对象存储配置',
+ 'menu.sys.oss.log': '对象存储日志',
+ 'menu.sys.config': '系统配置',
+ 'menu.sys.config.generator': '代码生成器',
+ 'menu.iot': '物联管理',
+ 'menu.iot.device': '设备管理',
+ 'menu.iot.device.thingModel': '物模型',
+ 'menu.iot.device.productCategory': '产品类别',
+};
From cea96c4ecec3ea189cbb692f6a6a7ce01534db8c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?laokou=E3=80=90k=E2=86=91=E3=80=91?= <2413176044@qq.com>
Date: Thu, 5 Mar 2026 22:25:04 +0800
Subject: [PATCH 3/8] =?UTF-8?q?feat:=20ui=E5=9B=BD=E9=99=85=E5=8C=96?=
=?UTF-8?q?=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/config/routes.ts | 45 +++++++++++++++++++---------------
ui/src/app.tsx | 2 +-
ui/src/locales/en-US.ts | 27 +++++++++++---------
ui/src/locales/zh-CN.ts | 3 +++
ui/src/pages/Login/index.tsx | 14 +++++------
ui/src/pages/Sys/Log/login.tsx | 12 +++++----
6 files changed, 58 insertions(+), 45 deletions(-)
diff --git a/ui/config/routes.ts b/ui/config/routes.ts
index d5268a21ef..51c241d36e 100644
--- a/ui/config/routes.ts
+++ b/ui/config/routes.ts
@@ -17,86 +17,91 @@ export default [
redirect: '/home',
},
{
- name: 'menu.home',
+ name: '首页',
path: '/home',
component: './Home',
icon: 'home'
},
{
- name: 'menu.login',
+ name: 'Login',
path: '/login',
component: './Login',
layout: false,
},
{
- name: 'menu.sys',
+ name: '系统管理',
path: '/sys',
icon: 'setting',
routes: [
{
- name: 'menu.sys.permission',
+ name: '权限管理',
path: '/sys/permission',
routes: [
{
- name: 'menu.sys.permission.dept',
+ name: '菜单',
+ path: '/sys/permission/menu',
+ component: './Sys/Permission/menu'
+ },
+ {
+ name: '部门',
path: '/sys/permission/dept',
component: './Sys/Permission/dept'
},
{
- name: 'menu.sys.permission.role',
+ name: '角色',
path: '/sys/permission/role',
component: './Sys/Permission/role'
},
{
- name: 'menu.sys.permission.user',
+ name: '用户',
path: '/sys/permission/user',
component: './Sys/Permission/user'
},
]
},
{
- name: 'menu.sys.log',
+ name: '日志管理',
path: '/sys/log',
routes: [
{
- name: 'menu.sys.log.login',
+ name: '登录日志',
path: '/sys/log/login',
component: './Sys/Log/login'
},
{
- name: 'menu.sys.log.notice',
+ name: '通知日志',
path: '/sys/log/notice',
component: './Sys/Log/notice'
},
{
- name: 'menu.sys.log.operate',
+ name: '操作日志',
path: '/sys/log/operate',
component: './Sys/Log/operate'
}
]
},
{
- name: 'menu.sys.oss',
+ name: '对象存储',
path: '/sys/oss',
routes: [
{
- name: 'menu.sys.oss.config',
+ name: '对象存储配置',
path: '/sys/oss/config',
component: './Sys/Oss/config'
},
{
- name: 'menu.sys.oss.log',
+ name: '对象存储日志',
path: '/sys/oss/log',
component: './Sys/Oss/log'
}
]
},
{
- name: 'menu.sys.config',
+ name: '系统配置',
path: '/sys/config',
routes: [
{
- name: 'menu.sys.config.generator',
+ name: '代码生成器',
path: '/sys/config/generator',
component: './Sys/Config/generator'
}
@@ -105,21 +110,21 @@ export default [
]
},
{
- name: 'menu.iot',
+ name: '物联管理',
path: '/iot',
icon: 'robot',
routes: [
{
- name: 'menu.iot.device',
+ name: '设备管理',
path: '/iot/device',
routes: [
{
- name: 'menu.iot.device.thingModel',
+ name: '物模型',
path: '/iot/device/thingModel',
component: './IoT/Device/thingModel'
},
{
- name: 'menu.iot.device.productCategory',
+ name: '产品类别',
path: '/iot/device/productCategory',
component: './IoT/Device/productCategory'
},
diff --git a/ui/src/app.tsx b/ui/src/app.tsx
index 46723f64ac..bb607c374e 100644
--- a/ui/src/app.tsx
+++ b/ui/src/app.tsx
@@ -28,7 +28,7 @@ const getIcon = (icon: string) => {
const getRouters = (menus: any[]) => {
const routers = [{
- name: 'menu.home',
+ name: '首页',
path: '/home',
icon:
}]
diff --git a/ui/src/locales/en-US.ts b/ui/src/locales/en-US.ts
index cb5a9ab8d0..2a9fe86783 100644
--- a/ui/src/locales/en-US.ts
+++ b/ui/src/locales/en-US.ts
@@ -24,31 +24,34 @@ export default {
'error.invalidClient': 'Invalid client. Please check the auth server configuration.',
// login
- 'login.subtitle': 'Enterprise-grade multi-tenant IoT platform based on microservice architecture',
+ 'login.subtitle': 'Enterprise level multi tenant IoT cloud platform',
+ 'login.usernamePassword': 'User pwd login',
+ 'login.mobile': 'Mobile login',
+ 'login.mail': 'Mail login',
'login.otherWays': 'Other sign-in methods',
- 'login.tenantCode.placeholder': 'Tenant code',
+ 'login.tenantCode.placeholder': 'Please enter tenant code',
'login.tenantCode.required': 'Please enter tenant code',
- 'login.username.placeholder': 'Username',
+ 'login.username.placeholder': 'Please enter username',
'login.username.required': 'Please enter username',
- 'login.password.placeholder': 'Password',
+ 'login.password.placeholder': 'Please enter password',
'login.password.required': 'Please enter password',
- 'login.captcha.placeholder': 'Captcha',
+ 'login.captcha.placeholder': 'Please enter captcha',
'login.captcha.required': 'Please enter captcha',
'login.captcha.invalid': 'Captcha must be 4 characters (letters or numbers)',
- 'login.mobile.placeholder': 'Mobile number',
+ 'login.mobile.placeholder': 'Please enter mobile number',
'login.mobile.required': 'Please enter mobile number',
'login.mobile.invalid': 'Invalid mobile number',
- 'login.smsCaptcha.placeholder': 'Verification code',
+ 'login.smsCaptcha.placeholder': 'Please enter verification code',
'login.smsCaptcha.required': 'Please enter verification code',
- 'login.smsCaptcha.invalid': 'Code must be 6 digits',
- 'login.mail.placeholder': 'Email',
+ 'login.smsCaptcha.invalid': 'Please enter a 6-digit verification code',
+ 'login.mail.placeholder': 'Please enter email',
'login.mail.required': 'Please enter email',
'login.mail.invalid': 'Invalid email address',
- 'login.mailCaptcha.placeholder': 'Verification code',
+ 'login.mailCaptcha.placeholder': 'Please enter verification code',
'login.mailCaptcha.required': 'Please enter verification code',
- 'login.mailCaptcha.invalid': 'Code must be 6 digits',
+ 'login.mailCaptcha.invalid': 'Please enter a 6-digit verification code',
'login.captcha.get': 'Get code',
- 'login.captcha.countdown': '{count}s',
+ 'login.captcha.countdown': '{count} Get code',
// menu (routes)
'menu.home': 'Home',
diff --git a/ui/src/locales/zh-CN.ts b/ui/src/locales/zh-CN.ts
index d7b1b20501..f82e1930b8 100644
--- a/ui/src/locales/zh-CN.ts
+++ b/ui/src/locales/zh-CN.ts
@@ -25,6 +25,9 @@ export default {
// login
'login.subtitle': '企业级多租户IoT云平台',
+ 'login.usernamePassword': '用户名密码登录',
+ 'login.mobile': '手机号登录',
+ 'login.mail': '邮箱登录',
'login.otherWays': '其他登录方式',
'login.tenantCode.placeholder': '请输入租户编码',
'login.tenantCode.required': '请输入租户编码',
diff --git a/ui/src/pages/Login/index.tsx b/ui/src/pages/Login/index.tsx
index 240146b241..e904bffca7 100644
--- a/ui/src/pages/Login/index.tsx
+++ b/ui/src/pages/Login/index.tsx
@@ -22,9 +22,9 @@ import {clearToken, setToken} from "@/access"
import {history} from "@umijs/max";
import {SelectLang, useIntl} from "@@/exports";
-const USERNAME_PASSWORD = {key: 'username_password', label: '用户名密码登录'};
-const MOBILE = {key: 'mobile', label: '手机号登录'};
-const MAIL = {key: 'mail', label: '邮箱登录'};
+const USERNAME_PASSWORD = {key: 'username_password', label: 'login.usernamePassword'};
+const MOBILE = {key: 'mobile', label: 'login.mobile'};
+const MAIL = {key: 'mail', label: 'login.mail'};
type LoginType = 'username_password' | 'mobile' | 'mail';
const iconStyles: CSSProperties = {
@@ -37,10 +37,10 @@ const iconStyles: CSSProperties = {
export default () => {
const intl = useIntl();
const t = (id: string, values?: Record) => intl.formatMessage({id}, values);
- const items = [
- USERNAME_PASSWORD,
- MOBILE,
- MAIL
+const items = [
+ { ...USERNAME_PASSWORD, label: t(USERNAME_PASSWORD.label) },
+ { ...MOBILE, label: t(MOBILE.label) },
+ { ...MAIL, label: t(MAIL.label) },
];
const [loading, setLoading] = useState(false);
const [loginType, setLoginType] = useState('username_password');
diff --git a/ui/src/pages/Sys/Log/login.tsx b/ui/src/pages/Sys/Log/login.tsx
index ca7fe006ea..aae450a8da 100644
--- a/ui/src/pages/Sys/Log/login.tsx
+++ b/ui/src/pages/Sys/Log/login.tsx
@@ -7,7 +7,7 @@ import {trim} from "@/utils/format";
import {ExportToExcel} from "@/utils/export";
import moment from "moment";
import {useRef, useState} from "react";
-import {useAccess} from "@@/exports";
+import {useAccess, useIntl} from "@@/exports";
export default () => {
@@ -25,6 +25,8 @@ export default () => {
};
const access = useAccess();
+ const intl = useIntl();
+ const t = (id: string, values?: Record) => intl.formatMessage({id}, values);
const actionRef = useRef(null);
const [list, setList] = useState([]);
const [param, setParam] = useState({});
@@ -32,8 +34,8 @@ export default () => {
const getLoginType = (type: string) => {
return {
- 'username_password': '用户名密码登录',
- 'mobile': '手机号登录',
+ 'username_password': t('login.usernamePassword'),
+ 'mobile': t('login.mobile'),
'mail': '邮箱登录',
'authorization_code': '授权码登录',
}[type]
@@ -173,8 +175,8 @@ export default () => {
mode: 'single',
options: [
{
- value: "username_password",
- label: "用户名密码登录",
+ value: 'username_password',
+ label: t('login.usernamePassword'),
},
{
value: "mail",
From aede9de550e1dae87b2c071662763121212d7418 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?laokou=E3=80=90k=E2=86=91=E3=80=91?= <2413176044@qq.com>
Date: Thu, 5 Mar 2026 23:10:57 +0800
Subject: [PATCH 4/8] =?UTF-8?q?feat:=20ui=E5=9B=BD=E9=99=85=E5=8C=96?=
=?UTF-8?q?=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/config/config.ts | 1 +
ui/src/app.tsx | 8 ++++----
ui/src/pages/Login/index.tsx | 2 +-
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/ui/config/config.ts b/ui/config/config.ts
index ca07fa740d..33210b2964 100644
--- a/ui/config/config.ts
+++ b/ui/config/config.ts
@@ -40,6 +40,7 @@ export default defineConfig({
// umi/max 内置 i18n
default: 'zh-CN',
antd: true,
+ title: true,
baseNavigator: true,
},
/**
diff --git a/ui/src/app.tsx b/ui/src/app.tsx
index bb607c374e..583fe9dbba 100644
--- a/ui/src/app.tsx
+++ b/ui/src/app.tsx
@@ -26,9 +26,9 @@ const getIcon = (icon: string) => {
}
}
-const getRouters = (menus: any[]) => {
+const getRouters = (menus: any[], homeName: string) => {
const routers = [{
- name: '首页',
+ name: homeName,
path: '/home',
icon:
}]
@@ -123,7 +123,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
params: initialState?.username,
request: async () => {
const result = await listUserTreeMenu({code: 0}).catch(console.log);
- return getRouters(result?.data)
+ return getRouters(result?.data, t('menu.home'))
}
},
layout: 'mix',
@@ -136,7 +136,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
siderMenuType: "sub",
actionsRender: () => {
// Ant Design Pro 风格的语言切换组件(来自 umi plugin-locale)
- return [];
+ return [];
},
avatarProps: {
src: initialState?.avatar,
diff --git a/ui/src/pages/Login/index.tsx b/ui/src/pages/Login/index.tsx
index e904bffca7..5c01918330 100644
--- a/ui/src/pages/Login/index.tsx
+++ b/ui/src/pages/Login/index.tsx
@@ -37,7 +37,7 @@ const iconStyles: CSSProperties = {
export default () => {
const intl = useIntl();
const t = (id: string, values?: Record) => intl.formatMessage({id}, values);
-const items = [
+ const items = [
{ ...USERNAME_PASSWORD, label: t(USERNAME_PASSWORD.label) },
{ ...MOBILE, label: t(MOBILE.label) },
{ ...MAIL, label: t(MAIL.label) },
From 99bc68d38846cc329cb770d0c10ee35642de24b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?laokou=E3=80=90k=E2=86=91=E3=80=91?= <2413176044@qq.com>
Date: Thu, 5 Mar 2026 23:27:02 +0800
Subject: [PATCH 5/8] =?UTF-8?q?feat:=20ui=E5=9B=BD=E9=99=85=E5=8C=96?=
=?UTF-8?q?=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/config/routes.ts | 2 +-
ui/src/app.tsx | 9 ++++++++-
ui/src/locales/en-US.ts | 38 +++++++++-----------------------------
ui/src/locales/zh-CN.ts | 26 +++-----------------------
4 files changed, 21 insertions(+), 54 deletions(-)
diff --git a/ui/config/routes.ts b/ui/config/routes.ts
index 51c241d36e..1427ee5ddc 100644
--- a/ui/config/routes.ts
+++ b/ui/config/routes.ts
@@ -23,7 +23,7 @@ export default [
icon: 'home'
},
{
- name: 'Login',
+ name: '登录',
path: '/login',
component: './Login',
layout: false,
diff --git a/ui/src/app.tsx b/ui/src/app.tsx
index 583fe9dbba..c454cc7673 100644
--- a/ui/src/app.tsx
+++ b/ui/src/app.tsx
@@ -254,9 +254,16 @@ export const request: {
},
},
// 请求拦截
- requestInterceptors: [
+ requestInterceptors: [
async (config: any) => {
const headers = config.headers ? config.headers : [];
+ // 国际化:携带语言到后端(优先使用 umi plugin-locale 的 current locale)
+ const { getLocale } = require('@@/exports');
+ const locale = getLocale?.() || 'zh-CN';
+ if (locale) {
+ // 若后端使用自定义 header,也可以同时带上(按需保留/改名)
+ headers['Language'] = locale;
+ }
const accessToken = getAccessToken()
if (!headers['Skip-Token'] && accessToken) {
headers['Authorization'] = `Bearer ${accessToken}`
diff --git a/ui/src/locales/en-US.ts b/ui/src/locales/en-US.ts
index 2a9fe86783..9c627f7f4f 100644
--- a/ui/src/locales/en-US.ts
+++ b/ui/src/locales/en-US.ts
@@ -37,42 +37,22 @@ export default {
'login.password.required': 'Please enter password',
'login.captcha.placeholder': 'Please enter captcha',
'login.captcha.required': 'Please enter captcha',
- 'login.captcha.invalid': 'Captcha must be 4 characters (letters or numbers)',
+ 'login.captcha.invalid': 'Please enter 4 digits captcha',
'login.mobile.placeholder': 'Please enter mobile number',
'login.mobile.required': 'Please enter mobile number',
'login.mobile.invalid': 'Invalid mobile number',
- 'login.smsCaptcha.placeholder': 'Please enter verification code',
- 'login.smsCaptcha.required': 'Please enter verification code',
- 'login.smsCaptcha.invalid': 'Please enter a 6-digit verification code',
+ 'login.smsCaptcha.placeholder': 'Please enter captcha',
+ 'login.smsCaptcha.required': 'Please enter captcha',
+ 'login.smsCaptcha.invalid': 'Please enter 6 digits captcha',
'login.mail.placeholder': 'Please enter email',
'login.mail.required': 'Please enter email',
'login.mail.invalid': 'Invalid email address',
- 'login.mailCaptcha.placeholder': 'Please enter verification code',
- 'login.mailCaptcha.required': 'Please enter verification code',
- 'login.mailCaptcha.invalid': 'Please enter a 6-digit verification code',
- 'login.captcha.get': 'Get code',
- 'login.captcha.countdown': '{count} Get code',
+ 'login.mailCaptcha.placeholder': 'Please enter captcha',
+ 'login.mailCaptcha.required': 'Please enter captcha',
+ 'login.mailCaptcha.invalid': 'Please enter 6 digits captcha',
+ 'login.captcha.get': 'Get captcha',
+ 'login.captcha.countdown': '{count} Get captcha',
// menu (routes)
'menu.home': 'Home',
- 'menu.login': 'Login',
- 'menu.sys': 'System',
- 'menu.sys.permission': 'Permission',
- 'menu.sys.permission.menu': 'Menus',
- 'menu.sys.permission.dept': 'Departments',
- 'menu.sys.permission.role': 'Roles',
- 'menu.sys.permission.user': 'Users',
- 'menu.sys.log': 'Logs',
- 'menu.sys.log.login': 'Login Logs',
- 'menu.sys.log.notice': 'Notice Logs',
- 'menu.sys.log.operate': 'Operation Logs',
- 'menu.sys.oss': 'Object Storage',
- 'menu.sys.oss.config': 'OSS Config',
- 'menu.sys.oss.log': 'OSS Logs',
- 'menu.sys.config': 'System Config',
- 'menu.sys.config.generator': 'Code Generator',
- 'menu.iot': 'IoT',
- 'menu.iot.device': 'Devices',
- 'menu.iot.device.thingModel': 'Thing Model',
- 'menu.iot.device.productCategory': 'Product Category',
};
diff --git a/ui/src/locales/zh-CN.ts b/ui/src/locales/zh-CN.ts
index f82e1930b8..5d26ba86c9 100644
--- a/ui/src/locales/zh-CN.ts
+++ b/ui/src/locales/zh-CN.ts
@@ -37,42 +37,22 @@ export default {
'login.password.required': '请输入密码',
'login.captcha.placeholder': '请输入验证码',
'login.captcha.required': '请输入验证码',
- 'login.captcha.invalid': '请输入4位验证码(数字和字母)',
+ 'login.captcha.invalid': '请输入4位验证码',
'login.mobile.placeholder': '请输入手机号',
'login.mobile.required': '请输入手机号',
'login.mobile.invalid': '手机号格式错误',
'login.smsCaptcha.placeholder': '请输入验证码',
'login.smsCaptcha.required': '请输入验证码',
- 'login.smsCaptcha.invalid': '请输入6位数字的验证码',
+ 'login.smsCaptcha.invalid': '请输入6位验证码',
'login.mail.placeholder': '请输入邮箱',
'login.mail.required': '请输入邮箱',
'login.mail.invalid': '邮箱格式错误',
'login.mailCaptcha.placeholder': '请输入验证码',
'login.mailCaptcha.required': '请输入验证码',
- 'login.mailCaptcha.invalid': '请输入6位数字的验证码',
+ 'login.mailCaptcha.invalid': '请输入6位验证码',
'login.captcha.get': '获取验证码',
'login.captcha.countdown': '{count} 获取验证码',
// menu (routes)
'menu.home': '首页',
- 'menu.login': '登录',
- 'menu.sys': '系统管理',
- 'menu.sys.permission': '权限管理',
- 'menu.sys.permission.menu': '菜单',
- 'menu.sys.permission.dept': '部门',
- 'menu.sys.permission.role': '角色',
- 'menu.sys.permission.user': '用户',
- 'menu.sys.log': '日志管理',
- 'menu.sys.log.login': '登录日志',
- 'menu.sys.log.notice': '通知日志',
- 'menu.sys.log.operate': '操作日志',
- 'menu.sys.oss': '对象存储',
- 'menu.sys.oss.config': '对象存储配置',
- 'menu.sys.oss.log': '对象存储日志',
- 'menu.sys.config': '系统配置',
- 'menu.sys.config.generator': '代码生成器',
- 'menu.iot': '物联管理',
- 'menu.iot.device': '设备管理',
- 'menu.iot.device.thingModel': '物模型',
- 'menu.iot.device.productCategory': '产品类别',
};
From 986417f5a1a1bea5215d42b2689fd47cd8af1ad9 Mon Sep 17 00:00:00 2001
From: koushenhai <2413176044@qq.com>
Date: Fri, 6 Mar 2026 12:29:39 +0800
Subject: [PATCH 6/8] =?UTF-8?q?feat:=20UI=E5=A2=9E=E5=8A=A0=E5=89=8D?=
=?UTF-8?q?=E7=AB=AF=E5=9B=BD=E9=99=85=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/config/routes.ts | 63 +++++++++++++++++++++++++++--------------
ui/src/app.tsx | 11 ++++---
ui/src/locales/en-US.ts | 20 +++++++++++++
ui/src/locales/zh-CN.ts | 20 +++++++++++++
4 files changed, 87 insertions(+), 27 deletions(-)
diff --git a/ui/config/routes.ts b/ui/config/routes.ts
index 1427ee5ddc..12d5879c6a 100644
--- a/ui/config/routes.ts
+++ b/ui/config/routes.ts
@@ -17,91 +17,108 @@ export default [
redirect: '/home',
},
{
- name: '首页',
+ name: 'menu.home',
+ title: 'menu.home',
path: '/home',
component: './Home',
icon: 'home'
},
{
- name: '登录',
+ name: 'menu.login',
+ title: 'menu.login',
path: '/login',
component: './Login',
layout: false,
},
{
- name: '系统管理',
+ name: 'menu.sys',
+ title: 'menu.sys',
path: '/sys',
icon: 'setting',
routes: [
{
- name: '权限管理',
+ name: 'menu.sys.permission',
+ title: 'menu.sys.permission',
path: '/sys/permission',
routes: [
{
- name: '菜单',
+ name: 'menu.sys.permission.menu',
+ title: 'menu.sys.permission.menu',
path: '/sys/permission/menu',
component: './Sys/Permission/menu'
},
{
- name: '部门',
+ name: 'menu.sys.permission.dept',
+ title: 'menu.sys.permission.dept',
path: '/sys/permission/dept',
component: './Sys/Permission/dept'
},
{
- name: '角色',
+ name: 'menu.sys.permission.role',
+ title: 'menu.sys.permission.role',
path: '/sys/permission/role',
component: './Sys/Permission/role'
},
{
- name: '用户',
+ name: 'menu.sys.permission.user',
+ title: 'menu.sys.permission.user',
path: '/sys/permission/user',
component: './Sys/Permission/user'
},
]
},
{
- name: '日志管理',
+ name: 'menu.sys.log',
+ title: 'menu.sys.log',
path: '/sys/log',
routes: [
{
- name: '登录日志',
+ name: 'menu.sys.log.login',
+ title: 'menu.sys.log.login',
path: '/sys/log/login',
component: './Sys/Log/login'
},
{
- name: '通知日志',
+ name: 'menu.sys.log.notice',
+ title: 'menu.sys.log.notice',
path: '/sys/log/notice',
component: './Sys/Log/notice'
},
{
- name: '操作日志',
+ name: 'menu.sys.log.operate',
+ title: 'menu.sys.log.operate',
path: '/sys/log/operate',
component: './Sys/Log/operate'
}
]
},
{
- name: '对象存储',
+ name: 'menu.sys.oss',
+ title: 'menu.sys.oss',
path: '/sys/oss',
routes: [
{
- name: '对象存储配置',
+ name: 'menu.sys.oss.config',
+ title: 'menu.sys.oss.config',
path: '/sys/oss/config',
component: './Sys/Oss/config'
},
{
- name: '对象存储日志',
+ name: 'menu.sys.oss.log',
+ title: 'menu.sys.oss.log',
path: '/sys/oss/log',
component: './Sys/Oss/log'
}
]
},
{
- name: '系统配置',
+ name: 'menu.sys.config',
+ title: 'menu.sys.config',
path: '/sys/config',
routes: [
{
- name: '代码生成器',
+ name: 'menu.sys.config.generator',
+ title: 'menu.sys.config.generator',
path: '/sys/config/generator',
component: './Sys/Config/generator'
}
@@ -110,21 +127,25 @@ export default [
]
},
{
- name: '物联管理',
+ name: 'menu.iot',
+ title: 'menu.iot',
path: '/iot',
icon: 'robot',
routes: [
{
- name: '设备管理',
+ name: 'menu.iot.device',
+ title: 'menu.iot.device',
path: '/iot/device',
routes: [
{
- name: '物模型',
+ name: 'menu.iot.device.thingModel',
+ title: 'menu.iot.device.thingModel',
path: '/iot/device/thingModel',
component: './IoT/Device/thingModel'
},
{
- name: '产品类别',
+ name: 'menu.iot.device.productCategory',
+ title: 'menu.iot.device.productCategory',
path: '/iot/device/productCategory',
component: './IoT/Device/productCategory'
},
diff --git a/ui/src/app.tsx b/ui/src/app.tsx
index c454cc7673..e3d4659fb8 100644
--- a/ui/src/app.tsx
+++ b/ui/src/app.tsx
@@ -3,7 +3,7 @@
// 全局初始化数据配置,用于 Layout 用户信息和权限初始化
// 更多信息见文档:https://umijs.org/docs/api/runtime-config#getinitialstate
import {Dropdown, message, theme} from "antd";
-import {history, SelectLang, useIntl} from "@@/exports";
+import {history, SelectLang} from "@@/exports";
import {HomeOutlined, LogoutOutlined, RobotOutlined, SettingOutlined} from "@ant-design/icons";
import {ReactElement, ReactNode, ReactPortal} from "react";
import {logout, refresh} from '@/services/auth/auth';
@@ -26,9 +26,10 @@ const getIcon = (icon: string) => {
}
}
-const getRouters = (menus: any[], homeName: string) => {
+const getRouters = (menus: any[]) => {
const routers = [{
- name: homeName,
+ name: 'menu.home',
+ title: 'menu.home',
path: '/home',
icon:
}]
@@ -112,8 +113,6 @@ export async function getInitialState(): Promise<{
export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
// 新写法:使用 intl.formatMessage,替代 formatMessage()
- const intl = useIntl();
- const t = (id: string, values?: Record) => intl.formatMessage({id}, values);
return {
// 面包屑配置
headerContentRender: () => ,
@@ -123,7 +122,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
params: initialState?.username,
request: async () => {
const result = await listUserTreeMenu({code: 0}).catch(console.log);
- return getRouters(result?.data, t('menu.home'))
+ return getRouters(result?.data)
}
},
layout: 'mix',
diff --git a/ui/src/locales/en-US.ts b/ui/src/locales/en-US.ts
index 9c627f7f4f..0ad21447e0 100644
--- a/ui/src/locales/en-US.ts
+++ b/ui/src/locales/en-US.ts
@@ -55,4 +55,24 @@ export default {
// menu (routes)
'menu.home': 'Home',
+ 'menu.login': 'Login',
+ 'menu.sys': 'System',
+ 'menu.sys.permission': 'Permission',
+ 'menu.sys.permission.menu': 'Menus',
+ 'menu.sys.permission.dept': 'Departments',
+ 'menu.sys.permission.role': 'Roles',
+ 'menu.sys.permission.user': 'Users',
+ 'menu.sys.log': 'Logs',
+ 'menu.sys.log.login': 'Login Logs',
+ 'menu.sys.log.notice': 'Notice Logs',
+ 'menu.sys.log.operate': 'Operation Logs',
+ 'menu.sys.oss': 'Object Storage',
+ 'menu.sys.oss.config': 'OSS Config',
+ 'menu.sys.oss.log': 'OSS Logs',
+ 'menu.sys.config': 'System Config',
+ 'menu.sys.config.generator': 'Code Generator',
+ 'menu.iot': 'IoT',
+ 'menu.iot.device': 'Devices',
+ 'menu.iot.device.thingModel': 'Thing Model',
+ 'menu.iot.device.productCategory': 'Product Category',
};
diff --git a/ui/src/locales/zh-CN.ts b/ui/src/locales/zh-CN.ts
index 5d26ba86c9..0b56bd5d39 100644
--- a/ui/src/locales/zh-CN.ts
+++ b/ui/src/locales/zh-CN.ts
@@ -55,4 +55,24 @@ export default {
// menu (routes)
'menu.home': '首页',
+ 'menu.login': '登录',
+ 'menu.sys': '系统管理',
+ 'menu.sys.permission': '权限管理',
+ 'menu.sys.permission.menu': '菜单',
+ 'menu.sys.permission.dept': '部门',
+ 'menu.sys.permission.role': '角色',
+ 'menu.sys.permission.user': '用户',
+ 'menu.sys.log': '日志管理',
+ 'menu.sys.log.login': '登录日志',
+ 'menu.sys.log.notice': '通知日志',
+ 'menu.sys.log.operate': '操作日志',
+ 'menu.sys.oss': '对象存储',
+ 'menu.sys.oss.config': '对象存储配置',
+ 'menu.sys.oss.log': '对象存储日志',
+ 'menu.sys.config': '系统配置',
+ 'menu.sys.config.generator': '代码生成器',
+ 'menu.iot': '物联管理',
+ 'menu.iot.device': '设备管理',
+ 'menu.iot.device.thingModel': '物模型',
+ 'menu.iot.device.productCategory': '产品类别',
};
From 475d1aa7c29551ec6e70403dc7700442e317d92e Mon Sep 17 00:00:00 2001
From: koushenhai <2413176044@qq.com>
Date: Fri, 6 Mar 2026 12:38:24 +0800
Subject: [PATCH 7/8] =?UTF-8?q?feat:=20UI=E5=A2=9E=E5=8A=A0=E5=89=8D?=
=?UTF-8?q?=E7=AB=AF=E5=9B=BD=E9=99=85=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/src/access.ts | 111 ++++++++++++++++++++--------------------
ui/src/app.tsx | 2 +
ui/src/locales/en-US.ts | 2 +-
3 files changed, 59 insertions(+), 56 deletions(-)
diff --git a/ui/src/access.ts b/ui/src/access.ts
index 6982ff4c31..a5fd089d24 100644
--- a/ui/src/access.ts
+++ b/ui/src/access.ts
@@ -2,63 +2,64 @@ export default (initialState: any) => {
// 在这里按照初始化数据定义项目中的权限,统一管理
// 参考文档 https://umijs.org/docs/max/access
const permissions = initialState?.permissions || []
+ const scopes = initialState?.scopes || []
return {
- canMenuGetDetail: permissions?.includes('sys:menu:detail'),
- canMenuModify: permissions?.includes('sys:menu:modify'),
- canMenuRemove: permissions?.includes('sys:menu:remove'),
- canMenuSave: permissions?.includes('sys:menu:save'),
-
- canDeptGetDetail: permissions?.includes('sys:dept:detail'),
- canDeptModify: permissions?.includes('sys:dept:modify'),
- canDeptRemove: permissions?.includes('sys:dept:remove'),
- canDeptSave: permissions?.includes('sys:dept:save'),
-
- canRoleGetDetail: permissions?.includes('sys:role:detail'),
- canRoleModify: permissions?.includes('sys:role:modify'),
- canRoleRemove: permissions?.includes('sys:role:remove'),
- canRoleSave: permissions?.includes('sys:role:save'),
-
- canUserGetDetail: permissions?.includes('sys:user:detail'),
- canUserModify: permissions?.includes('sys:user:modify'),
- canUserRemove: permissions?.includes('sys:user:remove'),
- canUserSave: permissions?.includes('sys:user:save'),
-
- canOssUpload: permissions?.includes('sys:oss:upload'),
- canOssGetDetail: permissions?.includes('sys:oss:detail'),
- canOssModify: permissions?.includes('sys:oss:modify'),
- canOssRemove: permissions?.includes('sys:oss:remove'),
- canOssSave: permissions?.includes('sys:oss:save'),
-
- canOssLogExport: permissions?.includes('sys:oss-log:export'),
-
- canDeviceGetDetail: permissions?.includes('iot:device:detail'),
- canDeviceModify: permissions?.includes('iot:device:modify'),
- canDeviceRemove: permissions?.includes('iot:device:remove'),
- canDeviceSave: permissions?.includes('iot:device:save'),
-
- canProductGetDetail: permissions?.includes('iot:product:detail'),
- canProductModify: permissions?.includes('iot:product:modify'),
- canProductRemove: permissions?.includes('iot:product:remove'),
- canProductSave: permissions?.includes('iot:product:save'),
-
- canThingModelGetDetail: permissions?.includes('iot:thing-model:detail'),
- canThingModelModify: permissions?.includes('iot:thing-model:modify'),
- canThingModelRemove: permissions?.includes('iot:thing-model:remove'),
- canThingModelSave: permissions?.includes('iot:thing-model:save'),
-
- canProductCategoryGetDetail: permissions?.includes('iot:product-category:detail'),
- canProductCategoryModify: permissions?.includes('iot:product-category:modify'),
- canProductCategoryRemove: permissions?.includes('iot:product-category:remove'),
- canProductCategorySave: permissions?.includes('iot:product-category:save'),
-
- canOperateLogGetDetail: permissions?.includes('sys:operate-log:detail'),
- canOperateLogExport: permissions?.includes('sys:operate-log:export'),
-
- canNoticeLogGetDetail: permissions?.includes('sys:notice-log:detail'),
- canNoticeLogExport: permissions?.includes('sys:notice-log:export'),
-
- canLoginLogExport: permissions?.includes('sys:login-log:export'),
+ canMenuGetDetail: permissions?.includes('sys:menu:detail') && scopes?.includes('read'),
+ canMenuModify: permissions?.includes('sys:menu:modify') && scopes?.includes('write'),
+ canMenuRemove: permissions?.includes('sys:menu:remove') && scopes?.includes('write'),
+ canMenuSave: permissions?.includes('sys:menu:save') && scopes?.includes('write'),
+
+ canDeptGetDetail: permissions?.includes('sys:dept:detail') && scopes?.includes('read'),
+ canDeptModify: permissions?.includes('sys:dept:modify') && scopes?.includes('write'),
+ canDeptRemove: permissions?.includes('sys:dept:remove') && scopes?.includes('write'),
+ canDeptSave: permissions?.includes('sys:dept:save') && scopes?.includes('write'),
+
+ canRoleGetDetail: permissions?.includes('sys:role:detail') && scopes?.includes('read'),
+ canRoleModify: permissions?.includes('sys:role:modify') && scopes?.includes('write'),
+ canRoleRemove: permissions?.includes('sys:role:remove') && scopes?.includes('write'),
+ canRoleSave: permissions?.includes('sys:role:save') && scopes?.includes('write'),
+
+ canUserGetDetail: permissions?.includes('sys:user:detail') && scopes?.includes('read'),
+ canUserModify: permissions?.includes('sys:user:modify') && scopes?.includes('write'),
+ canUserRemove: permissions?.includes('sys:user:remove') && scopes?.includes('write'),
+ canUserSave: permissions?.includes('sys:user:save') && scopes?.includes('write'),
+
+ canOssUpload: permissions?.includes('sys:oss:upload') && scopes?.includes('write'),
+ canOssGetDetail: permissions?.includes('sys:oss:detail') && scopes?.includes('read'),
+ canOssModify: permissions?.includes('sys:oss:modify') && scopes?.includes('write'),
+ canOssRemove: permissions?.includes('sys:oss:remove') && scopes?.includes('write'),
+ canOssSave: permissions?.includes('sys:oss:save') && scopes?.includes('write'),
+
+ canOssLogExport: permissions?.includes('sys:oss-log:export') && scopes?.includes('write'),
+
+ canDeviceGetDetail: permissions?.includes('iot:device:detail') && scopes?.includes('read'),
+ canDeviceModify: permissions?.includes('iot:device:modify') && scopes?.includes('write'),
+ canDeviceRemove: permissions?.includes('iot:device:remove') && scopes?.includes('write'),
+ canDeviceSave: permissions?.includes('iot:device:save') && scopes?.includes('write'),
+
+ canProductGetDetail: permissions?.includes('iot:product:detail') && scopes?.includes('read'),
+ canProductModify: permissions?.includes('iot:product:modify') && scopes?.includes('write'),
+ canProductRemove: permissions?.includes('iot:product:remove') && scopes?.includes('write'),
+ canProductSave: permissions?.includes('iot:product:save') && scopes?.includes('write'),
+
+ canThingModelGetDetail: permissions?.includes('iot:thing-model:detail') && scopes?.includes('read'),
+ canThingModelModify: permissions?.includes('iot:thing-model:modify') && scopes?.includes('write'),
+ canThingModelRemove: permissions?.includes('iot:thing-model:remove') && scopes?.includes('write'),
+ canThingModelSave: permissions?.includes('iot:thing-model:save') && scopes?.includes('write'),
+
+ canProductCategoryGetDetail: permissions?.includes('iot:product-category:detail') && scopes?.includes('read'),
+ canProductCategoryModify: permissions?.includes('iot:product-category:modify') && scopes?.includes('write'),
+ canProductCategoryRemove: permissions?.includes('iot:product-category:remove') && scopes?.includes('write'),
+ canProductCategorySave: permissions?.includes('iot:product-category:save') && scopes?.includes('write'),
+
+ canOperateLogGetDetail: permissions?.includes('sys:operate-log:detail') && scopes?.includes('read'),
+ canOperateLogExport: permissions?.includes('sys:operate-log:export') && scopes?.includes('write'),
+
+ canNoticeLogGetDetail: permissions?.includes('sys:notice-log:detail') && scopes?.includes('read'),
+ canNoticeLogExport: permissions?.includes('sys:notice-log:export') && scopes?.includes('write'),
+
+ canLoginLogExport: permissions?.includes('sys:login-log:export') && scopes?.includes('write'),
};
};
diff --git a/ui/src/app.tsx b/ui/src/app.tsx
index e3d4659fb8..7886c6cbf6 100644
--- a/ui/src/app.tsx
+++ b/ui/src/app.tsx
@@ -101,6 +101,7 @@ export async function getInitialState(): Promise<{
username: string;
avatar: string;
permissions: string[]
+ scopes: string[]
}> {
const result = await getUserProfile().catch(console.log);
return {
@@ -108,6 +109,7 @@ export async function getInitialState(): Promise<{
username: result?.data?.username,
avatar: result?.data?.avatar ? result?.data?.avatar : '/1.png',
permissions: result?.data?.permissions,
+ scopes: result?.data?.scopes,
};
}
diff --git a/ui/src/locales/en-US.ts b/ui/src/locales/en-US.ts
index 0ad21447e0..535a230c19 100644
--- a/ui/src/locales/en-US.ts
+++ b/ui/src/locales/en-US.ts
@@ -19,7 +19,7 @@ export default {
// error
'error.serverInternal': 'Internal server error. Unable to complete the request.',
'error.network': 'Network request error. Please try again later.',
- 'error.refreshTokenFailed': 'Session refresh failed. Please log in again.',
+ 'error.refreshTokenFailed': 'Token refresh failed. Please log in again.',
'error.resourceNotFound': 'Resource not found: {url}',
'error.invalidClient': 'Invalid client. Please check the auth server configuration.',
From 82c697b1812458b15a4eae5ab7b177996380c616 Mon Sep 17 00:00:00 2001
From: koushenhai <2413176044@qq.com>
Date: Fri, 6 Mar 2026 13:40:26 +0800
Subject: [PATCH 8/8] =?UTF-8?q?feat:=20UI=E5=A2=9E=E5=8A=A0=E5=89=8D?=
=?UTF-8?q?=E7=AB=AF=E5=9B=BD=E9=99=85=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/config/config.ts | 2 +-
ui/src/app.tsx | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/ui/config/config.ts b/ui/config/config.ts
index 33210b2964..c20bf9a39d 100644
--- a/ui/config/config.ts
+++ b/ui/config/config.ts
@@ -34,7 +34,7 @@ export default defineConfig({
*/
proxy: proxy[REACT_APP_ENV as keyof typeof proxy],
layout: {
- title: '老寇IoT云平台',
+ // title 建议放到 src/app.tsx 的运行时 layout 中用 t('app.title') 动态返回
},
locale: {
// umi/max 内置 i18n
diff --git a/ui/src/app.tsx b/ui/src/app.tsx
index 7886c6cbf6..bf38c68942 100644
--- a/ui/src/app.tsx
+++ b/ui/src/app.tsx
@@ -114,8 +114,10 @@ export async function getInitialState(): Promise<{
}
export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
- // 新写法:使用 intl.formatMessage,替代 formatMessage()
return {
+ // 浏览器 Tab 标题(可国际化)
+ title: t('app.title'),
+
// 面包屑配置
headerContentRender: () => ,
logo: '/logo.png',