2025-12-01 23:03:17 +08:00
|
|
|
import { useState, useEffect } from 'react'
|
2025-11-21 04:32:08 +08:00
|
|
|
import { useNavigate, useLocation } from 'react-router-dom'
|
|
|
|
|
import { Layout as AntLayout, Menu, Drawer, Button } from 'antd'
|
|
|
|
|
import { useMediaQuery } from 'react-responsive'
|
|
|
|
|
import {
|
|
|
|
|
WalletOutlined,
|
|
|
|
|
UserOutlined,
|
|
|
|
|
UnorderedListOutlined,
|
|
|
|
|
BarChartOutlined,
|
2025-12-01 23:03:17 +08:00
|
|
|
MenuOutlined,
|
|
|
|
|
FileTextOutlined,
|
|
|
|
|
LinkOutlined,
|
|
|
|
|
AppstoreOutlined
|
2025-11-21 04:32:08 +08:00
|
|
|
} from '@ant-design/icons'
|
2025-12-01 23:03:17 +08:00
|
|
|
import type { MenuProps } from 'antd'
|
2025-11-21 04:32:08 +08:00
|
|
|
import type { ReactNode } from 'react'
|
|
|
|
|
|
|
|
|
|
const { Header, Content, Sider } = AntLayout
|
|
|
|
|
|
|
|
|
|
interface LayoutProps {
|
|
|
|
|
children: ReactNode
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Layout: React.FC<LayoutProps> = ({ children }) => {
|
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
const location = useLocation()
|
|
|
|
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
|
|
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
|
|
|
|
|
2025-12-01 23:03:17 +08:00
|
|
|
// 获取当前选中的菜单项
|
|
|
|
|
const getSelectedKeys = (): string[] => {
|
|
|
|
|
return [location.pathname]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取当前应该打开的父菜单
|
|
|
|
|
const getInitialOpenKeys = (): string[] => {
|
|
|
|
|
const path = location.pathname
|
|
|
|
|
if (path.startsWith('/templates') || path.startsWith('/copy-trading')) {
|
|
|
|
|
return ['/copy-trading-management']
|
|
|
|
|
}
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const [openKeys, setOpenKeys] = useState<string[]>(getInitialOpenKeys())
|
|
|
|
|
|
|
|
|
|
// 当路径变化时,自动打开对应的父菜单
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const path = location.pathname
|
|
|
|
|
if (path.startsWith('/templates') || path.startsWith('/copy-trading')) {
|
|
|
|
|
setOpenKeys(['/copy-trading-management'])
|
|
|
|
|
}
|
|
|
|
|
}, [location.pathname])
|
|
|
|
|
|
|
|
|
|
const menuItems: MenuProps['items'] = [
|
2025-11-21 04:32:08 +08:00
|
|
|
{
|
|
|
|
|
key: '/accounts',
|
|
|
|
|
icon: <WalletOutlined />,
|
|
|
|
|
label: '账户管理'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: '/leaders',
|
|
|
|
|
icon: <UserOutlined />,
|
|
|
|
|
label: 'Leader 管理'
|
|
|
|
|
},
|
|
|
|
|
{
|
2025-12-01 23:03:17 +08:00
|
|
|
key: '/copy-trading-management',
|
|
|
|
|
icon: <AppstoreOutlined />,
|
|
|
|
|
label: '跟单管理',
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
key: '/templates',
|
|
|
|
|
icon: <FileTextOutlined />,
|
|
|
|
|
label: '跟单模板'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: '/copy-trading',
|
|
|
|
|
icon: <LinkOutlined />,
|
|
|
|
|
label: '跟单配置'
|
|
|
|
|
}
|
|
|
|
|
]
|
2025-11-21 04:32:08 +08:00
|
|
|
},
|
|
|
|
|
{
|
2025-11-27 01:13:52 +08:00
|
|
|
key: '/positions',
|
2025-11-21 04:32:08 +08:00
|
|
|
icon: <UnorderedListOutlined />,
|
2025-11-27 01:13:52 +08:00
|
|
|
label: '仓位管理'
|
2025-11-21 04:32:08 +08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: '/statistics',
|
|
|
|
|
icon: <BarChartOutlined />,
|
|
|
|
|
label: '统计信息'
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
2025-12-01 23:03:17 +08:00
|
|
|
const handleMenuClick = ({ key }: { key: string }) => {
|
|
|
|
|
// 如果是父菜单,不导航
|
|
|
|
|
if (key === '/copy-trading-management') {
|
|
|
|
|
return
|
|
|
|
|
}
|
2025-11-21 04:32:08 +08:00
|
|
|
navigate(key)
|
|
|
|
|
if (isMobile) {
|
|
|
|
|
setMobileMenuOpen(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-01 23:03:17 +08:00
|
|
|
const handleOpenChange = (keys: string[]) => {
|
|
|
|
|
setOpenKeys(keys)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-21 04:32:08 +08:00
|
|
|
if (isMobile) {
|
|
|
|
|
// 移动端布局
|
|
|
|
|
return (
|
|
|
|
|
<AntLayout style={{ minHeight: '100vh' }}>
|
|
|
|
|
<Header style={{
|
|
|
|
|
background: '#001529',
|
|
|
|
|
padding: '0 16px',
|
|
|
|
|
display: 'flex',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
justifyContent: 'space-between'
|
|
|
|
|
}}>
|
|
|
|
|
<div style={{ color: '#fff', fontSize: '18px', fontWeight: 'bold' }}>
|
|
|
|
|
Polymarket 跟单
|
|
|
|
|
</div>
|
|
|
|
|
<Button
|
|
|
|
|
type="text"
|
|
|
|
|
icon={<MenuOutlined />}
|
|
|
|
|
style={{ color: '#fff' }}
|
|
|
|
|
onClick={() => setMobileMenuOpen(true)}
|
|
|
|
|
/>
|
|
|
|
|
</Header>
|
|
|
|
|
<Content style={{
|
|
|
|
|
padding: '12px 8px',
|
|
|
|
|
background: '#f0f2f5',
|
|
|
|
|
minHeight: 'calc(100vh - 64px)'
|
|
|
|
|
}}>
|
|
|
|
|
{children}
|
|
|
|
|
</Content>
|
|
|
|
|
<Drawer
|
|
|
|
|
title="导航菜单"
|
|
|
|
|
placement="left"
|
|
|
|
|
onClose={() => setMobileMenuOpen(false)}
|
|
|
|
|
open={mobileMenuOpen}
|
|
|
|
|
bodyStyle={{ padding: 0 }}
|
|
|
|
|
>
|
|
|
|
|
<Menu
|
|
|
|
|
mode="inline"
|
2025-12-01 23:03:17 +08:00
|
|
|
selectedKeys={getSelectedKeys()}
|
|
|
|
|
openKeys={openKeys}
|
|
|
|
|
onOpenChange={handleOpenChange}
|
2025-11-21 04:32:08 +08:00
|
|
|
items={menuItems}
|
2025-12-01 23:03:17 +08:00
|
|
|
onClick={handleMenuClick}
|
2025-11-21 04:32:08 +08:00
|
|
|
style={{ border: 'none' }}
|
|
|
|
|
/>
|
|
|
|
|
</Drawer>
|
|
|
|
|
</AntLayout>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 桌面端布局
|
|
|
|
|
return (
|
2025-11-27 01:13:52 +08:00
|
|
|
<AntLayout style={{ height: '100vh', overflow: 'hidden' }}>
|
|
|
|
|
<Sider
|
|
|
|
|
width={200}
|
|
|
|
|
style={{
|
|
|
|
|
background: '#001529',
|
|
|
|
|
height: '100vh',
|
|
|
|
|
position: 'fixed',
|
|
|
|
|
left: 0,
|
|
|
|
|
top: 0,
|
|
|
|
|
overflow: 'hidden'
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-11-21 04:32:08 +08:00
|
|
|
<div style={{
|
|
|
|
|
height: '64px',
|
|
|
|
|
display: 'flex',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
color: '#fff',
|
|
|
|
|
fontSize: '18px',
|
2025-11-27 01:13:52 +08:00
|
|
|
fontWeight: 'bold',
|
|
|
|
|
flexShrink: 0
|
2025-11-21 04:32:08 +08:00
|
|
|
}}>
|
|
|
|
|
Polymarket 跟单
|
|
|
|
|
</div>
|
|
|
|
|
<Menu
|
|
|
|
|
mode="inline"
|
2025-12-01 23:03:17 +08:00
|
|
|
selectedKeys={getSelectedKeys()}
|
|
|
|
|
openKeys={openKeys}
|
|
|
|
|
onOpenChange={handleOpenChange}
|
2025-11-21 04:32:08 +08:00
|
|
|
items={menuItems}
|
2025-12-01 23:03:17 +08:00
|
|
|
onClick={handleMenuClick}
|
2025-11-27 01:13:52 +08:00
|
|
|
style={{
|
|
|
|
|
height: 'calc(100vh - 64px)',
|
|
|
|
|
borderRight: 0,
|
|
|
|
|
overflowY: 'auto'
|
|
|
|
|
}}
|
2025-11-21 04:32:08 +08:00
|
|
|
/>
|
|
|
|
|
</Sider>
|
2025-11-27 01:13:52 +08:00
|
|
|
<AntLayout style={{ marginLeft: 200, height: '100vh' }}>
|
|
|
|
|
<Content style={{
|
|
|
|
|
padding: '24px',
|
|
|
|
|
background: '#f0f2f5',
|
|
|
|
|
height: '100vh',
|
|
|
|
|
overflowY: 'auto'
|
|
|
|
|
}}>
|
2025-11-21 04:32:08 +08:00
|
|
|
{children}
|
|
|
|
|
</Content>
|
|
|
|
|
</AntLayout>
|
|
|
|
|
</AntLayout>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default Layout
|
|
|
|
|
|