1
This commit is contained in:
parent
9250627dc0
commit
1e8c38dfc5
@ -1,37 +1,10 @@
|
|||||||
import { AuthGuard } from "@/components/auth-guard";
|
import { AuthGuard } from "@/components/auth-guard";
|
||||||
import { UserMenu } from "@/components/user-menu";
|
import { MobileShell } from "@/components/mobile-shell";
|
||||||
import { SidebarNav } from "@/components/nav";
|
|
||||||
|
|
||||||
export default function AuthLayout({ children }: { children: React.ReactNode }) {
|
export default function AuthLayout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<AuthGuard>
|
<AuthGuard>
|
||||||
<div className="flex min-h-screen">
|
<MobileShell>{children}</MobileShell>
|
||||||
<aside className="fixed inset-y-0 left-0 z-40 flex w-48 sm:w-60 flex-col glass-sidebar">
|
|
||||||
<div className="px-4 sm:px-6 pt-5 sm:pt-7 pb-4 sm:pb-5">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-amber-600 text-sm font-bold text-white shadow-glow-sm">
|
|
||||||
A
|
|
||||||
</div>
|
|
||||||
<div className="min-w-0">
|
|
||||||
<h1 className="text-sm font-semibold tracking-tight">AlphaX Agent</h1>
|
|
||||||
<p className="text-xs text-text-muted mt-0.5 font-light tracking-wide">A 股投研作战台</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mx-5 h-px bg-gradient-to-r from-transparent via-border-default to-transparent" />
|
|
||||||
|
|
||||||
<SidebarNav />
|
|
||||||
|
|
||||||
<div className="px-4 sm:px-6 py-4 sm:py-5 border-t border-border-subtle">
|
|
||||||
<UserMenu />
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<main className="min-h-screen flex-1 ml-48 sm:ml-60 pb-10">
|
|
||||||
{children}
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</AuthGuard>
|
</AuthGuard>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
60
frontend/src/components/mobile-shell.tsx
Normal file
60
frontend/src/components/mobile-shell.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { UserMenu } from "@/components/user-menu";
|
||||||
|
import { SidebarNav } from "@/components/nav";
|
||||||
|
|
||||||
|
export function MobileShell({ children }: { children: React.ReactNode }) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen">
|
||||||
|
{/* 移动端汉堡按钮 */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="打开菜单"
|
||||||
|
onClick={() => setOpen(true)}
|
||||||
|
className="fixed left-3 top-3 z-50 flex h-10 w-10 items-center justify-center rounded-lg glass-sidebar md:hidden"
|
||||||
|
>
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
||||||
|
<path d="M4 6h16M4 12h16M4 18h16" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* 移动端遮罩 */}
|
||||||
|
{open && (
|
||||||
|
<div className="fixed inset-0 z-40 bg-black/50 md:hidden" onClick={() => setOpen(false)} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<aside
|
||||||
|
className={`fixed inset-y-0 left-0 z-40 flex w-48 sm:w-60 flex-col glass-sidebar transition-transform duration-200 md:translate-x-0 ${
|
||||||
|
open ? "translate-x-0" : "-translate-x-full"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="px-4 sm:px-6 pt-5 sm:pt-7 pb-4 sm:pb-5">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-amber-600 text-sm font-bold text-white shadow-glow-sm">
|
||||||
|
A
|
||||||
|
</div>
|
||||||
|
<div className="min-w-0">
|
||||||
|
<h1 className="text-sm font-semibold tracking-tight">AlphaX Agent</h1>
|
||||||
|
<p className="text-xs text-text-muted mt-0.5 font-light tracking-wide">A 股投研作战台</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mx-5 h-px bg-gradient-to-r from-transparent via-border-default to-transparent" />
|
||||||
|
|
||||||
|
<SidebarNav onNavigate={() => setOpen(false)} />
|
||||||
|
|
||||||
|
<div className="px-4 sm:px-6 py-4 sm:py-5 border-t border-border-subtle">
|
||||||
|
<UserMenu />
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main className="min-h-screen flex-1 md:ml-60 pb-10">
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -134,13 +134,14 @@ function TasksIcon() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SideNavItem({ href, icon, label }: { href: string; icon: React.ReactNode; label: string }) {
|
function SideNavItem({ href, icon, label, onNavigate }: { href: string; icon: React.ReactNode; label: string; onNavigate?: () => void }) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const isActive = pathname === href || (href !== "/dashboard" && pathname.startsWith(href));
|
const isActive = pathname === href || (href !== "/dashboard" && pathname.startsWith(href));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={href}
|
href={href}
|
||||||
|
onClick={onNavigate}
|
||||||
className={`flex min-w-0 items-center gap-2.5 sm:gap-3 rounded-xl px-3 sm:px-4 py-2.5 text-sm transition-all duration-200 ${
|
className={`flex min-w-0 items-center gap-2.5 sm:gap-3 rounded-xl px-3 sm:px-4 py-2.5 text-sm transition-all duration-200 ${
|
||||||
isActive
|
isActive
|
||||||
? "text-text-primary bg-surface-4"
|
? "text-text-primary bg-surface-4"
|
||||||
@ -153,24 +154,24 @@ function SideNavItem({ href, icon, label }: { href: string; icon: React.ReactNod
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SidebarNav() {
|
export function SidebarNav({ onNavigate }: { onNavigate?: () => void }) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="flex-1 overflow-y-auto px-2 sm:px-3 py-4 sm:py-5 space-y-1">
|
<nav className="flex-1 overflow-y-auto px-2 sm:px-3 py-4 sm:py-5 space-y-1">
|
||||||
<SideNavItem href="/dashboard" icon={<DashboardIcon />} label="今日作战" />
|
<SideNavItem href="/dashboard" icon={<DashboardIcon />} label="今日作战" onNavigate={onNavigate} />
|
||||||
<SideNavItem href="/recommendations" icon={<TargetIcon />} label="推荐池" />
|
<SideNavItem href="/recommendations" icon={<TargetIcon />} label="推荐池" onNavigate={onNavigate} />
|
||||||
<SideNavItem href="/sectors" icon={<FireIcon />} label="板块主线" />
|
<SideNavItem href="/sectors" icon={<FireIcon />} label="板块主线" onNavigate={onNavigate} />
|
||||||
<SideNavItem href="/sentiment" icon={<RadarIcon />} label="舆情雷达" />
|
<SideNavItem href="/sentiment" icon={<RadarIcon />} label="舆情雷达" onNavigate={onNavigate} />
|
||||||
<SideNavItem href="/watchlists" icon={<WatchlistIcon />} label="自选股" />
|
<SideNavItem href="/watchlists" icon={<WatchlistIcon />} label="自选股" onNavigate={onNavigate} />
|
||||||
<SideNavItem href="/chat" icon={<ChatIcon />} label="研究助手" />
|
<SideNavItem href="/chat" icon={<ChatIcon />} label="研究助手" onNavigate={onNavigate} />
|
||||||
{user?.role === "admin" && (
|
{user?.role === "admin" && (
|
||||||
<>
|
<>
|
||||||
<SideNavItem href="/strategy" icon={<StrategyIcon />} label="策略校准" />
|
<SideNavItem href="/strategy" icon={<StrategyIcon />} label="策略校准" onNavigate={onNavigate} />
|
||||||
<SideNavItem href="/ops-logs" icon={<LogsIcon />} label="系统日志" />
|
<SideNavItem href="/ops-logs" icon={<LogsIcon />} label="系统日志" onNavigate={onNavigate} />
|
||||||
<SideNavItem href="/data-health" icon={<HealthIcon />} label="数据源健康" />
|
<SideNavItem href="/data-health" icon={<HealthIcon />} label="数据源健康" onNavigate={onNavigate} />
|
||||||
<SideNavItem href="/tasks" icon={<TasksIcon />} label="任务中心" />
|
<SideNavItem href="/tasks" icon={<TasksIcon />} label="任务中心" onNavigate={onNavigate} />
|
||||||
<SideNavItem href="/settings" icon={<SettingsIcon />} label="管理设置" />
|
<SideNavItem href="/settings" icon={<SettingsIcon />} label="管理设置" onNavigate={onNavigate} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user