118 lines
3.8 KiB
TypeScript
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>
|
|
);
|
|
}
|