Files
Web_BLV_OA_Exam_Prod/src/layouts/AdminLayout.tsx

161 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;