180 lines
5.9 KiB
TypeScript
180 lines
5.9 KiB
TypeScript
import {
|
|
GitBranch,
|
|
Server,
|
|
Key,
|
|
Cpu,
|
|
Wrench,
|
|
Download,
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
Rocket,
|
|
Zap,
|
|
History,
|
|
CloudSync,
|
|
Puzzle,
|
|
Settings,
|
|
BookOpen,
|
|
Users,
|
|
Sun,
|
|
Moon,
|
|
LogOut,
|
|
} from 'lucide-react';
|
|
import { useTheme } from '@/providers/theme';
|
|
|
|
type ModuleType = 'pipelines' | 'gitops' | 'batch-release' | 'release-records' | 'environments' | 'credentials' | 'build-nodes' | 'tools' | 'jenkins-import' | 'plugins' | 'users' | 'docs' | 'settings';
|
|
|
|
interface SidebarProps {
|
|
activeModule: ModuleType;
|
|
onModuleChange: (module: ModuleType) => void;
|
|
collapsed: boolean;
|
|
onToggleCollapse: () => void;
|
|
onLogout?: () => void;
|
|
}
|
|
|
|
const menuGroups: { label: string; items: { id: ModuleType; label: string; icon: typeof GitBranch }[] }[] = [
|
|
{
|
|
label: '发布',
|
|
items: [
|
|
{ id: 'pipelines', label: '流水线', icon: GitBranch },
|
|
{ id: 'gitops', label: 'GitOps 部署', icon: CloudSync },
|
|
{ id: 'batch-release', label: '批量发布', icon: Zap },
|
|
{ id: 'release-records', label: '发布记录', icon: History },
|
|
],
|
|
},
|
|
{
|
|
label: '资源',
|
|
items: [
|
|
{ id: 'environments', label: '环境管理', icon: Server },
|
|
{ id: 'credentials', label: '凭证管理', icon: Key },
|
|
{ id: 'build-nodes', label: '构建节点', icon: Cpu },
|
|
],
|
|
},
|
|
{
|
|
label: '集成',
|
|
items: [
|
|
{ id: 'jenkins-import', label: 'Jenkins导入', icon: Download },
|
|
{ id: 'tools', label: '工具配置', icon: Wrench },
|
|
],
|
|
},
|
|
{
|
|
label: '系统',
|
|
items: [
|
|
{ id: 'docs', label: '文档', icon: BookOpen },
|
|
{ id: 'plugins', label: '插件市场', icon: Puzzle },
|
|
{ id: 'users', label: '用户管理', icon: Users },
|
|
{ id: 'settings', label: '系统设置', icon: Settings },
|
|
],
|
|
},
|
|
];
|
|
|
|
export default function Sidebar({ activeModule, onModuleChange, collapsed, onToggleCollapse, onLogout }: SidebarProps) {
|
|
const { toggle: themeToggle, isDark } = useTheme();
|
|
|
|
return (
|
|
<div
|
|
className="flex flex-col border-r transition-all duration-300 ease-in-out h-full"
|
|
style={{
|
|
width: collapsed ? '56px' : '200px',
|
|
background: 'var(--panel-bg)',
|
|
borderColor: 'var(--border-color)',
|
|
}}
|
|
>
|
|
{/* Logo area */}
|
|
<div
|
|
className="flex items-center gap-2 px-3 h-12 border-b flex-shrink-0"
|
|
style={{ borderColor: 'var(--border-color)' }}
|
|
>
|
|
<div
|
|
className="w-7 h-7 rounded-md flex items-center justify-center flex-shrink-0"
|
|
style={{ background: 'var(--accent-blue)' }}
|
|
>
|
|
<Rocket className="w-4 h-4 text-white" />
|
|
</div>
|
|
{!collapsed && (
|
|
<span className="text-[13px] font-semibold truncate" style={{ color: 'var(--text-primary)' }}>
|
|
应用发布
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
{/* Menu groups */}
|
|
<div className="flex-1 py-2 overflow-y-auto custom-scrollbar">
|
|
{menuGroups.map((group) => (
|
|
<div key={group.label} className="mb-2">
|
|
{!collapsed && (
|
|
<div
|
|
className="px-3 py-1 text-[10px] font-semibold uppercase tracking-wider"
|
|
style={{ color: 'var(--text-tertiary)' }}
|
|
>
|
|
{group.label}
|
|
</div>
|
|
)}
|
|
<div className="px-2 space-y-[2px]">
|
|
{group.items.map((item) => {
|
|
const Icon = item.icon;
|
|
const isActive = activeModule === item.id;
|
|
return (
|
|
<button
|
|
key={item.id}
|
|
onClick={() => onModuleChange(item.id)}
|
|
className="w-full flex items-center gap-2.5 px-2.5 py-[7px] rounded-md transition-all duration-150 group relative"
|
|
style={{
|
|
background: isActive ? 'var(--accent-blue-light)' : 'transparent',
|
|
color: isActive ? 'var(--accent-blue)' : 'var(--text-secondary)',
|
|
}}
|
|
title={collapsed ? item.label : undefined}
|
|
>
|
|
<Icon className="w-[18px] h-[18px] flex-shrink-0" />
|
|
{!collapsed && (
|
|
<span className="text-[13px] font-medium truncate flex-1 text-left">
|
|
{item.label}
|
|
</span>
|
|
)}
|
|
{isActive && !collapsed && (
|
|
<div
|
|
className="absolute left-0 top-1/2 -translate-y-1/2 w-[3px] h-5 rounded-r-full"
|
|
style={{ background: 'var(--accent-blue)' }}
|
|
/>
|
|
)}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Bottom bar: theme toggle + collapse */}
|
|
<div
|
|
className="px-2 py-2 border-t flex items-center justify-center gap-1"
|
|
style={{ borderColor: 'var(--border-color)' }}
|
|
>
|
|
<button
|
|
onClick={themeToggle}
|
|
className="p-1.5 rounded-md transition-colors hover:bg-black/5"
|
|
style={{ color: 'var(--text-tertiary)' }}
|
|
title="切换深色/浅色主题"
|
|
>
|
|
{isDark ? <Sun className="w-4 h-4" /> : <Moon className="w-4 h-4" />}
|
|
</button>
|
|
<button
|
|
onClick={onLogout}
|
|
className="p-1.5 rounded-md transition-colors hover:bg-black/5"
|
|
style={{ color: 'var(--text-tertiary)' }}
|
|
title="退出登录"
|
|
>
|
|
<LogOut className="w-4 h-4" />
|
|
</button>
|
|
<button
|
|
onClick={onToggleCollapse}
|
|
className="p-1.5 rounded-md transition-colors hover:bg-black/5"
|
|
style={{ color: 'var(--text-tertiary)' }}
|
|
title={collapsed ? '展开' : '收起'}
|
|
>
|
|
{collapsed ? <ChevronRight className="w-4 h-4" /> : <ChevronLeft className="w-4 h-4" />}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|