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,85 @@
|
||||
/** Multi-Workspace Tab 管理器 — 多 Tab + 独立上下文 + 最多 8 个。 */
|
||||
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { X, Plus } from "lucide-react";
|
||||
|
||||
/** 工作区 Tab 定义。 */
|
||||
export interface WorkspaceTab {
|
||||
id: string;
|
||||
title: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
/** Workspace Tabs Props。 */
|
||||
interface WorkspaceTabsProps {
|
||||
tabs: WorkspaceTab[];
|
||||
activeId: string;
|
||||
onSwitch: (id: string) => void;
|
||||
onClose: (id: string) => void;
|
||||
onAdd: () => void;
|
||||
}
|
||||
|
||||
/** 最大 Tab 数量。 */
|
||||
const MAX_TABS = 8;
|
||||
|
||||
/**
|
||||
* Multi-Workspace Tab 管理器组件。
|
||||
*
|
||||
* 支持多 Tab 独立上下文,最多 8 个 Tab,超出时淘汰最久未访问的 Tab。
|
||||
*/
|
||||
export function WorkspaceTabs({ tabs, activeId, onSwitch, onClose, onAdd }: WorkspaceTabsProps) {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center gap-1 border-b bg-white px-2"
|
||||
role="tablist"
|
||||
aria-label="工作区标签"
|
||||
>
|
||||
{tabs.map((tab) => (
|
||||
<div
|
||||
key={tab.id}
|
||||
className={`group flex items-center gap-1.5 rounded-t-md border-b-2 px-3 py-2 text-sm transition-colors ${
|
||||
tab.id === activeId
|
||||
? "border-indigo-500 text-indigo-600"
|
||||
: "border-transparent text-gray-600 hover:text-gray-900"
|
||||
}`}
|
||||
role="tab"
|
||||
aria-selected={tab.id === activeId}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onSwitch(tab.id)}
|
||||
className="truncate"
|
||||
>
|
||||
{tab.title}
|
||||
</button>
|
||||
{tabs.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onClose(tab.id);
|
||||
}}
|
||||
className="text-gray-300 transition-colors hover:text-red-500"
|
||||
aria-label={`关闭 ${tab.title}`}
|
||||
>
|
||||
<X size={14} aria-hidden="true" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{tabs.length < MAX_TABS && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onAdd}
|
||||
className="flex items-center gap-1 rounded-md px-2 py-1.5 text-sm text-gray-400 transition-colors hover:bg-gray-50 hover:text-gray-600"
|
||||
aria-label="新建工作区"
|
||||
>
|
||||
<Plus size={14} aria-hidden="true" />
|
||||
新建
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user