feat(frontend): P1 核心 UI 组件 — Context Bar + Work Mode + Insight Rail + 工作区 + 决策线程
- ContextBar: Scope/Lens/Time 三维切换器 + Action Filter - ContextBarProvider: 全局 Context + URL 参数双向同步 - WorkModeSwitcher: Overview/Compare/Focus/Queue 四种模式 - FocusMode: 全屏沉浸分析 + TOC/BML 入口 - QueueMode: 左右分栏 Split View - CompareMode: 2-5 企业并排对比 + 差异高亮 - InsightRail: 右侧可折叠 AI 面板 + 12 Agent 差异化卡片 - WorkspaceTabs: 多 Tab 管理(最多 8 个) - HighlightsPanel: 企业战情室顶部 KPI 摘要 - ThreadList + ThreadDetail: 决策线程列表/详情 + 状态机时间线
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
/** Insight Rail 组件 — 右侧 AI 洞察面板,可展开/折叠。 */
|
||||
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import {
|
||||
ChevronRight, ChevronLeft, Bot, Sparkles, FileText, AlertTriangle,
|
||||
Users, ScrollText, Network, UserCircle, TrendingUp, Cpu, ShieldCheck,
|
||||
type LucideIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
/** Agent 类型定义。 */
|
||||
interface AgentDef {
|
||||
key: string;
|
||||
name: string;
|
||||
icon: LucideIcon;
|
||||
trigger: string;
|
||||
output: string;
|
||||
action: string;
|
||||
}
|
||||
|
||||
/** 12 个 Agent 配置。 */
|
||||
const AGENTS: AgentDef[] = [
|
||||
{ key: "report", name: "报表分析", icon: FileText, trigger: "查看月报/季报", output: "指标提取卡片 + 异常高亮", action: "生成投后摘要草稿" },
|
||||
{ key: "validator", name: "数据校验", icon: ShieldCheck, trigger: "指标出现矛盾", output: "校验维度矩阵 + 可信度评分", action: "标记可疑数据" },
|
||||
{ key: "risk", name: "风险预警", icon: AlertTriangle, trigger: "健康度下降/弱信号", output: "风险等级 + 证据链", action: "创建决策线程" },
|
||||
{ key: "board", name: "董事会", icon: Users, trigger: "董事会前后", output: "决议追踪 + 提问清单", action: "加入董事会议题" },
|
||||
{ key: "agreement", name: "投资协议", icon: ScrollText, trigger: "协议条款即将触发", output: "条款原文 + 触发条件", action: "发送披露提醒" },
|
||||
{ key: "synergy", name: "协同匹配", icon: Network, trigger: "查看协同中心", output: "匹配度评分 + 资源互补图", action: "生成协同方案草稿" },
|
||||
{ key: "talent", name: "人才分析", icon: UserCircle, trigger: "核心团队变动", output: "稳定性评分 + 9-Box 矩阵", action: "启动人才搜索" },
|
||||
{ key: "financing", name: "融资支持", icon: TrendingUp, trigger: "融资阶段/Runway", output: "融资准备度 + 投资人画像", action: "生成融资材料清单" },
|
||||
{ key: "research", name: "行业研究", icon: Bot, trigger: "竞品/行业动态", output: "行业风险提示 + 对标分析", action: "加入洞察域报告" },
|
||||
{ key: "ai_commercial", name: "AI+商业化", icon: Sparkles, trigger: "PoC/转化率指标", output: "转化漏斗 + 证据链", action: "生成董事会问题清单" },
|
||||
{ key: "ai_cost", name: "AI+模型成本", icon: Cpu, trigger: "推理成本/毛利指标", output: "成本趋势 + 单位经济模型", action: "建议模型调用优化" },
|
||||
{ key: "ai_compliance", name: "AI+数据合规", icon: ShieldCheck, trigger: "合规指标变化", output: "合规风险等级 + 整改建议", action: "生成合规报告草稿" },
|
||||
];
|
||||
|
||||
/** Insight Rail Props。 */
|
||||
interface InsightRailProps {
|
||||
/** 当前激活的 Agent key,默认 "risk"。 */
|
||||
defaultAgent?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insight Rail 组件 — 桌面端右侧可折叠 AI 洞察面板。
|
||||
*
|
||||
* 展开宽度 w-80(320px),折叠宽度 w-10(40px)。
|
||||
* 包含 12 个 Agent 差异化卡片,支持 Tab 切换。
|
||||
*/
|
||||
export function InsightRail({ defaultAgent = "risk" }: InsightRailProps) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [activeAgent, setActiveAgent] = useState(defaultAgent);
|
||||
const [activeTabs, setActiveTabs] = useState<string[]>([defaultAgent]);
|
||||
|
||||
const agent = AGENTS.find((a) => a.key === activeAgent) ?? AGENTS[0];
|
||||
|
||||
/** 切换 Agent(最多 3 个 Tab)。 */
|
||||
function switchAgent(key: string) {
|
||||
setActiveAgent(key);
|
||||
setActiveTabs((prev) => {
|
||||
if (prev.includes(key)) return prev;
|
||||
if (prev.length >= 3) return [...prev.slice(1), key];
|
||||
return [...prev, key];
|
||||
});
|
||||
}
|
||||
|
||||
if (!expanded) {
|
||||
return (
|
||||
<aside
|
||||
className="sticky top-0 hidden h-screen w-10 shrink-0 items-center justify-center border-l bg-white md:flex"
|
||||
aria-expanded={false}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setExpanded(true)}
|
||||
className="flex flex-col items-center gap-1 text-gray-400 transition-colors hover:text-indigo-600"
|
||||
aria-label="展开 Insight Rail"
|
||||
>
|
||||
<ChevronLeft size={18} aria-hidden="true" />
|
||||
<Bot size={16} aria-hidden="true" />
|
||||
<span className="text-xs [writing-mode:vertical-rl]">Insight</span>
|
||||
</button>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<aside
|
||||
className="sticky top-0 hidden h-screen w-80 shrink-0 flex-col border-l bg-white md:flex"
|
||||
aria-expanded={true}
|
||||
>
|
||||
{/* 顶部:折叠按钮 + 当前上下文 */}
|
||||
<div className="flex items-center justify-between border-b px-3 py-2">
|
||||
<span className="text-sm font-medium">AI 洞察</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setExpanded(false)}
|
||||
className="text-gray-400 transition-colors hover:text-gray-600"
|
||||
aria-label="折叠 Insight Rail"
|
||||
>
|
||||
<ChevronRight size={18} aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 当前上下文摘要 */}
|
||||
<div className="border-b px-3 py-2 text-xs text-muted-foreground">
|
||||
<div>企业 A · 治理</div>
|
||||
<div>近 90 天 · 3 个风险</div>
|
||||
</div>
|
||||
|
||||
{/* Agent Tab 切换 */}
|
||||
<div className="flex gap-1 border-b px-2 py-1.5">
|
||||
{activeTabs.map((key) => {
|
||||
const a = AGENTS.find((ag) => ag.key === key)!;
|
||||
return (
|
||||
<button
|
||||
key={key}
|
||||
type="button"
|
||||
onClick={() => setActiveAgent(key)}
|
||||
className={`flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors ${
|
||||
key === activeAgent
|
||||
? "bg-indigo-50 text-indigo-600"
|
||||
: "text-gray-500 hover:bg-gray-50"
|
||||
}`}
|
||||
>
|
||||
<a.icon size={12} aria-hidden="true" />
|
||||
{a.name}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Agent 列表 */}
|
||||
<div className="flex-1 overflow-y-auto px-3 py-2">
|
||||
{/* 当前 Agent 输出 */}
|
||||
<div className="mb-3 rounded-lg border bg-gray-50 p-3">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<agent.icon size={16} className="text-indigo-500" aria-hidden="true" />
|
||||
<span className="text-sm font-medium">{agent.name}</span>
|
||||
</div>
|
||||
<div className="space-y-1.5 text-xs text-gray-600">
|
||||
<p><span className="text-gray-400">触发:</span>{agent.trigger}</p>
|
||||
<p><span className="text-gray-400">输出:</span>{agent.output}</p>
|
||||
<p><span className="text-gray-400">建议:</span>{agent.action}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* AI 输出规范字段 */}
|
||||
<div className="mb-3 space-y-1.5 rounded-lg border p-3 text-xs">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">置信度</span>
|
||||
<span className="font-medium text-indigo-600">0.85</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">证据</span>
|
||||
<span className="text-gray-600">3 条关联信号</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">担忧</span>
|
||||
<span className="text-gray-600">数据延迟 2 天</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">兜底</span>
|
||||
<span className="text-gray-600">未使用</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className="space-y-2">
|
||||
<button
|
||||
type="button"
|
||||
className="w-full rounded-md bg-indigo-50 px-3 py-2 text-sm text-indigo-600 transition-colors hover:bg-indigo-100"
|
||||
>
|
||||
创建任务草稿
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="w-full rounded-md border px-3 py-2 text-sm text-gray-600 transition-colors hover:bg-gray-50"
|
||||
>
|
||||
发起情景模拟
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 全部 Agent 列表 */}
|
||||
<div className="mt-4">
|
||||
<div className="mb-2 text-xs font-medium text-gray-400">全部 Agent</div>
|
||||
<div className="grid grid-cols-2 gap-1">
|
||||
{AGENTS.map((a) => (
|
||||
<button
|
||||
key={a.key}
|
||||
type="button"
|
||||
onClick={() => switchAgent(a.key)}
|
||||
className={`flex items-center gap-1.5 rounded px-2 py-1.5 text-xs transition-colors ${
|
||||
a.key === activeAgent
|
||||
? "bg-indigo-50 text-indigo-600"
|
||||
: "text-gray-600 hover:bg-gray-50"
|
||||
}`}
|
||||
>
|
||||
<a.icon size={12} aria-hidden="true" />
|
||||
{a.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user