From ff90cac755a33e433b561168be39be9b22a9cd55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E5=AE=8F=E5=87=AF?= Date: Wed, 27 May 2026 15:30:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Step2Panel.tsx | 31 +- .../pages/RoundtablePlanningPage.tsx | 544 +++++++++++++----- 2 files changed, 432 insertions(+), 143 deletions(-) diff --git a/frontend-web/src/roundtable-planning/components/Step2Panel.tsx b/frontend-web/src/roundtable-planning/components/Step2Panel.tsx index 4fbbb315..b872c93c 100644 --- a/frontend-web/src/roundtable-planning/components/Step2Panel.tsx +++ b/frontend-web/src/roundtable-planning/components/Step2Panel.tsx @@ -2,7 +2,7 @@ * Step 2 主面板 —— 多智能体圆桌研讨。 * * 包含 4 块: - * 1) 顶部 Headline + 挂起/恢复按钮 + * 1) 顶部 Headline + 推理模型下拉 + 挂起/恢复按钮 * 2) Persona 抽屉(点击席位头像后展开,见 `PersonaDrawer.tsx`) * 3) 对话流(初始化占位 + 气泡列表 + ChainOfThought 折叠卡 + markdown) * 4) 底部澄清条 + 用户干预输入 + 「达成共识」入口 @@ -68,6 +68,7 @@ export interface Step2PanelProps { selectedModel: string; availableModels: ModelOption[]; modelsLoading: boolean; + setSelectedModel: React.Dispatch>; // ─── Setters ────────────────────────────────────────────────── setIsRoundTableRunning: React.Dispatch>; setClarificationDraft: React.Dispatch>; @@ -100,6 +101,7 @@ export function Step2Panel(props: Step2PanelProps) { selectedModel, availableModels, modelsLoading, + setSelectedModel, setIsRoundTableRunning, setClarificationDraft, setCustomAgentInput, @@ -129,7 +131,32 @@ export function Step2Panel(props: Step2PanelProps) {

-
+
+ {isRoundTableRunning && isOrchestrating ? ( + )} + {currentStep === 3 ? ( <> {/* Step 3 时只保留「参与与结论贡献」面板;原文件里其它两个 hidden 块未启用, @@ -849,41 +875,67 @@ function LeftSidebar({
- ) : ( - <> - {/* 任务概览 */} -
-
- -

任务概览

-
-
-
- - 当前节点 - -
- 初始任务 - - {currentStep === 1 ? "待澄清" : currentStep === 2 ? "研讨中" : "已规划交付"} - -
-

- 围绕某复杂任务制定规划方案 -

+ ) : currentStep === 2 ? ( + sidebarExpanded ? ( + <> + setSidebarExpanded((v) => !v), + }} + /> + +
+
+ +

参与角色

+
+
+ {roles.map((role) => ( + + ))}
-
- {/* 参与角色 */} + + + + ) : ( + <> + + + + + +
+ {roles.map((role) => ( + + ))} +
+
+ + + + + + + + + + ) + ) : ( + <> + +
@@ -896,32 +948,317 @@ function LeftSidebar({
- {/* 人工参与 */} -
-
- -

人工参与

-
-
-
- - 用户 -
-
- - - - - 在线 -
-
-
+ )} ); } +function TaskOverviewCard({ currentStep }: { currentStep: number }) { + return ( +
+
+ +

任务概览

+
+
+
+ + 当前节点 + +
+ 初始任务 + + {currentStep === 1 ? "待澄清" : currentStep === 2 ? "研讨中" : "已规划交付"} + +
+

+ 围绕某复杂任务制定规划方案 +

+
+
+
+ ); +} + +function HumanParticipationCard() { + return ( +
+
+ +

人工参与

+
+
+
+ + 用户 +
+
+ + + + + 在线 +
+
+
+ ); +} + +interface ModelSelectorCardProps { + selectedModel: string; + setSelectedModel: React.Dispatch>; + availableModels: ReadonlyArray<{ name: string; display_name?: string }>; + modelsLoading: boolean; +} + +function ModelSelectorCard({ + selectedModel, + setSelectedModel, + availableModels, + modelsLoading, +}: ModelSelectorCardProps) { + return ( +
+
+ +

推理模型

+
+ +
+ ); +} + +interface IntentSummaryCardProps { + intentReady: ReturnType["intentReady"]; + title?: string; + /** Step 2 侧栏: 收起时仅展示目标,展开后显示约束与假设。 */ + collapsible?: boolean; + /** 侧栏整体收起按钮,放在标题栏右侧。 */ + sidebarToggle?: { + expanded: boolean; + onToggle: () => void; + }; +} + +function IntentSummaryCard({ + intentReady, + title = "当前意图摘要", + collapsible = false, + sidebarToggle, +}: IntentSummaryCardProps) { + const [expanded, setExpanded] = useState(!collapsible); + const showFullDetail = !collapsible || expanded; + + return ( +
+
+
+ +

{title}

+
+ {sidebarToggle && ( + + )} +
+ +
+ {intentReady ? ( + <> +
    +
  • + • + + 目标: {intentReady.objective} + +
  • +
+ {collapsible && ( + + )} + {showFullDetail && ( +
    + {intentReady.constraints.map((c, i) => ( +
  • + • + + 约束: {c} + +
  • + ))} + {(intentReady.assumptions ?? []).map((a, i) => ( +
  • + • + + 假设: {a} + +
  • + ))} +
+ )} + + ) : ( +

+ 等待任务理解智能体澄清完成... +

+ )} +
+
+ ); +} + +function IntentSectionIcon({ className }: { className?: string }) { + return ( + + + + + + ); +} + +function CollapsedSidebarSection({ + title, + children, + className, +}: { + title: string; + children: React.ReactNode; + className?: string; +}) { + return ( +
+ {children} +
+ ); +} + +function renderRoleAvatar( + role: RoleEntry, + agentNameToRole: Record< + string, + { speakerId: number; avatarType: string; displayName: string } + >, + size: string, +) { + if (role.type === "human") { + return ; + } + return getAvatar( + role.agentId ? agentNameToRole[role.agentId]?.avatarType ?? "blue" : "blue", + size, + ); +} + +function RoleAvatarBadge({ + role, + agentNameToRole, + size = "h-7 w-7", + showSpeakingAnimation = false, +}: { + role: RoleEntry; + agentNameToRole: Record< + string, + { speakerId: number; avatarType: string; displayName: string } + >; + size?: string; + showSpeakingAnimation?: boolean; +}) { + const isOngoing = role.status === "研讨中"; + const animate = showSpeakingAnimation && isOngoing; + + return ( +
+ {animate && ( + + )} +
+ {renderRoleAvatar(role, agentNameToRole, size)} +
+
+ ); +} + function RoleRow({ role, agentNameToRole, @@ -949,14 +1286,7 @@ function RoleRow({ }`} >
- {role.type === "human" ? ( - - ) : ( - getAvatar( - role.agentId ? agentNameToRole[role.agentId]?.avatarType ?? "blue" : "blue", - "h-7 w-7", - ) - )} +
{role.name} {role.type === "agent" && ( @@ -1024,89 +1354,21 @@ function RightSidebar({ timelineEntries, triggerToast, }: RightSidebarProps) { - if (currentStep === 3) return null; + // Step 2 侧栏内容已迁至左侧;Step 3 沿用原逻辑不展示右侧栏。 + if (currentStep !== 1) return null; return (