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:
selfrelease
2026-07-19 18:51:38 +08:00
parent 947642919b
commit 956270d14d
9 changed files with 575 additions and 82 deletions
@@ -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>
);
}
+53 -67
View File
@@ -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>
);
}
+110
View File
@@ -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>
);
}