190 lines
5.6 KiB
TypeScript
190 lines
5.6 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
|
|
import {
|
|
Layout,
|
|
Menu,
|
|
Avatar,
|
|
Dropdown,
|
|
theme,
|
|
Button,
|
|
Space,
|
|
} from 'antd';
|
|
import {
|
|
DashboardOutlined,
|
|
AppstoreOutlined,
|
|
InboxOutlined,
|
|
MobileOutlined,
|
|
UserOutlined,
|
|
TeamOutlined,
|
|
LogoutOutlined,
|
|
MenuFoldOutlined,
|
|
MenuUnfoldOutlined,
|
|
} from '@ant-design/icons';
|
|
import { useAuthStore } from '../../store/useAuthStore';
|
|
|
|
const { Header, Sider, Content } = Layout;
|
|
|
|
const menuItems = [
|
|
{
|
|
key: '/dashboard',
|
|
icon: <DashboardOutlined />,
|
|
label: '仪表盘',
|
|
},
|
|
{
|
|
key: '/device-types',
|
|
icon: <AppstoreOutlined />,
|
|
label: '设备类型',
|
|
},
|
|
{
|
|
key: '/batches',
|
|
icon: <InboxOutlined />,
|
|
label: '批次管理',
|
|
},
|
|
{
|
|
key: '/devices',
|
|
icon: <MobileOutlined />,
|
|
label: '设备列表',
|
|
},
|
|
{
|
|
key: '/users',
|
|
icon: <UserOutlined />,
|
|
label: '用户管理',
|
|
},
|
|
{
|
|
key: '/admins',
|
|
icon: <TeamOutlined />,
|
|
label: '管理员',
|
|
},
|
|
];
|
|
|
|
const MainLayout: React.FC = () => {
|
|
const [collapsed, setCollapsed] = useState(false);
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const { adminInfo, logout } = useAuthStore();
|
|
const { token: themeToken } = theme.useToken();
|
|
|
|
const handleMenuClick = ({ key }: { key: string }) => {
|
|
navigate(key);
|
|
};
|
|
|
|
const handleLogout = () => {
|
|
logout();
|
|
navigate('/login');
|
|
};
|
|
|
|
const userMenuItems = [
|
|
{
|
|
key: 'profile',
|
|
icon: <UserOutlined />,
|
|
label: '个人信息',
|
|
},
|
|
{
|
|
type: 'divider' as const,
|
|
},
|
|
{
|
|
key: 'logout',
|
|
icon: <LogoutOutlined />,
|
|
label: '退出登录',
|
|
danger: true,
|
|
onClick: handleLogout,
|
|
},
|
|
];
|
|
|
|
const getRoleLabel = (role?: string) => {
|
|
const roleMap: Record<string, string> = {
|
|
super_admin: '超级管理员',
|
|
admin: '管理员',
|
|
operator: '操作员',
|
|
};
|
|
return roleMap[role || ''] || role;
|
|
};
|
|
|
|
return (
|
|
<Layout style={{ minHeight: '100vh' }}>
|
|
<Sider
|
|
trigger={null}
|
|
collapsible
|
|
collapsed={collapsed}
|
|
theme="dark"
|
|
style={{
|
|
overflow: 'auto',
|
|
height: '100vh',
|
|
position: 'fixed',
|
|
left: 0,
|
|
top: 0,
|
|
bottom: 0,
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
height: 64,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
color: '#fff',
|
|
fontSize: collapsed ? 16 : 20,
|
|
fontWeight: 'bold',
|
|
borderBottom: '1px solid rgba(255,255,255,0.1)',
|
|
}}
|
|
>
|
|
{collapsed ? 'RTC' : 'RTC 管理后台'}
|
|
</div>
|
|
<Menu
|
|
theme="dark"
|
|
mode="inline"
|
|
selectedKeys={[location.pathname]}
|
|
items={menuItems}
|
|
onClick={handleMenuClick}
|
|
/>
|
|
</Sider>
|
|
<Layout style={{ marginLeft: collapsed ? 80 : 200, transition: 'margin-left 0.2s' }}>
|
|
<Header
|
|
style={{
|
|
padding: '0 24px',
|
|
background: themeToken.colorBgContainer,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
boxShadow: '0 1px 4px rgba(0,0,0,0.08)',
|
|
position: 'sticky',
|
|
top: 0,
|
|
zIndex: 10,
|
|
}}
|
|
>
|
|
<Button
|
|
type="text"
|
|
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
|
onClick={() => setCollapsed(!collapsed)}
|
|
style={{ fontSize: 16 }}
|
|
/>
|
|
<Space>
|
|
<Dropdown menu={{ items: userMenuItems }} placement="bottomRight">
|
|
<Space style={{ cursor: 'pointer' }}>
|
|
<Avatar icon={<UserOutlined />} style={{ backgroundColor: themeToken.colorPrimary }} />
|
|
<span>{adminInfo?.name || adminInfo?.username}</span>
|
|
<span style={{ color: themeToken.colorTextSecondary, fontSize: 12 }}>
|
|
({getRoleLabel(adminInfo?.role)})
|
|
</span>
|
|
</Space>
|
|
</Dropdown>
|
|
</Space>
|
|
</Header>
|
|
<Content
|
|
style={{
|
|
margin: 24,
|
|
padding: 24,
|
|
background: themeToken.colorBgContainer,
|
|
borderRadius: themeToken.borderRadiusLG,
|
|
minHeight: 280,
|
|
}}
|
|
>
|
|
<Outlet />
|
|
</Content>
|
|
</Layout>
|
|
</Layout>
|
|
);
|
|
};
|
|
|
|
export default MainLayout;
|