import { useEffect, useMemo, useState } from 'react'; import { useNavigate, useLocation } from 'react-router'; import { Layout, Menu } from 'antd'; import type { MenuProps } from 'antd'; import { useAppStore } from '@/stores/useAppStore'; import { useAuthStore } from '@/stores/useAuthStore'; import { WindowsOutlined } from '@ant-design/icons'; import { menusToMenuItems, findOpenKeys, findActiveMenuMeta } from '@/router/dynamicRoutes'; function useActivePath(): string { const location = useLocation(); const menus = useAuthStore((s) => s.menus); const activeMenu = findActiveMenuMeta(location.pathname, menus); return activeMenu || location.pathname; } const Sidebar: React.FC = () => { const collapsed = useAppStore((s) => s.collapsed); const menuTheme = useAppStore((s) => s.menuTheme); const menus = useAuthStore((s) => s.menus); const navigate = useNavigate(); const activePath = useActivePath(); const menuItems = useMemo(() => menusToMenuItems(menus), [menus]); /** * 受控 `openKeys`:保持菜单聚焦简洁。任意层级只允许同时展开一条祖先链 —— * 点击任一子菜单时,其它分支全部收起。 * * 参考 https://ant.design/components/menu-cn#menu-demo-sider-current 的 accordion 写法, * 在此基础上将"只允许一条根级"扩展为"任意层级都收紧到一条分支"。 */ const [openKeys, setOpenKeys] = useState([]); // 路由切换或菜单数据更新:把 openKeys 同步成"激活路径所在的那条分支" useEffect(() => { setOpenKeys(findOpenKeys(activePath, menus)); // eslint-disable-line react-hooks/set-state-in-effect }, [activePath, menus]); const handleOpenChange: MenuProps['onOpenChange'] = (keys) => { // 找到本次新打开的 key(不在旧 openKeys 中的) const latestOpenKey = keys.find((k) => !openKeys.includes(k)); if (latestOpenKey) { // 用户打开了一个子菜单 → 只保留它本身 + 其祖先链,其它分支全部收起 setOpenKeys([...findOpenKeys(latestOpenKey, menus), latestOpenKey]); } else { // 仅是收起操作 → 直接同步 setOpenKeys(keys); } }; const siderStyle: React.CSSProperties = { overflow: 'auto', height: '100vh', position: 'fixed', insetInlineStart: 0, top: 0, scrollbarWidth: 'thin', scrollbarGutter: 'stable', left: 0, bottom: 0, }; const sidebarTitle = import.meta.env.VITE_APP_SIDEBAR_TITLE || ''; return (
{!collapsed && ( {sidebarTitle} )}
navigate(key)} /> ); }; export default Sidebar;