feat: 完成前端多语言支持和菜单优化
- 缩短菜单标题,解决显示不全问题 - 添加跟随系统语言选项,作为默认选项 - 移除语言切换时的页面刷新,实现无刷新切换 - 完成主要页面的多语言替换: * ConfigPage - 全局配置页面 * ResetPassword - 重置密码页面 * LeaderList - Leader 列表页面 * UserList - 用户列表页面 * Statistics - 统计信息页面 * OrderList - 订单列表页面 * TemplateList - 模板列表页面 * CopyTradingList - 跟单配置列表页面 - 添加简体中文、繁体中文和英文的完整翻译键 - 优化语言设置页面,支持跟随系统语言
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Select, Space } from 'antd'
|
||||
import { GlobalOutlined } from '@ant-design/icons'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
|
||||
const LanguageSwitcher: React.FC = () => {
|
||||
const { i18n } = useTranslation()
|
||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||
const [currentLang, setCurrentLang] = useState<string>(i18n.language || 'en')
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentLang(i18n.language || 'en')
|
||||
}, [i18n.language])
|
||||
|
||||
const languages = [
|
||||
{ value: 'zh-CN', label: '简体中文' },
|
||||
{ value: 'zh-TW', label: '繁體中文' },
|
||||
{ value: 'en', label: 'English' }
|
||||
]
|
||||
|
||||
const handleChange = async (value: string) => {
|
||||
setCurrentLang(value)
|
||||
await i18n.changeLanguage(value)
|
||||
// 保存到 localStorage
|
||||
localStorage.setItem('i18nextLng', value)
|
||||
// 刷新页面以应用 Ant Design 的 locale 和所有翻译
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
return (
|
||||
<Space>
|
||||
<GlobalOutlined style={{ color: '#fff', fontSize: isMobile ? '14px' : '16px' }} />
|
||||
<Select
|
||||
value={currentLang}
|
||||
onChange={handleChange}
|
||||
options={languages}
|
||||
style={{
|
||||
width: isMobile ? 100 : 120,
|
||||
color: '#fff'
|
||||
}}
|
||||
dropdownStyle={{
|
||||
minWidth: 120
|
||||
}}
|
||||
bordered={false}
|
||||
size={isMobile ? 'small' : 'middle'}
|
||||
/>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
|
||||
export default LanguageSwitcher
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
import { Layout as AntLayout, Menu, Drawer, Button, Modal } from 'antd'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import {
|
||||
WalletOutlined,
|
||||
@@ -15,7 +16,9 @@ import {
|
||||
LogoutOutlined,
|
||||
SettingOutlined,
|
||||
GithubOutlined,
|
||||
TwitterOutlined
|
||||
TwitterOutlined,
|
||||
GlobalOutlined,
|
||||
CheckCircleOutlined
|
||||
} from '@ant-design/icons'
|
||||
import type { MenuProps } from 'antd'
|
||||
import type { ReactNode } from 'react'
|
||||
@@ -29,6 +32,7 @@ interface LayoutProps {
|
||||
}
|
||||
|
||||
const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||
@@ -46,6 +50,9 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
if (path.startsWith('/leaders') || path.startsWith('/templates') || path.startsWith('/copy-trading')) {
|
||||
keys.push('/copy-trading-management')
|
||||
}
|
||||
if (path.startsWith('/system-settings')) {
|
||||
keys.push('/system-settings')
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
@@ -58,6 +65,9 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
if (path.startsWith('/leaders') || path.startsWith('/templates') || path.startsWith('/copy-trading')) {
|
||||
keys.push('/copy-trading-management')
|
||||
}
|
||||
if (path.startsWith('/system-settings')) {
|
||||
keys.push('/system-settings')
|
||||
}
|
||||
setOpenKeys(keys)
|
||||
}, [location.pathname])
|
||||
|
||||
@@ -65,54 +75,71 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
{
|
||||
key: '/accounts',
|
||||
icon: <WalletOutlined />,
|
||||
label: '账户管理'
|
||||
label: t('menu.accounts')
|
||||
},
|
||||
{
|
||||
key: '/copy-trading-management',
|
||||
icon: <AppstoreOutlined />,
|
||||
label: '跟单交易',
|
||||
label: t('menu.copyTrading'),
|
||||
children: [
|
||||
{
|
||||
key: '/leaders',
|
||||
icon: <UserOutlined />,
|
||||
label: 'Leader 管理'
|
||||
label: t('menu.leaders')
|
||||
},
|
||||
{
|
||||
key: '/templates',
|
||||
icon: <FileTextOutlined />,
|
||||
label: '跟单模板'
|
||||
label: t('menu.templates')
|
||||
},
|
||||
{
|
||||
key: '/copy-trading',
|
||||
icon: <LinkOutlined />,
|
||||
label: '跟单配置'
|
||||
label: t('menu.copyTradingConfig')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: '/positions',
|
||||
icon: <UnorderedListOutlined />,
|
||||
label: '仓位管理'
|
||||
label: t('menu.positions')
|
||||
},
|
||||
{
|
||||
key: '/statistics',
|
||||
icon: <BarChartOutlined />,
|
||||
label: '统计信息'
|
||||
label: t('menu.statistics')
|
||||
},
|
||||
{
|
||||
key: '/users',
|
||||
icon: <TeamOutlined />,
|
||||
label: '用户管理'
|
||||
label: t('menu.users')
|
||||
},
|
||||
{
|
||||
key: '/system-settings',
|
||||
icon: <SettingOutlined />,
|
||||
label: '系统管理'
|
||||
label: t('menu.systemSettings'),
|
||||
children: [
|
||||
{
|
||||
key: '/system-settings/language',
|
||||
icon: <GlobalOutlined />,
|
||||
label: t('menu.language')
|
||||
},
|
||||
{
|
||||
key: '/system-settings/api-health',
|
||||
icon: <CheckCircleOutlined />,
|
||||
label: t('menu.apiHealth')
|
||||
},
|
||||
{
|
||||
key: '/system-settings/proxy',
|
||||
icon: <LinkOutlined />,
|
||||
label: t('menu.proxy')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'logout',
|
||||
icon: <LogoutOutlined />,
|
||||
label: '退出登录'
|
||||
label: t('menu.logout')
|
||||
}
|
||||
]
|
||||
|
||||
@@ -125,10 +152,10 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
|
||||
const handleLogoutConfirm = () => {
|
||||
Modal.confirm({
|
||||
title: '确认退出',
|
||||
content: '确定要退出登录吗?',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
title: t('menu.logoutConfirm'),
|
||||
content: t('menu.logoutConfirmDesc'),
|
||||
okText: t('common.confirm'),
|
||||
cancelText: t('common.cancel'),
|
||||
onOk: () => {
|
||||
handleLogout()
|
||||
if (isMobile) {
|
||||
@@ -140,7 +167,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
|
||||
const handleMenuClick = ({ key }: { key: string }) => {
|
||||
// 如果是父菜单,不导航
|
||||
if (key === '/copy-trading-management') {
|
||||
if (key === '/copy-trading-management' || key === '/system-settings') {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -207,7 +234,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
{children}
|
||||
</Content>
|
||||
<Drawer
|
||||
title="导航菜单"
|
||||
title={t('menu.navigation')}
|
||||
placement="left"
|
||||
onClose={() => setMobileMenuOpen(false)}
|
||||
open={mobileMenuOpen}
|
||||
@@ -253,7 +280,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<span>PolyHermes</span>
|
||||
<div style={{ display: 'flex', gap: '12px' }}>
|
||||
<div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>
|
||||
<a
|
||||
href="https://github.com/WrBug/PolyHermes"
|
||||
target="_blank"
|
||||
|
||||
Reference in New Issue
Block a user