| 1 | /** |
| 2 | * LayoutSidebar - 左侧侧边栏布局组件 |
| 3 | * 包含 Logo、主导航、底部功能区(余额、插件)、用户下拉菜单 |
| 4 | * 支持展开/收缩两种状态 |
| 5 | */ |
| 6 | 'use client' |
| 7 | |
| 8 | import type { IRouterDataItem } from '../routerData' |
| 9 | import type { NavItemData } from './types' |
| 10 | import { useShallow } from 'zustand/shallow' |
| 11 | import { useNavigationLogic, useVisibleRouterData } from '@/app/layout/shared' |
| 12 | import { useSettingsModalStore } from '@/store/settingsModal' |
| 13 | import { useUserStore } from '@/store/user' |
| 14 | import { cn } from '@/utils/className' |
| 15 | import { BottomSection, LogoSection, NavSection, UserDropdownMenu } from './components' |
| 16 | import { ConfigManagerEntry } from './components/BottomSection/ConfigManagerEntry' |
| 17 | import { MyChannelsEntry } from './components/BottomSection/MyChannelsEntry' |
| 18 | |
| 19 | /** |
| 20 | * 侧边栏主组件 |
| 21 | */ |
| 22 | function LayoutSidebar() { |
| 23 | const { currRouter, isAuthPage } = useNavigationLogic() |
| 24 | const visibleRoutes = useVisibleRouterData() |
| 25 | |
| 26 | // 获取侧边栏状态和设置方法 |
| 27 | const { sidebarCollapsed: collapsed, setSidebarCollapsed: setCollapsed } = useUserStore( |
| 28 | useShallow(state => ({ |
| 29 | sidebarCollapsed: state.sidebarCollapsed, |
| 30 | setSidebarCollapsed: state.setSidebarCollapsed, |
| 31 | })), |
| 32 | ) |
| 33 | |
| 34 | const { openSettings } = useSettingsModalStore() |
| 35 | |
| 36 | const mapNavItem = (item: IRouterDataItem): NavItemData => ({ |
| 37 | path: item.path, |
| 38 | translationKey: item.translationKey, |
| 39 | icon: item.icon, |
| 40 | children: item.children?.map(mapNavItem), |
| 41 | }) |
| 42 | |
| 43 | // 首页、auth、websit 页面不显示侧边栏 |
| 44 | if (isAuthPage) { |
| 45 | return null |
| 46 | } |
| 47 | |
| 48 | // 转换路由数据为 NavSection 所需格式 |
| 49 | const navItems = visibleRoutes.filter(item => item.translationKey !== 'myTasks').map(mapNavItem) |
| 50 | |
| 51 | return ( |
| 52 | <aside |
| 53 | className={cn( |
| 54 | 'group sticky left-0 top-0 hidden h-screen flex-col border-r border-sidebar-border bg-sidebar p-3 transition-all duration-300 md:flex', |
| 55 | collapsed ? 'w-[68px] min-w-[68px]' : 'w-[240px] min-w-[240px]', |
| 56 | )} |
| 57 | > |
| 58 | {/* Logo 区域 - 固定 */} |
| 59 | <LogoSection collapsed={collapsed} onToggle={() => setCollapsed(!collapsed)} /> |
| 60 | |
| 61 | {/* 可滚动区域:主导航 */} |
| 62 | <div className="flex-1 min-h-0 overflow-y-auto overflow-x-hidden scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent"> |
| 63 | <NavSection items={navItems} currentRoute={currRouter!} collapsed={collapsed} /> |
| 64 | </div> |
| 65 | |
| 66 | {/* 底部固定区域 - 不随滚动 */} |
| 67 | <div className="flex-shrink-0"> |
| 68 | {/* 配置管理入口 */} |
| 69 | <div className="pb-1 flex flex-1"> |
| 70 | <ConfigManagerEntry collapsed={collapsed} /> |
| 71 | </div> |
| 72 | |
| 73 | {/* 我的频道入口 */} |
| 74 | <div className="pb-1 flex flex-1"> |
| 75 | <MyChannelsEntry collapsed={collapsed} /> |
| 76 | </div> |
| 77 | |
| 78 | {/* 底部功能区 */} |
| 79 | <BottomSection collapsed={collapsed} onOpenSettings={openSettings} /> |
| 80 | |
| 81 | {/* 用户下拉菜单 */} |
| 82 | <div className="mt-2 border-t border-sidebar-border pt-2"> |
| 83 | <UserDropdownMenu collapsed={collapsed} onOpenSettings={openSettings} /> |
| 84 | </div> |
| 85 | </div> |
| 86 | </aside> |
| 87 | ) |
| 88 | } |
| 89 | |
| 90 | export default LayoutSidebar |
| 91 |