Initial commit
This commit is contained in:
111
src/components/layout/Sidebar.tsx
Normal file
111
src/components/layout/Sidebar.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
import { clsx } from 'clsx'
|
||||
import { useAppStore } from '@/stores/useAppStore'
|
||||
import {
|
||||
Home,
|
||||
BookOpen,
|
||||
Layers,
|
||||
LayoutGrid,
|
||||
Share2,
|
||||
Settings,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
} from 'lucide-react'
|
||||
|
||||
const navItems = [
|
||||
{ path: '/', label: '首页', icon: Home },
|
||||
{ path: '/knowledge-areas', label: '知识领域', icon: BookOpen },
|
||||
{ path: '/process-groups', label: '过程组', icon: Layers },
|
||||
{ path: '/process-matrix', label: '49过程矩阵', icon: LayoutGrid },
|
||||
{ path: '/process-graph', label: '过程关系图', icon: Share2 },
|
||||
{ path: '/settings', label: '设置', icon: Settings },
|
||||
]
|
||||
|
||||
export function Sidebar() {
|
||||
const location = useLocation()
|
||||
const sidebarOpen = useAppStore((s) => s.sidebarOpen)
|
||||
const toggleSidebar = useAppStore((s) => s.toggleSidebar)
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 移动端遮罩 */}
|
||||
{sidebarOpen && (
|
||||
<div
|
||||
className="fixed inset-0 z-20 bg-black/50 lg:hidden"
|
||||
onClick={toggleSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 侧边栏 */}
|
||||
<aside
|
||||
className={clsx(
|
||||
'fixed left-0 top-0 z-30 h-full bg-white dark:bg-gray-800 shadow-lg transition-all duration-300',
|
||||
sidebarOpen ? 'w-64' : 'w-20',
|
||||
'lg:translate-x-0',
|
||||
sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'
|
||||
)}
|
||||
>
|
||||
{/* Logo */}
|
||||
<div className="flex h-16 items-center justify-between px-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<Link to="/" className="flex items-center gap-3">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 text-white font-bold text-lg">
|
||||
IT
|
||||
</div>
|
||||
{sidebarOpen && (
|
||||
<span className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
ITTOView
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
<button
|
||||
onClick={toggleSidebar}
|
||||
className="hidden lg:flex h-8 w-8 items-center justify-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-500"
|
||||
aria-label="切换侧边栏"
|
||||
>
|
||||
{sidebarOpen ? <ChevronLeft size={18} /> : <ChevronRight size={18} />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 导航菜单 */}
|
||||
<nav className="mt-4 px-3">
|
||||
<ul className="space-y-1">
|
||||
{navItems.map((item) => {
|
||||
const isActive = location.pathname === item.path ||
|
||||
(item.path !== '/' && location.pathname.startsWith(item.path))
|
||||
const Icon = item.icon
|
||||
|
||||
return (
|
||||
<li key={item.path}>
|
||||
<Link
|
||||
to={item.path}
|
||||
className={clsx(
|
||||
'flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors',
|
||||
isActive
|
||||
? 'bg-indigo-50 text-indigo-600 dark:bg-indigo-900/50 dark:text-indigo-400'
|
||||
: 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'
|
||||
)}
|
||||
>
|
||||
<Icon size={20} />
|
||||
{sidebarOpen && <span>{item.label}</span>}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{/* 底部信息 */}
|
||||
{sidebarOpen && (
|
||||
<div className="absolute bottom-4 left-4 right-4">
|
||||
<div className="rounded-lg bg-gray-50 dark:bg-gray-700/50 p-4">
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 mb-1">PMBOK 第6版</div>
|
||||
<div className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
49个过程 · 10大知识领域
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</aside>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user