/** * 侧边栏导航组件 * 分组式菜单结构,支持折叠/展开,移动端抽屉模式 */ import { Link, useLocation } from 'react-router-dom' import { useState } from 'react' import clsx from 'clsx' import { LayoutDashboard, Users, CalendarCheck, UserX, Calculator, Shield, FileSearch, FileText, Stethoscope, HeartPulse, Award, Bot, BookMarked, Bell, ScrollText, Settings, ChevronDown, ChevronRight, Building2, } from 'lucide-react' import Logo from '../ui/Logo' interface NavItem { path: string label: string icon: typeof LayoutDashboard } interface NavGroup { title: string items: NavItem[] } const navGroups: NavGroup[] = [ { title: '工作台', items: [ { path: '/', label: '总览', icon: LayoutDashboard }, ], }, { title: '员工管理', items: [ { path: '/roster', label: '花名册', icon: Users }, { path: '/attendance', label: '考勤确认', icon: CalendarCheck }, { path: '/termination', label: '解聘补偿', icon: UserX }, ], }, { title: '薪税社保', items: [ { path: '/money', label: '薪税管理', icon: Calculator }, { path: '/social', label: '社保公积金', icon: Shield }, ], }, { title: '合规风控', items: [ { path: '/evidence', label: '证据链', icon: FileSearch }, { path: '/policies', label: '规章制度', icon: FileText }, { path: '/tools/health-check', label: '用工体检', icon: Stethoscope }, { path: '/tools/medical-period', label: '医疗期计算', icon: HeartPulse }, { path: '/tools/annual-value', label: '年度价值', icon: Award }, ], }, { title: 'AI 辅助', items: [ { path: '/ai-assistant', label: 'AI 顾问', icon: Bot }, { path: '/templates', label: '文本模板', icon: BookMarked }, ], }, { title: '系统', items: [ { path: '/notifications', label: '通知管理', icon: Bell }, { path: '/audit', label: '操作日志', icon: ScrollText }, { path: '/settings', label: '设置', icon: Settings }, ], }, ] /** * 侧边栏导航 */ export default function SidebarNav({ mobileOpen, onClose }: { mobileOpen: boolean; onClose: () => void }) { const location = useLocation() const [expandedGroups, setExpandedGroups] = useState>(new Set(navGroups.map(g => g.title))) const toggleGroup = (title: string) => { setExpandedGroups(prev => { const next = new Set(prev) if (next.has(title)) next.delete(title) else next.add(title) return next }) } const isActive = (path: string) => { if (path === '/') return location.pathname === '/' return location.pathname.startsWith(path) } return ( <> {/* 移动端遮罩 */} {mobileOpen && (
)} ) }