feat(frontend): P0 导航架构重构 — 6 业务域 + 角色化排序 + 新路由
- 投资人 Sidebar: 5 扁平分组 → 6 业务域(今日/组合/工作/增长/洞察/报告) - 角色化导航: GP→组合优先, 投后负责人→今日优先, 投资经理→今日+组合 - 新增路由: /today /compare /threads /threads/[id] /workspace - Admin: 单页标签 → 6 管理域 Sidebar(租户/用户/审计/数据源/安全/设置) - 创始人: 3 底部导航 → 6 域(今日/经营/月报/Copilot/通知/我的) - 新增 navConfig.ts 统一管理导航配置
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/** 多企业对比工作区 — 2-5 家企业并排对比。 */
|
||||
|
||||
import { GitCompareArrows } from "lucide-react";
|
||||
|
||||
/** 多企业对比页面。 */
|
||||
export default function ComparePage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<GitCompareArrows className="text-[var(--investor-primary)]" size={24} />
|
||||
<h1 className="text-2xl font-bold">多企业对比</h1>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
选择 2-5 家企业进行并排对比,支持指标选择、差异高亮和趋势对比。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 对比卡片占位 */}
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||
{["智链科技", "云栈数据", "深瞳智能"].map((name) => (
|
||||
<div key={name} className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<h3 className="mb-3 font-medium">{name}</h3>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">健康度</span>
|
||||
<span className="font-medium">78.5</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">月营收</span>
|
||||
<span className="font-medium">800万</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">Runway</span>
|
||||
<span className="font-medium">12月</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">高风险</span>
|
||||
<span className="font-medium">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,90 +1,76 @@
|
||||
/** 投资人端布局 — B 端专业风格:左 Sidebar + 灰色内容区 + AI Copilot。 */
|
||||
|
||||
import {
|
||||
LayoutDashboard, Building2, FileText, AlertTriangle, Settings,
|
||||
Receipt, ScrollText, Users, Lightbulb, Target, GitBranch,
|
||||
TrendingUp, Network, ShieldCheck, Bot, BookOpen, BarChart3,
|
||||
} from "lucide-react";
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useMemo } from "react";
|
||||
import { CopilotWidget } from "@/components/shared/CopilotWidget";
|
||||
import { RiskToastNotifier } from "@/components/shared/RiskToastNotifier";
|
||||
import { AuthGuard } from "@/components/shared/AuthGuard";
|
||||
import { useAuth } from "@/lib/auth-context";
|
||||
import { getNavDomains, NAV_FOOTER } from "@/lib/navConfig";
|
||||
|
||||
const navSections = [
|
||||
{
|
||||
title: "核心功能",
|
||||
items: [
|
||||
{ href: "/dashboard", label: "驾驶舱", icon: LayoutDashboard },
|
||||
{ href: "/companies", label: "企业档案", icon: Building2 },
|
||||
{ href: "/reports", label: "月报管理", icon: FileText },
|
||||
{ href: "/risks", label: "风险工作台", icon: AlertTriangle },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "投后管理",
|
||||
items: [
|
||||
{ href: "/financial", label: "财务校验", icon: Receipt },
|
||||
{ href: "/agreements", label: "协议监控", icon: ScrollText },
|
||||
{ href: "/board", label: "董事会", icon: Users },
|
||||
{ href: "/weak-signals", label: "弱信号", icon: Lightbulb },
|
||||
{ href: "/sentinels", label: "决策前哨", icon: Target },
|
||||
{ href: "/events", label: "重大事项", icon: AlertTriangle },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "增值服务",
|
||||
items: [
|
||||
{ href: "/synergies", label: "协同中心", icon: Network },
|
||||
{ href: "/innovation", label: "组合创新", icon: Lightbulb },
|
||||
{ href: "/talents", label: "人才引力场", icon: Users },
|
||||
{ href: "/okrs", label: "OKR", icon: Target },
|
||||
{ href: "/milestones", label: "里程碑", icon: GitBranch },
|
||||
{ href: "/tasks", label: "任务看板", icon: BarChart3 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "高级分析",
|
||||
items: [
|
||||
{ href: "/alpha", label: "Alpha 归因", icon: TrendingUp },
|
||||
{ href: "/exit-signals", label: "退出信号", icon: TrendingUp },
|
||||
{ href: "/portfolio", label: "组合管理", icon: BarChart3 },
|
||||
{ href: "/digital-twins", label: "数字孪生", icon: Bot },
|
||||
{ href: "/knowledge-graph", label: "知识图谱", icon: Network },
|
||||
{ href: "/aars", label: "AAR 复盘", icon: BookOpen },
|
||||
{ href: "/pre-mortems", label: "Pre-mortem", icon: ShieldCheck },
|
||||
{ href: "/agents", label: "Agent 监控", icon: Bot },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "系统",
|
||||
items: [
|
||||
{ href: "/settings", label: "设置", icon: Settings },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 投资人端布局组件。
|
||||
*
|
||||
* 左侧 Sidebar 按 6 业务域分组,域顺序根据用户角色动态排序。
|
||||
*/
|
||||
export default function InvestorLayout({ children }: { children: React.ReactNode }) {
|
||||
const { user } = useAuth();
|
||||
const pathname = usePathname();
|
||||
|
||||
const domains = useMemo(() => getNavDomains(user?.role ?? "investor"), [user?.role]);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen">
|
||||
{/* 左侧 Sidebar */}
|
||||
<aside className="sticky top-0 hidden h-screen w-52 shrink-0 bg-[var(--investor-sidebar-bg)] text-white md:block">
|
||||
<div className="flex h-14 items-center px-4 font-bold">AIPortPilot</div>
|
||||
<nav className="flex flex-col gap-1 overflow-y-auto px-3 py-2">
|
||||
{navSections.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) => (
|
||||
<nav className="flex flex-col gap-1 overflow-y-auto px-3 py-2" style={{ maxHeight: "calc(100vh - 3.5rem)" }}>
|
||||
{domains.map((domain) => (
|
||||
<div key={domain.key} className="mb-2">
|
||||
<div className="px-3 py-1 text-xs font-medium text-white/40">{domain.title}</div>
|
||||
{domain.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-white/15 text-white"
|
||||
: "text-white/70 hover:bg-white/10 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
<item.icon size={16} aria-hidden="true" />
|
||||
{item.label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* 底部固定 */}
|
||||
<div className="mt-auto border-t border-white/10 pt-2">
|
||||
{NAV_FOOTER.map((item) => {
|
||||
const isActive = pathname === item.href;
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className="flex items-center gap-2 rounded-md px-3 py-2 text-sm text-white/70 transition-colors hover:bg-white/10 hover:text-white"
|
||||
className={`flex items-center gap-2 rounded-md px-3 py-2 text-sm transition-colors ${
|
||||
isActive
|
||||
? "bg-white/15 text-white"
|
||||
: "text-white/70 hover:bg-white/10 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
<item.icon size={16} aria-hidden="true" />
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/** 决策线程详情页 — 状态机时间线 + 关联事件 + AAR 链接。 */
|
||||
|
||||
import { GitBranch, ArrowLeft } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
/** 决策线程详情页面。 */
|
||||
export default function ThreadDetailPage({ params }: { params: { id: string } }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Link href="/threads" className="flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground">
|
||||
<ArrowLeft size={16} />
|
||||
返回线程列表
|
||||
</Link>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<GitBranch className="text-[var(--investor-primary)]" size={24} />
|
||||
<h1 className="text-2xl font-bold">决策线程详情</h1>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<h2 className="text-lg font-medium">线程 #{params.id}</h2>
|
||||
<span className="rounded-full bg-indigo-50 px-3 py-1 text-xs text-indigo-600">分析中</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
状态机时间线、关联事件和 AAR 链接将在 P1 #17 组件完成后集成。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 时间线占位 */}
|
||||
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<h3 className="mb-4 font-medium">状态机时间线</h3>
|
||||
<div className="space-y-4">
|
||||
{[
|
||||
{ label: "已识别", date: "2026-07-09", active: true },
|
||||
{ label: "分析中", date: "2026-07-12", active: true },
|
||||
{ label: "已行动", date: "—", active: false },
|
||||
{ label: "已关闭", date: "—", active: false },
|
||||
].map((step, i) => (
|
||||
<div key={i} className="flex items-center gap-3">
|
||||
<div className={`h-3 w-3 rounded-full ${step.active ? "bg-indigo-500" : "bg-gray-300"}`} />
|
||||
<span className={`text-sm ${step.active ? "font-medium" : "text-muted-foreground"}`}>
|
||||
{step.label}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">{step.date}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/** 决策线程列表页 — 按状态/企业筛选 + 线程卡片。 */
|
||||
|
||||
import { GitBranch } from "lucide-react";
|
||||
|
||||
/** 决策线程列表页面。 */
|
||||
export default function ThreadsPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<GitBranch className="text-[var(--investor-primary)]" size={24} />
|
||||
<h1 className="text-2xl font-bold">决策线程</h1>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
决策线程将风险、建议、任务串联为可追踪的决策链路,支持状态机驱动的时间线视图。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 线程卡片占位 */}
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{ title: "云栈数据 A+轮融资时机选择", status: "分析中", company: "云栈数据" },
|
||||
{ title: "深瞳智能 CTO retention 策略", status: "已识别", company: "深瞳智能" },
|
||||
{ title: "量子芯微 融资策略:天使+ vs Pre-A", status: "已行动", company: "量子芯微" },
|
||||
].map((thread) => (
|
||||
<div key={thread.title} className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="font-medium">{thread.title}</h3>
|
||||
<p className="mt-1 text-sm text-muted-foreground">{thread.company}</p>
|
||||
</div>
|
||||
<span className="rounded-full bg-indigo-50 px-3 py-1 text-xs text-indigo-600">
|
||||
{thread.status}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/** 今日行动中心 — 投后负责人/投资经理默认首页。 */
|
||||
|
||||
import { CalendarClock, AlertTriangle, FileText, ListTodo, TrendingUp } from "lucide-react";
|
||||
|
||||
/** 今日行动中心页面。 */
|
||||
export default function TodayPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<CalendarClock className="text-[var(--investor-primary)]" size={24} />
|
||||
<h1 className="text-2xl font-bold">今日行动中心</h1>
|
||||
</div>
|
||||
|
||||
{/* AI 简报 */}
|
||||
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<h2 className="mb-2 text-sm font-medium text-muted-foreground">AI 简报</h2>
|
||||
<p className="text-sm text-gray-700">
|
||||
今日有 3 条待处理风险、2 份月报待审阅、5 个任务待完成。量子芯微现金 Runway 不足 7 月,需紧急关注。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 四象限概览 */}
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<AlertTriangle className="text-amber-500" size={18} />
|
||||
<h3 className="font-medium">风险队列</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between rounded bg-red-50 px-3 py-2 text-sm">
|
||||
<span>量子芯微 — 现金Runway不足7月</span>
|
||||
<span className="text-red-600">紧急</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded bg-amber-50 px-3 py-2 text-sm">
|
||||
<span>云栈数据 — 营收增长停滞</span>
|
||||
<span className="text-amber-600">高</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded bg-blue-50 px-3 py-2 text-sm">
|
||||
<span>智链科技 — 客户流失率上升</span>
|
||||
<span className="text-blue-600">中</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<FileText className="text-blue-500" size={18} />
|
||||
<h3 className="font-medium">月报状态</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between rounded bg-green-50 px-3 py-2 text-sm">
|
||||
<span>智链科技 — 已提交</span>
|
||||
<span className="text-green-600">AI 解析完成</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded bg-green-50 px-3 py-2 text-sm">
|
||||
<span>深瞳智能 — 已审阅</span>
|
||||
<span className="text-green-600">完成</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded bg-amber-50 px-3 py-2 text-sm">
|
||||
<span>量子芯微 — 草稿中</span>
|
||||
<span className="text-amber-600">待提交</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<ListTodo className="text-indigo-500" size={18} />
|
||||
<h3 className="font-medium">任务概览</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between rounded bg-indigo-50 px-3 py-2 text-sm">
|
||||
<span>联系流失客户进行回访</span>
|
||||
<span className="text-indigo-600">进行中</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded bg-gray-50 px-3 py-2 text-sm">
|
||||
<span>准备天使+轮 BP</span>
|
||||
<span className="text-gray-600">待办</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded bg-green-50 px-3 py-2 text-sm">
|
||||
<span>审阅智链科技月报</span>
|
||||
<span className="text-green-600">已完成</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<TrendingUp className="text-emerald-500" size={18} />
|
||||
<h3 className="font-medium">健康度变化</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between rounded bg-green-50 px-3 py-2 text-sm">
|
||||
<span>智链科技 78.5 ↑</span>
|
||||
<span className="text-green-600">+2.3</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded bg-red-50 px-3 py-2 text-sm">
|
||||
<span>量子芯微 48.0 ↓</span>
|
||||
<span className="text-red-600">-5.0</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded bg-gray-50 px-3 py-2 text-sm">
|
||||
<span>云栈数据 62.0 →</span>
|
||||
<span className="text-gray-600">持平</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/** 通用工作区 — URL 参数驱动,支持多 Tab 独立上下文。 */
|
||||
|
||||
import { Layers } from "lucide-react";
|
||||
|
||||
/** 通用工作区页面。 */
|
||||
export default function WorkspacePage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Layers className="text-[var(--investor-primary)]" size={24} />
|
||||
<h1 className="text-2xl font-bold">工作区</h1>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
通用工作区支持多 Tab 独立上下文,最多 8 个 Tab,由 URL 参数驱动。
|
||||
将在 P1 #18 Multi-Workspace Tab 管理器完成后集成。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +1,109 @@
|
||||
/** Admin 端布局 — 警示风格:slate-950 Header + amber 主色。 */
|
||||
/** 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="min-h-screen bg-slate-100">
|
||||
<header className="sticky top-0 z-10 flex h-14 items-center bg-[var(--admin-header-bg)] px-4">
|
||||
<span className="font-bold text-[var(--admin-primary)]">AIPortPilot</span>
|
||||
<span className="ml-2 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>
|
||||
<main className="container mx-auto max-w-7xl px-4 py-6">{children}</main>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
/** 创始人端布局 — C 端温暖风格:顶部 Header + indigo 主色。 */
|
||||
/** 创始人端布局 — C 端温暖风格:顶部 Header + indigo 主色 + 6 域底部导航。 */
|
||||
|
||||
import { Home, FileText, Sparkle } from "lucide-react";
|
||||
import { CalendarClock, Gauge, FileText, Sparkle, Bell, UserCircle } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
/** 创始人端 6 域导航。 */
|
||||
const navItems = [
|
||||
{ href: "/founder", label: "概览", icon: Home },
|
||||
{ href: "/founder", label: "今日", icon: CalendarClock },
|
||||
{ href: "/founder/operations", label: "经营", icon: Gauge },
|
||||
{ href: "/founder/reports", label: "月报", icon: FileText },
|
||||
{ href: "/founder/copilot", label: "AI 副驾驶", icon: Sparkle },
|
||||
{ href: "/founder/copilot", label: "Copilot", icon: Sparkle },
|
||||
{ href: "/founder/notifications", label: "通知", icon: Bell },
|
||||
{ href: "/founder/profile", label: "我的", icon: UserCircle },
|
||||
];
|
||||
|
||||
/**
|
||||
* 创始人端布局组件。
|
||||
*
|
||||
* 顶部 sticky Header + 底部 6 域导航栏,移动优先设计。
|
||||
*/
|
||||
export default function FounderLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-indigo-50 to-white">
|
||||
@@ -21,8 +30,8 @@ export default function FounderLayout({ children }: { children: React.ReactNode
|
||||
{/* 内容区 */}
|
||||
<main className="container mx-auto max-w-7xl px-4 py-6 pb-20 md:pb-6">{children}</main>
|
||||
|
||||
{/* 移动端底部导航 */}
|
||||
<nav className="fixed bottom-0 left-0 right-0 flex h-14 border-t bg-white md:hidden">
|
||||
{/* 底部 6 域导航 */}
|
||||
<nav className="fixed bottom-0 left-0 right-0 flex h-14 border-t bg-white">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/** 投资人端导航配置 — 6 业务域分组 + 角色化排序。 */
|
||||
|
||||
import {
|
||||
CalendarClock, LayoutDashboard, Building2, Scale, GitCompareArrows,
|
||||
AlertTriangle, FileText, Receipt, ScrollText, Users, Flag, ListTodo,
|
||||
Network, UserCircle, TrendingUp, Target, GitBranch, Lightbulb,
|
||||
Radar, Crosshair, TrendingUp as TrendUp, LogOut, Bot, Cpu, Network as Net,
|
||||
BookOpen, ShieldCheck, FileBarChart, Settings,
|
||||
type LucideIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
/** 导航项定义。 */
|
||||
export interface NavItem {
|
||||
href: string;
|
||||
label: string;
|
||||
icon: LucideIcon;
|
||||
}
|
||||
|
||||
/** 业务域定义。 */
|
||||
export interface NavDomain {
|
||||
key: string;
|
||||
title: string;
|
||||
items: NavItem[];
|
||||
}
|
||||
|
||||
/** 角色默认首页。 */
|
||||
export const ROLE_HOME: Record<string, string> = {
|
||||
gp: "/dashboard",
|
||||
partner: "/dashboard",
|
||||
post_invest_lead: "/today",
|
||||
investor: "/today",
|
||||
};
|
||||
|
||||
/** 6 业务域完整定义(顺序为通用默认顺序)。 */
|
||||
export const NAV_DOMAINS: Record<string, NavDomain> = {
|
||||
today: {
|
||||
key: "today",
|
||||
title: "今日",
|
||||
items: [
|
||||
{ href: "/today", label: "今日行动中心", icon: CalendarClock },
|
||||
],
|
||||
},
|
||||
portfolio: {
|
||||
key: "portfolio",
|
||||
title: "组合",
|
||||
items: [
|
||||
{ href: "/dashboard", label: "指挥塔", icon: LayoutDashboard },
|
||||
{ href: "/companies", label: "企业列表", icon: Building2 },
|
||||
{ href: "/portfolio", label: "组合再平衡", icon: Scale },
|
||||
{ href: "/compare", label: "多企业对比", icon: GitCompareArrows },
|
||||
],
|
||||
},
|
||||
work: {
|
||||
key: "work",
|
||||
title: "工作",
|
||||
items: [
|
||||
{ href: "/risks", label: "风险队列", icon: AlertTriangle },
|
||||
{ href: "/reports", label: "月报审阅", icon: FileText },
|
||||
{ href: "/financial", label: "财务校验", icon: Receipt },
|
||||
{ href: "/agreements", label: "协议监控", icon: ScrollText },
|
||||
{ href: "/board", label: "董事会", icon: Users },
|
||||
{ href: "/events", label: "重大事项", icon: Flag },
|
||||
{ href: "/tasks", label: "任务看板", icon: ListTodo },
|
||||
],
|
||||
},
|
||||
growth: {
|
||||
key: "growth",
|
||||
title: "增长",
|
||||
items: [
|
||||
{ href: "/synergies", label: "协同机会", icon: Network },
|
||||
{ href: "/talents", label: "人才", icon: UserCircle },
|
||||
{ href: "/customer-growth", label: "客户增长", icon: TrendingUp },
|
||||
{ href: "/okrs", label: "OKR", icon: Target },
|
||||
{ href: "/milestones", label: "里程碑", icon: GitBranch },
|
||||
{ href: "/innovation", label: "组合创新", icon: Lightbulb },
|
||||
{ href: "/peer-circles", label: "Peer Learning", icon: Users },
|
||||
{ href: "/product-diagnostics", label: "产品诊断", icon: Cpu },
|
||||
],
|
||||
},
|
||||
insight: {
|
||||
key: "insight",
|
||||
title: "洞察",
|
||||
items: [
|
||||
{ href: "/weak-signals", label: "弱信号", icon: Radar },
|
||||
{ href: "/sentinels", label: "决策前哨", icon: Crosshair },
|
||||
{ href: "/threads", label: "决策线程", icon: GitBranch },
|
||||
{ href: "/alpha", label: "Alpha 归因", icon: TrendUp },
|
||||
{ href: "/exit-signals", label: "退出信号", icon: LogOut },
|
||||
{ href: "/digital-twins", label: "数字孪生", icon: Bot },
|
||||
{ href: "/knowledge-graph", label: "知识图谱", icon: Net },
|
||||
{ href: "/agents", label: "Agent 监控", icon: Bot },
|
||||
{ href: "/nudges", label: "行为助推", icon: Lightbulb },
|
||||
],
|
||||
},
|
||||
report: {
|
||||
key: "report",
|
||||
title: "报告",
|
||||
items: [
|
||||
{ href: "/aars", label: "AAR 复盘", icon: BookOpen },
|
||||
{ href: "/pre-mortems", label: "Pre-mortem", icon: ShieldCheck },
|
||||
{ href: "/reports/templates", label: "投后报告", icon: FileBarChart },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
/** 底部固定项。 */
|
||||
export const NAV_FOOTER: NavItem[] = [
|
||||
{ href: "/settings", label: "设置", icon: Settings },
|
||||
];
|
||||
|
||||
/** 角色化域排序。 */
|
||||
export const ROLE_DOMAIN_ORDER: Record<string, string[]> = {
|
||||
gp: ["portfolio", "insight", "report", "growth", "work", "today"],
|
||||
partner: ["portfolio", "insight", "report", "growth", "work", "today"],
|
||||
post_invest_lead: ["today", "work", "portfolio", "growth", "insight", "report"],
|
||||
investor: ["today", "portfolio", "work", "growth", "report", "insight"],
|
||||
};
|
||||
|
||||
/** 默认域排序。 */
|
||||
const DEFAULT_ORDER = ["today", "portfolio", "work", "growth", "insight", "report"];
|
||||
|
||||
/**
|
||||
* 根据角色获取排序后的导航域列表。
|
||||
* @param role 用户角色
|
||||
* @returns 排序后的 NavDomain 数组
|
||||
*/
|
||||
export function getNavDomains(role: string): NavDomain[] {
|
||||
const order = ROLE_DOMAIN_ORDER[role] ?? DEFAULT_ORDER;
|
||||
return order.map((key) => NAV_DOMAINS[key]).filter(Boolean);
|
||||
}
|
||||
Reference in New Issue
Block a user