Files
model-platform/frontend/app/components/common/Sidebar.tsx
T
2026-08-25 16:52:10 +08:00

118 lines
3.8 KiB
TypeScript

import Icon from "./Icon";
type ActivePage = "home" | "scripts" | "schedules" | "system";
type SidebarProps = {
activePage: ActivePage;
collapsed: boolean;
onNavigate: (page: ActivePage) => void;
onToggleCollapse: () => void;
onEndEditing?: () => void;
onSelectScript?: (scriptId: null) => void;
editSessionRef?: React.RefObject<unknown | null>;
isSystemAdmin?: boolean;
};
type NavigationItem = {
label: string;
icon: "home" | "script" | "schedule" | "settings";
page: ActivePage;
};
export function Sidebar({
activePage,
collapsed,
onNavigate,
onToggleCollapse,
onEndEditing,
onSelectScript,
editSessionRef,
isSystemAdmin = false,
}: SidebarProps) {
const handleNavigationClick = (page: ActivePage) => {
if (page !== "scripts") {
if (editSessionRef?.current) {
onEndEditing?.();
} else {
onSelectScript?.(null);
}
}
onNavigate(page);
};
const navigation: NavigationItem[] = [
{ label: "工作台", icon: "home", page: "home" },
{ label: "构建脚本", icon: "script", page: "scripts" },
{ label: "调度配置", icon: "schedule", page: "schedules" },
];
if (isSystemAdmin) {
navigation.push({ label: "系统管理", icon: "settings", page: "system" });
}
return (
<aside
className={`sidebar-shell relative z-[3] flex min-h-screen flex-col text-[#c9d7e7] shadow-[5px_0_18px_rgb(19_47_77_/_9%)] transition-[width,min-width] duration-200 ease-in-out ${
collapsed
? "w-[70px] min-w-[70px]"
: "w-[210px] min-w-[210px] max-xl:w-[184px] max-xl:min-w-[184px]"
}`}
>
<div
className={`flex h-[70px] items-center gap-2.5 border-b border-white/8 text-white ${
collapsed ? "justify-center px-0" : "px-[17px]"
}`}
>
<span className="grid size-9 shrink-0 place-items-center text-[#5ca9ff]">
<Icon name="brand" size={31} />
</span>
{!collapsed && (
<span className="whitespace-nowrap text-[15px] font-bold tracking-[0.01em] max-xl:text-[13px]">
模型实验开发平台
</span>
)}
</div>
{!collapsed && (
<nav
className="flex flex-col gap-[7px] px-[9px] py-[19px]"
aria-label="主导航"
>
{navigation.map((item) => {
const isActive = item.page === activePage;
return (
<button
className={`flex h-12 w-full items-center gap-[13px] rounded-md border-0 px-[17px] text-left text-sm transition-[color,background] duration-[180ms] ease-in-out ${
isActive
? "bg-gradient-to-br from-[#1479e8] to-[#1267ca] text-white shadow-[0_7px_18px_rgb(7_96_193_/_26%)]"
: "bg-transparent text-[#b9cade] hover:bg-white/6 hover:text-white"
}`}
key={item.label}
type="button"
onClick={() => handleNavigationClick(item.page)}
>
<Icon name={item.icon} size={19} />
<span>{item.label}</span>
</button>
);
})}
</nav>
)}
<button
className={`mt-auto mb-[17px] flex h-12 cursor-pointer items-center gap-[13px] rounded-md border-0 bg-transparent text-sm text-[#b9cade] transition-[color,background] duration-[180ms] ease-in-out hover:bg-white/6 hover:text-white ${
collapsed
? "mx-auto w-[calc(100%-18px)] justify-center px-[17px]"
: "mx-[9px] w-[calc(100%-18px)] px-[17px]"
}`}
type="button"
onClick={onToggleCollapse}
aria-label={collapsed ? "展开菜单" : "收起菜单"}
>
<Icon name="menu" size={19} />
{!collapsed && <span>收起菜单</span>}
</button>
</aside>
);
}