Spaces:
Sleeping
Sleeping
| import type { ReactNode } from "react" | |
| import { Link, NavLink, Outlet, useLocation } from "react-router-dom" | |
| import { BarChart3, Blocks, LayoutDashboard, RotateCcw, Sparkles } from "lucide-react" | |
| import { cn } from "@/lib/utils" | |
| import { useTheme } from "@/hooks/useTheme" | |
| import { useAppStore } from "@/store/useAppStore" | |
| function ShellNavItem(props: { to: string; icon: ReactNode; label: string }) { | |
| return ( | |
| <NavLink | |
| to={props.to} | |
| end | |
| className={({ isActive }) => | |
| cn( | |
| "group flex items-center gap-3 rounded-xl px-3 py-2 text-sm transition", | |
| isActive | |
| ? "bg-[color:var(--panel)] shadow-[var(--glow)]" | |
| : "text-[color:var(--muted)] hover:bg-[color:var(--panel2)] hover:text-[color:var(--text)]", | |
| ) | |
| } | |
| > | |
| <span className="grid h-9 w-9 place-items-center rounded-lg border border-[color:var(--stroke)] bg-[color:var(--panel2)] text-[color:var(--text)] transition group-hover:border-[color:var(--accentSoft)]"> | |
| {props.icon} | |
| </span> | |
| <span className="font-medium tracking-wide">{props.label}</span> | |
| </NavLink> | |
| ) | |
| } | |
| export default function ConsoleLayout() { | |
| const { toggleTheme, isDark } = useTheme() | |
| const resetDemo = useAppStore((s) => s.resetDemo) | |
| const location = useLocation() | |
| const showDataLink = location.pathname.startsWith("/campaign/") | |
| return ( | |
| <div className="relative min-h-screen bg-[color:var(--bg)] text-[color:var(--text)]"> | |
| <div className="pointer-events-none absolute inset-0 opacity-60 [background:var(--bgFx)]" /> | |
| <div className="pointer-events-none absolute inset-0 opacity-[0.06] [background-image:radial-gradient(circle_at_1px_1px,rgba(255,255,255,1)_1px,transparent_0)] [background-size:22px_22px]" /> | |
| <div className="relative mx-auto grid max-w-[1220px] grid-cols-1 gap-6 px-4 py-6 md:grid-cols-[280px_1fr]"> | |
| <aside className="sticky top-6 hidden h-[calc(100vh-48px)] flex-col justify-between md:flex"> | |
| <div className="rounded-3xl border border-[color:var(--stroke)] bg-[color:var(--panel2)] p-4 shadow-[var(--glow)] backdrop-blur"> | |
| <Link to="/" className="group flex items-center gap-3"> | |
| <span className="grid h-11 w-11 place-items-center rounded-2xl border border-[color:var(--stroke)] bg-[color:var(--panel)] shadow-[0_0_0_1px_rgba(255,255,255,0.06)]"> | |
| <Sparkles className="h-5 w-5 text-[color:var(--accent)]" /> | |
| </span> | |
| <div className="leading-tight"> | |
| <div className="font-display text-lg tracking-[0.08em]">GROWTH LOOP</div> | |
| <div className="text-xs text-[color:var(--muted)]">活动搭建 · 留资 · 核销 · 复盘</div> | |
| </div> | |
| </Link> | |
| <div className="mt-5 space-y-2"> | |
| <ShellNavItem to="/" icon={<LayoutDashboard className="h-4 w-4" />} label="运营控制台" /> | |
| <ShellNavItem to="/campaign/new" icon={<Blocks className="h-4 w-4" />} label="新建活动" /> | |
| {showDataLink ? ( | |
| <ShellNavItem to={location.pathname.replace(/\/edit$|\/leads$/, "/leads")} icon={<BarChart3 className="h-4 w-4" />} label="线索与导出" /> | |
| ) : null} | |
| </div> | |
| </div> | |
| <div className="mt-4 space-y-3 rounded-3xl border border-[color:var(--stroke)] bg-[color:var(--panel2)] p-4 backdrop-blur"> | |
| <button | |
| type="button" | |
| onClick={toggleTheme} | |
| className="flex w-full items-center justify-between rounded-2xl border border-[color:var(--stroke)] bg-[color:var(--panel)] px-4 py-3 text-sm text-[color:var(--muted)] transition hover:text-[color:var(--text)]" | |
| > | |
| <span className="font-medium">模式</span> | |
| <span className="font-display text-xs tracking-[0.12em]">{isDark ? "DARK" : "LIGHT"}</span> | |
| </button> | |
| <button | |
| type="button" | |
| onClick={resetDemo} | |
| className="flex w-full items-center justify-between rounded-2xl border border-[color:var(--stroke)] bg-[color:var(--panel)] px-4 py-3 text-sm text-[color:var(--muted)] transition hover:border-[color:var(--accentSoft)] hover:text-[color:var(--text)]" | |
| > | |
| <span className="font-medium">重置演示数据</span> | |
| <RotateCcw className="h-4 w-4" /> | |
| </button> | |
| </div> | |
| </aside> | |
| <main className="min-w-0"> | |
| <Outlet /> | |
| </main> | |
| </div> | |
| </div> | |
| ) | |
| } | |