Files
Web_BLV_OA_Exam_Prod/src/layouts/AdminLayout.tsx

161 lines
4.3 KiB
TypeScript
Raw Normal View History

import { useState } from 'react';
import { Layout, Menu, Button, Avatar, Dropdown, message } from 'antd';
import { useNavigate, useLocation } from 'react-router-dom';
import {
DashboardOutlined,
QuestionCircleOutlined,
BarChartOutlined,
UserOutlined,
LogoutOutlined,
DatabaseOutlined,
SafetyOutlined,
BookOutlined,
CalendarOutlined,
TeamOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined
} from '@ant-design/icons';
import { useAdmin } from '../contexts';
import { Logo } from '../components/common/Logo';
const { Header, Sider, Content, Footer } = Layout;
const AdminLayout = ({ children }: { children: React.ReactNode }) => {
const navigate = useNavigate();
const location = useLocation();
const { admin, clearAdmin } = useAdmin();
const [collapsed, setCollapsed] = useState(false);
const menuItems = [
{
key: '/admin/dashboard',
icon: <DashboardOutlined />,
label: '仪表盘',
},
{
key: '/admin/questions',
icon: <QuestionCircleOutlined />,
label: '题库管理',
},
{
key: '/admin/categories',
icon: <SafetyOutlined />,
label: '题目类别',
},
{
key: '/admin/subjects',
icon: <BookOutlined />,
label: '考试科目',
},
{
key: '/admin/tasks',
icon: <CalendarOutlined />,
label: '考试任务',
},
{
key: '/admin/users',
icon: <TeamOutlined />,
label: '用户管理',
},
{
key: '/admin/statistics',
icon: <BarChartOutlined />,
label: '数据统计',
},
{
key: '/admin/backup',
icon: <DatabaseOutlined />,
label: '数据备份',
},
];
const handleMenuClick = ({ key }: { key: string }) => {
navigate(key);
};
const handleLogout = () => {
clearAdmin();
message.success('退出登录成功');
navigate('/admin/login');
};
const userMenuItems = [
{
key: 'logout',
icon: <LogoutOutlined />,
label: '退出登录',
onClick: handleLogout,
},
];
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider
trigger={null}
collapsible
collapsed={collapsed}
theme="light"
className="shadow-md z-10"
width={240}
>
<div className="h-16 flex items-center justify-center border-b border-gray-100">
{collapsed ? (
<span className="text-xl font-bold text-mars-500">OA</span>
) : (
<Logo variant="primary" />
)}
</div>
<Menu
mode="inline"
selectedKeys={[location.pathname]}
items={menuItems}
onClick={handleMenuClick}
style={{ borderRight: 0 }}
className="py-4"
/>
</Sider>
<Layout className="bg-gray-50/50">
<Header className="bg-white shadow-sm flex justify-between items-center px-6 h-16 sticky top-0 z-10">
<Button
type="text"
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
onClick={() => setCollapsed(!collapsed)}
className="text-lg w-10 h-10 flex items-center justify-center"
/>
<div className="flex items-center gap-4">
<span className="text-gray-600 hidden sm:block">
{admin?.username}
</span>
<Dropdown menu={{ items: userMenuItems }} placement="bottomRight" arrow>
<Avatar
icon={<UserOutlined />}
className="cursor-pointer bg-mars-100 text-mars-600 hover:bg-mars-200 transition-colors"
/>
</Dropdown>
</div>
</Header>
<Content className="m-6 flex flex-col">
<div className="flex-1 bg-white rounded-xl shadow-sm p-6 min-h-[calc(100vh-160px)]">
{children}
</div>
</Content>
<Footer className="bg-transparent text-center py-6 px-8 text-gray-400 text-sm flex flex-col md:flex-row justify-between items-center">
<div>
&copy; {new Date().getFullYear()} Boonlive OA System. All Rights Reserved.
</div>
<div className="mt-4 md:mt-0">
<Logo variant="secondary" />
</div>
</Footer>
</Layout>
</Layout>
);
};
export default AdminLayout;