update:css样式拆分

This commit is contained in:
xiaozhu
2026-08-25 16:52:10 +08:00
parent 76aa2dcb27
commit 12111de788
27 changed files with 1404 additions and 3059 deletions
+53 -22
View File
@@ -40,46 +40,77 @@ export function Sidebar({
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${collapsed ? " is-collapsed" : ""}`}>
<div className="brand">
<span className="brand__mark"><Icon name="brand" size={31} /></span>
{!collapsed && <span className="brand__name"></span>}
<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="navigation" aria-label="主导航">
{navigation.map((item) => (
<button
className={`nav-item${
item.page === activePage ? " nav-item--active" : ""
}`}
key={item.label}
type="button"
onClick={() => handleNavigationClick(item.page)}
>
<Icon name={item.icon} size={19} />
<span>{item.label}</span>
</button>
))}
<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="sidebar-footer" type="button" onClick={onToggleCollapse}>
<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} />
<span>{collapsed ? "展开菜单" : "收起菜单"}</span>
{!collapsed && <span></span>}
</button>
</aside>
);
+13 -2
View File
@@ -9,12 +9,23 @@ type ToastProps = {
toast: ToastState | null;
};
const toneIconClass: Record<ToastState["tone"], string> = {
success: "bg-[#e6f7f0] text-[#177b55]",
error: "bg-[#fff0f0] text-[#b94a4a]",
info: "bg-[#eaf4fd] text-[#2675bf]",
};
export function Toast({ toast }: ToastProps) {
if (!toast) return null;
return (
<div className={`toast toast--${toast.tone}`} role="status">
<span>
<div
className="toast-enter fixed top-[82px] right-[21px] z-30 flex min-h-[46px] min-w-[260px] max-w-[410px] items-center gap-[9px] rounded-lg border border-[#cfdbe6] bg-white px-3.5 py-2.5 text-xs text-[#43566a] shadow-[0_12px_35px_rgb(20_42_66/14%)]"
role="status"
>
<span
className={`grid size-[27px] shrink-0 place-items-center rounded-full ${toneIconClass[toast.tone]}`}
>
<Icon name={toast.tone === "success" ? "check" : "info"} size={17} />
</span>
{toast.message}
+71 -47
View File
@@ -32,80 +32,104 @@ export function Topbar({
};
return (
<header className="topbar">
<div className="page-title">
<button className="icon-button icon-button--back" type="button">
<header className="relative z-[2] flex h-[70px] min-h-[70px] items-center justify-between border-b border-[#dfe6ee] bg-white px-[22px]">
<div className="flex items-center gap-[9px]">
<button
className="grid h-[31px] w-[31px] rotate-180 cursor-pointer place-items-center rounded-[7px] border border-transparent bg-white text-[#7b8999] hover:border-[#b9c9da] hover:bg-[#f7faff]"
type="button"
>
<Icon name="chevron" size={19} />
</button>
<div>
<span className="page-title__eyebrow"></span>
<h1>{pageTitles[activePage] || "工作台"}</h1>
<span className="text-[10px] font-[650] uppercase tracking-[0.08em] text-[#94a2b3]">
</span>
<h1 className="m-0 mt-px text-[18px] leading-[1.1] text-[#14243a]">
{pageTitles[activePage] || "工作台"}
</h1>
</div>
</div>
<div className="topbar__actions">
<div className="api-state">
<span className={`api-state__dot${apiOnline ? " is-online" : ""}`} />
<div className="flex items-center gap-[14px]">
<div className="flex items-center gap-[7px] text-xs text-[#768597] max-xl:hidden">
<span
className={`size-[7px] rounded-full ${
apiOnline
? "bg-[#19b878] shadow-[0_0_0_3px_rgb(25_184_120_/_11%)]"
: "bg-[#d05151] shadow-[0_0_0_3px_rgb(208_81_81_/_10%)]"
}`}
/>
{apiOnline ? "服务已连接" : "服务未连接"}
</div>
<div className="topbar-menu-wrap">
<div className="relative flex items-center gap-[9px]">
<button
className="workspace-switcher"
className="flex h-[46px] cursor-pointer items-center gap-[9px] rounded-lg border border-[#e0e7ee] bg-white px-[11px] hover:border-[#b9cce0]"
type="button"
onClick={() => onSetWorkspaceMenuOpen(!workspaceMenuOpen)}
>
<span className="workspace-switcher__icon">
<span className="grid size-[30px] place-items-center rounded-[7px] bg-gradient-to-br from-[#23aa73] to-[#158d5d] text-white">
<Icon name="workspace" size={18} />
</span>
<span>
<small> Workspace</small>
<strong>{currentWorkspace.workspace_name}</strong>
<span className="flex min-w-[155px] max-xl:min-w-[130px] flex-col items-start">
<small className="text-[10px] text-[#97a3b1]"> Workspace</small>
<strong className="max-w-[190px] truncate text-[13px] text-[#26384d]">
{currentWorkspace.workspace_name}
</strong>
</span>
<Icon name="chevron" size={15} />
</button>
{workspaceMenuOpen && (
<div className="topbar-dropdown">
{workspaces.map((workspace) => (
<button
className={
workspace.workspace_id === currentWorkspace.workspace_id
? "is-selected"
: ""
}
type="button"
key={workspace.workspace_id}
onClick={() => {
onSetCurrentWorkspace(workspace.workspace_id);
onSetWorkspaceMenuOpen(false);
}}
>
<Icon name="workspace" size={15} />
<span>
<strong>{workspace.workspace_name}</strong>
<small>
{workspace.workspace_id === currentWorkspace.workspace_id
? "当前使用"
: "点击切换"}
</small>
</span>
</button>
))}
<div className="absolute top-[calc(100%+7px)] right-0 z-50 w-[260px] rounded-lg border border-[#dce5ed] bg-white p-1.5 shadow-[0_14px_38px_rgb(17_43_70_/_18%)]">
{workspaces.map((workspace) => {
const isSelected =
workspace.workspace_id === currentWorkspace.workspace_id;
return (
<button
className={`flex w-full cursor-pointer items-center gap-2.5 rounded-md border-0 px-2.5 py-2 text-left text-[#40576d] ${
isSelected
? "bg-brand-soft"
: "bg-transparent hover:bg-brand-soft"
}`}
type="button"
key={workspace.workspace_id}
onClick={() => {
onSetCurrentWorkspace(workspace.workspace_id);
onSetWorkspaceMenuOpen(false);
}}
>
<Icon name="workspace" size={15} />
<span className="flex min-w-0 flex-col gap-0.5">
<strong className="truncate text-[11px] text-[#243a50]">
{workspace.workspace_name}
</strong>
<small className="text-[9px] text-[#8797a7]">
{isSelected ? "当前使用" : "点击切换"}
</small>
</span>
</button>
);
})}
</div>
)}
</div>
<div className="topbar-menu-wrap">
<button className="user-menu" type="button">
<span className="avatar">
<div className="relative flex items-center gap-[9px]">
<button
className="flex h-[46px] cursor-pointer items-center gap-[9px] rounded-lg border border-transparent bg-white px-[11px] hover:border-[#b9cce0]"
type="button"
>
<span className="grid size-[34px] place-items-center rounded-full bg-gradient-to-br from-[#3b92ed] to-[#1869c9] text-sm font-bold text-white">
{user?.display_name?.slice(0, 1) ?? "?"}
</span>
<span className="user-menu__copy">
<strong>{user?.display_name ?? "未知用户"}</strong>
<small>
<span className="flex min-w-[50px] flex-col items-start">
<strong className="max-w-[190px] truncate text-[13px] text-[#26384d]">
{user?.display_name ?? "未知用户"}
</strong>
<small className="text-[10px] text-[#97a3b1]">
{user?.role_code === "admin" ? "管理员" : "开发人员"}
</small>
</span>
</button>
<button
className="text-button"
className="inline-flex cursor-pointer items-center gap-0.5 border-0 bg-transparent px-px py-[5px] text-xs font-semibold text-[#1474d4] hover:text-[#0d58a2]"
type="button"
onClick={() => {
void onLogout();
@@ -1,4 +1,5 @@
import Icon from "./Icon";
import { primaryButtonClass } from "../../features/platform/modalUi";
type WelcomePanelProps = {
onCreateScript: () => void;
@@ -6,16 +7,20 @@ type WelcomePanelProps = {
export function WelcomePanel({ onCreateScript }: WelcomePanelProps) {
return (
<div className="welcome-panel">
<div className="welcome-panel__visual">
<div className="welcome-panel-shell flex min-h-full flex-col items-center justify-center p-[35px] text-center">
<div className="mb-[21px] grid size-[86px] place-items-center rounded-[25px] border border-[#cfe0f0] bg-gradient-to-br from-white to-[#eaf4fd] text-[#3d8cd9] shadow-[0_16px_35px_rgb(40_103_165/12%)]">
<Icon name="script" size={42} />
</div>
<span className="welcome-panel__label"></span>
<h2></h2>
<p>
<span className="text-[10px] font-extrabold tracking-[0.12em] text-[#2e81ce]">
</span>
<h2 className="mb-[7px] mt-[9px] text-[23px] text-[#21354c]">
</h2>
<p className="mb-[22px] w-full max-w-[430px] text-xs leading-[1.8] text-[#76879a]">
Notebook 使 Python
</p>
<button className="primary-button" onClick={onCreateScript}>
<button className={primaryButtonClass} type="button" onClick={onCreateScript}>
<Icon name="plus" size={17} />
</button>