| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- import { useState, useEffect } from 'react'
- import { Outlet, useNavigate, useLocation } from 'react-router-dom'
- import { Layout as AntLayout, Menu, Button, Drawer, Popconfirm } from 'antd'
- import {
- DashboardOutlined,
- UserOutlined,
- AppstoreOutlined,
- FileTextOutlined,
- ExportOutlined,
- ImportOutlined,
- MenuFoldOutlined,
- MenuUnfoldOutlined,
- TeamOutlined,
- LogoutOutlined,
- MenuOutlined
- } from '@ant-design/icons'
- import { useAuth } from '../contexts/AuthContext'
- const { Header, Sider, Content } = AntLayout
- const MOBILE_BREAKPOINT = 768
- const menuItems = [
- {
- key: '/dashboard',
- icon: <DashboardOutlined />,
- label: '仪表盘'
- },
- {
- key: '/persons',
- icon: <UserOutlined />,
- label: '人员管理'
- },
- {
- key: '/items',
- icon: <AppstoreOutlined />,
- label: '物品管理'
- },
- {
- key: '/work-records',
- icon: <FileTextOutlined />,
- label: '工作记录'
- },
- {
- key: '/export',
- icon: <ExportOutlined />,
- label: '导出报表'
- },
- {
- key: '/import',
- icon: <ImportOutlined />,
- label: '导入数据'
- },
- {
- key: '/admins',
- icon: <TeamOutlined />,
- label: '管理员管理'
- }
- ]
- function Layout() {
- const [collapsed, setCollapsed] = useState(false)
- const [isMobile, setIsMobile] = useState(window.innerWidth < MOBILE_BREAKPOINT)
- const [drawerVisible, setDrawerVisible] = useState(false)
- const navigate = useNavigate()
- const location = useLocation()
- const { logout, admin } = useAuth()
- // Mobile detection with resize listener
- useEffect(() => {
- const handleResize = () => {
- const mobile = window.innerWidth < MOBILE_BREAKPOINT
- setIsMobile(mobile)
- if (!mobile) {
- setDrawerVisible(false)
- }
- }
- window.addEventListener('resize', handleResize)
- return () => window.removeEventListener('resize', handleResize)
- }, [])
- const handleMenuClick = ({ key }) => {
- navigate(key)
- if (isMobile) {
- setDrawerVisible(false)
- }
- }
- const handleLogout = () => {
- logout()
- navigate('/login')
- }
- const toggleDrawer = () => {
- setDrawerVisible(!drawerVisible)
- }
- const menuContent = (
- <Menu
- theme="dark"
- mode="inline"
- selectedKeys={[location.pathname]}
- items={menuItems}
- onClick={handleMenuClick}
- />
- )
- const logoContent = (
- <div style={{
- height: 32,
- margin: 16,
- background: 'rgba(255, 255, 255, 0.2)',
- borderRadius: 6,
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- color: '#fff',
- fontWeight: 'bold',
- fontSize: collapsed && !isMobile ? 14 : 16
- }}>
- {collapsed && !isMobile ? '统计' : '工作统计系统'}
- </div>
- )
- return (
- <AntLayout style={{ minHeight: '100vh' }}>
- {/* Mobile Drawer */}
- {isMobile ? (
- <Drawer
- placement="left"
- onClose={() => setDrawerVisible(false)}
- open={drawerVisible}
- width={250}
- bodyStyle={{ padding: 0, background: '#001529' }}
- headerStyle={{ display: 'none' }}
- >
- {logoContent}
- {menuContent}
- </Drawer>
- ) : (
- <Sider
- trigger={null}
- collapsible
- collapsed={collapsed}
- style={{
- overflow: 'auto',
- height: '100vh',
- position: 'fixed',
- left: 0,
- top: 0,
- bottom: 0
- }}
- >
- {logoContent}
- {menuContent}
- </Sider>
- )}
- <AntLayout style={{ marginLeft: isMobile ? 0 : (collapsed ? 80 : 200), transition: 'margin-left 0.2s' }}>
- <Header style={{
- padding: '0 16px',
- background: '#fff',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'space-between',
- position: 'sticky',
- top: 0,
- zIndex: 1
- }}>
- {isMobile ? (
- <Button
- type="text"
- icon={<MenuOutlined />}
- onClick={toggleDrawer}
- style={{ fontSize: 18, minWidth: 44, minHeight: 44 }}
- className="mobile-menu-btn"
- />
- ) : (
- collapsed ? (
- <MenuUnfoldOutlined
- style={{ fontSize: 18, cursor: 'pointer' }}
- onClick={() => setCollapsed(false)}
- />
- ) : (
- <MenuFoldOutlined
- style={{ fontSize: 18, cursor: 'pointer' }}
- onClick={() => setCollapsed(true)}
- />
- )
- )}
- <div style={{ display: 'flex', alignItems: 'center', gap: isMobile ? 8 : 16 }}>
- {admin && !isMobile && <span style={{ color: '#666' }}>欢迎, {admin.username}</span>}
- <Popconfirm
- title="确认登出"
- description="确定要退出登录吗?"
- onConfirm={handleLogout}
- okText="确定"
- cancelText="取消"
- >
- <Button
- type="text"
- icon={<LogoutOutlined />}
- style={{ minWidth: 44, minHeight: 44 }}
- className="mobile-touch-target"
- >
- {!isMobile && '登出'}
- </Button>
- </Popconfirm>
- </div>
- </Header>
- <Content style={{
- margin: isMobile ? 12 : 24,
- padding: isMobile ? 12 : 24,
- background: '#fff',
- borderRadius: 8,
- minHeight: 280
- }}>
- <Outlet />
- </Content>
- </AntLayout>
- </AntLayout>
- )
- }
- export default Layout
|