Files
makemd/dashboard/src/layouts/index.tsx

403 lines
10 KiB
TypeScript
Raw Normal View History

// 全局布局组件 - 包含左侧菜单和头部
import React, { useState, useEffect } from 'react';
import { Layout, Menu, Typography, Avatar, Dropdown, Badge, Space } from 'antd';
import {
DashboardOutlined,
ShoppingOutlined,
FileTextOutlined,
UserOutlined,
TruckOutlined,
AlertOutlined,
AuditOutlined,
DollarOutlined,
BarChartOutlined,
SettingOutlined,
TeamOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
BellOutlined,
DownOutlined,
ShopOutlined,
ScheduleOutlined,
WalletOutlined,
SafetyCertificateOutlined,
GlobalOutlined,
TrophyOutlined,
RobotOutlined,
SwapOutlined,
ThunderboltOutlined,
} from '@ant-design/icons';
import { Link, useLocation, useNavigate, Outlet } from 'react-router-dom';
import type { MenuProps } from 'antd';
const { Header, Sider, Content } = Layout;
const { Title, Text } = Typography;
type MenuItem = MenuProps['items'][number];
// 菜单项配置
const menuItems: MenuItem[] = [
// 核心业务
{
key: 'core',
icon: <ShoppingOutlined />,
label: '核心业务',
children: [
{
key: '/Product',
label: <Link to="/Product"></Link>,
},
{
key: '/Orders',
label: <Link to="/Orders"></Link>,
},
{
key: '/Logistics',
label: <Link to="/Logistics"></Link>,
},
{
key: '/AfterSales',
label: <Link to="/AfterSales"></Link>,
},
],
},
// 商户与贸易
{
key: 'merchant',
icon: <ShopOutlined />,
label: '商户与贸易',
children: [
{
key: '/Merchant',
label: <Link to="/Merchant"></Link>,
},
{
key: '/B2B',
label: <Link to="/B2B">B2B贸易</Link>,
},
],
},
// 营销与增长
{
key: 'marketing',
icon: <BarChartOutlined />,
label: '营销与增长',
children: [
{
key: '/Ad',
label: <Link to="/Ad">广</Link>,
},
{
key: '/DynamicPricing',
label: <Link to="/DynamicPricing"></Link>,
},
{
key: '/StrategyMarketplace',
label: <Link to="/StrategyMarketplace"></Link>,
},
],
},
// 数据分析
{
key: 'analytics',
icon: <GlobalOutlined />,
label: '数据分析',
children: [
{
key: '/Analytics',
label: <Link to="/Analytics"></Link>,
},
{
key: '/Leaderboard',
label: <Link to="/Leaderboard"></Link>,
},
{
key: '/ArbitrageMonitor',
label: <Link to="/ArbitrageMonitor"></Link>,
},
],
},
// 智能运营
{
key: 'ai',
icon: <RobotOutlined />,
label: '智能运营',
children: [
{
key: '/AutoPilot',
label: <Link to="/AutoPilot">AI托管</Link>,
},
{
key: '/TaskCenter',
label: <Link to="/TaskCenter"></Link>,
},
],
},
// 合规与安全
{
key: 'compliance',
icon: <SafetyCertificateOutlined />,
label: '合规与安全',
children: [
{
key: '/Compliance',
label: <Link to="/Compliance"></Link>,
},
{
key: '/Blacklist',
label: <Link to="/Blacklist"></Link>,
},
],
},
// 财务管理
{
key: '/Finance',
icon: <WalletOutlined />,
label: <Link to="/Finance"></Link>,
},
// 系统管理
{
key: 'system',
icon: <SettingOutlined />,
label: '系统管理',
children: [
{
key: '/User',
label: <Link to="/User"></Link>,
},
{
key: '/Role',
label: <Link to="/Role"></Link>,
},
{
key: '/Settings',
label: <Link to="/Settings"></Link>,
},
],
},
];
// 用户菜单项
const userMenuItems: MenuItem[] = [
{
key: 'profile',
label: '个人中心',
},
{
key: 'settings',
label: '账号设置',
},
{
key: 'divider-1',
type: 'divider',
},
{
key: 'logout',
label: '退出登录',
},
];
const MainLayout: React.FC = () => {
const location = useLocation();
const navigate = useNavigate();
const [collapsed, setCollapsed] = useState(false);
const [selectedKeys, setSelectedKeys] = useState<string[]>(['/']);
// 根据当前路径设置选中的菜单项
useEffect(() => {
const pathname = location.pathname;
const findKey = (items: MenuItem[]): string | undefined => {
for (const item of items) {
if (item && 'key' in item && typeof item.key === 'string') {
if (item.key.startsWith('/') && pathname.startsWith(item.key)) {
return item.key;
}
if ('children' in item && Array.isArray(item.children)) {
const found = findKey(item.children as MenuItem[]);
if (found) return found;
}
}
}
return undefined;
};
const matchedKey = findKey(menuItems);
if (matchedKey) {
setSelectedKeys([matchedKey]);
}
}, [location.pathname]);
// 从 localStorage 读取菜单折叠状态
useEffect(() => {
const savedCollapsed = localStorage.getItem('sidebar_collapsed');
if (savedCollapsed !== null) {
setCollapsed(savedCollapsed === 'true');
}
}, []);
// 保存菜单折叠状态到 localStorage
const handleCollapse = (value: boolean) => {
setCollapsed(value);
localStorage.setItem('sidebar_collapsed', String(value));
};
const handleUserMenuClick = ({ key }: { key: string }) => {
if (key === 'logout') {
// 处理登出逻辑
localStorage.removeItem('token');
navigate('/Auth/LoginPage');
} else if (key === 'profile') {
navigate('/Settings/ProfileSettings');
} else if (key === 'settings') {
navigate('/Settings');
}
};
return (
<Layout style={{ minHeight: '100vh' }}>
{/* 左侧菜单栏 */}
<Sider
trigger={null}
collapsible
collapsed={collapsed}
collapsedWidth={80}
width={220}
style={{
background: '#001529',
position: 'fixed',
left: 0,
top: 0,
bottom: 0,
zIndex: 100,
boxShadow: '2px 0 8px rgba(0, 0, 0, 0.15)',
}}
>
{/* Logo 区域 */}
<div
style={{
height: '64px',
display: 'flex',
alignItems: 'center',
justifyContent: collapsed ? 'center' : 'flex-start',
padding: collapsed ? '0' : '0 24px',
background: '#002140',
borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
}}
>
{collapsed ? (
<div style={{ color: '#fff', fontSize: '20px', fontWeight: 'bold' }}>C</div>
) : (
<Title level={4} style={{ color: '#fff', margin: 0, whiteSpace: 'nowrap' }}>
Crawlful Hub
</Title>
)}
</div>
{/* 菜单 */}
<Menu
theme="dark"
mode="inline"
selectedKeys={selectedKeys}
items={menuItems}
style={{
borderRight: 0,
paddingTop: '8px',
}}
/>
</Sider>
{/* 右侧内容区域 */}
<Layout
style={{
marginLeft: collapsed ? 80 : 220,
transition: 'all 0.2s',
}}
>
{/* 顶部Header */}
<Header
style={{
background: '#fff',
padding: '0 24px',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
boxShadow: '0 1px 4px rgba(0, 21, 41, 0.08)',
position: 'sticky',
top: 0,
zIndex: 99,
}}
>
{/* 左侧:折叠按钮 */}
<div
style={{
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
padding: '8px',
borderRadius: '4px',
transition: 'background 0.3s',
}}
onClick={() => handleCollapse(!collapsed)}
onMouseEnter={(e) => {
e.currentTarget.style.background = '#f0f0f0';
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'transparent';
}}
>
{collapsed ? <MenuUnfoldOutlined style={{ fontSize: '18px' }} /> : <MenuFoldOutlined style={{ fontSize: '18px' }} />}
</div>
{/* 右侧:通知和用户 */}
<Space size={24}>
{/* 通知图标 */}
<Badge count={5} size="small">
<BellOutlined style={{ fontSize: '18px', cursor: 'pointer', color: '#666' }} />
</Badge>
{/* 用户下拉菜单 */}
<Dropdown
menu={{ items: userMenuItems, onClick: handleUserMenuClick }}
placement="bottomRight"
>
<Space style={{ cursor: 'pointer' }}>
<Avatar style={{ backgroundColor: '#1890ff' }} icon={<UserOutlined />} />
{!collapsed && (
<>
<Text strong></Text>
<DownOutlined style={{ fontSize: '12px', color: '#999' }} />
</>
)}
</Space>
</Dropdown>
</Space>
</Header>
{/* 主内容区域 */}
<Content
style={{
margin: '24px',
padding: '24px',
background: '#f0f2f5',
minHeight: 280,
}}
>
<div
style={{
background: '#fff',
padding: '24px',
borderRadius: '8px',
minHeight: 'calc(100vh - 184px)',
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.06)',
}}
>
<Outlet />
</div>
</Content>
</Layout>
</Layout>
);
};
export default MainLayout;