Files
AIPortPilot/frontend/src/app/admin/layout.tsx
T
selfrelease 956270d14d feat(frontend): P0 导航架构重构 — 6 业务域 + 角色化排序 + 新路由
- 投资人 Sidebar: 5 扁平分组 → 6 业务域(今日/组合/工作/增长/洞察/报告)
- 角色化导航: GP→组合优先, 投后负责人→今日优先, 投资经理→今日+组合
- 新增路由: /today /compare /threads /threads/[id] /workspace
- Admin: 单页标签 → 6 管理域 Sidebar(租户/用户/审计/数据源/安全/设置)
- 创始人: 3 底部导航 → 6 域(今日/经营/月报/Copilot/通知/我的)
- 新增 navConfig.ts 统一管理导航配置
2026-07-19 18:51:38 +08:00

110 lines
3.7 KiB
TypeScript

/** Admin 端布局 — 警示风格:slate-950 Header + amber 主色 + 6 管理域 Sidebar。 */
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import {
Building2, Users, ScrollText, Database, ShieldCheck, Settings,
} from "lucide-react";
/** Admin 6 管理域导航。 */
const adminNav = [
{
title: "租户管理",
items: [
{ href: "/admin", label: "系统概览", icon: Building2 },
{ href: "/admin/tenants", label: "租户列表", icon: Building2 },
],
},
{
title: "用户管理",
items: [
{ href: "/admin/users", label: "用户列表", icon: Users },
],
},
{
title: "审计日志",
items: [
{ href: "/admin/audit", label: "操作日志", icon: ScrollText },
],
},
{
title: "数据源",
items: [
{ href: "/admin/data-sources", label: "数据源管理", icon: Database },
],
},
{
title: "安全配置",
items: [
{ href: "/admin/security", label: "商业秘密保护", icon: ShieldCheck },
],
},
{
title: "系统设置",
items: [
{ href: "/admin/settings", label: "系统参数", icon: Settings },
],
},
];
/**
* Admin 端布局组件。
*
* 左侧 Sidebar 按 6 管理域分组,Header 使用 slate-950 + amber 警示色。
*/
export default function AdminLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
return (
<div className="flex min-h-screen bg-slate-100">
{/* 左侧 Sidebar */}
<aside className="sticky top-0 hidden h-screen w-52 shrink-0 bg-slate-950 text-white md:block">
<div className="flex h-14 items-center gap-2 px-4">
<span className="font-bold text-[var(--admin-primary)]">AIPortPilot</span>
<span className="inline-flex items-center rounded bg-[var(--admin-primary)]/20 px-1.5 py-0.5 text-xs font-medium text-[var(--admin-primary)]">
ADMIN
</span>
</div>
<nav className="flex flex-col gap-1 overflow-y-auto px-3 py-2" style={{ maxHeight: "calc(100vh - 3.5rem)" }}>
{adminNav.map((section) => (
<div key={section.title} className="mb-2">
<div className="px-3 py-1 text-xs font-medium text-white/40">{section.title}</div>
{section.items.map((item) => {
const isActive = pathname === item.href || pathname.startsWith(item.href + "/");
return (
<Link
key={item.href}
href={item.href}
className={`flex items-center gap-2 rounded-md px-3 py-2 text-sm transition-colors ${
isActive
? "bg-[var(--admin-primary)]/20 text-[var(--admin-primary)]"
: "text-white/70 hover:bg-white/10 hover:text-white"
}`}
>
<item.icon size={16} aria-hidden="true" />
{item.label}
</Link>
);
})}
</div>
))}
</nav>
</aside>
{/* 内容区 */}
<main className="flex-1">
{/* 移动端顶部 Header */}
<header className="sticky top-0 z-10 flex h-14 items-center gap-2 bg-[var(--admin-header-bg)] px-4 md:hidden">
<span className="font-bold text-[var(--admin-primary)]">AIPortPilot</span>
<span className="inline-flex items-center rounded bg-[var(--admin-primary)]/20 px-1.5 py-0.5 text-xs font-medium text-[var(--admin-primary)]">
ADMIN
</span>
</header>
<div className="container mx-auto max-w-7xl px-4 py-6">{children}</div>
</main>
</div>
);
}