| 12345678910111213141516171819202122232425262728293031323334353637383940414243 |
- import { ArrowRight, ChevronDown, FileText } from "lucide-react";
- import { Handle, Position, type NodeProps } from "@xyflow/react";
- import type { CanvasNodeData } from "@/lib/layout";
- import { StatusBadge } from "@/components/ui/Badges";
- import { SourceComparisonButton } from "@/components/flow/SourceComparisonButton";
- export function RoundSummaryCard({ data }: NodeProps) {
- const { item, onOpen, onToggleRound } = data as unknown as CanvasNodeData;
- if (!item || item.itemType !== "round-summary") return null;
- const summary = item.round.summary;
- const approach = `${summary.approach.mode || "方式未记录"} · ${summary.approach.candidateCount} 个实现方案`;
- const decisionMeta = summary.decision.batchCount
- ? `${summary.decision.batchCount} 个决策批次${summary.decision.branchIds.length ? ` · 涉及方案 ${summary.decision.branchIds.join("、")}` : ""}`
- : summary.decision.summary;
- const outputs = [
- summary.output.mergedContentCount ? summary.output.scriptSummary : null,
- summary.output.domainFactCount ? summary.output.domainSummary : null,
- ].filter((value): value is string => Boolean(value));
- if (!outputs.length) outputs.push(summary.output.summary);
- return <article className="flowCard roundSummaryCard" data-focus-return={`${item.id}:card`} aria-label={`第 ${item.roundIndex} 轮摘要`} tabIndex={0} onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); onOpen(item, "business"); } }}>
- <Handle type="target" position={Position.Left} className="flowHandle" /><Handle type="source" position={Position.Right} className="flowHandle" />
- <header className="cardHeader"><span className="roundNumber">第 {item.roundIndex} 轮</span><StatusBadge status={item.round.state} /></header>
- <div className="roundSummaryContent">
- <section className="roundGoalSummary">
- <b>本轮目标</b>
- <p>{summary.goal.headline}</p>
- {summary.goal.itemCount ? <small>{summary.goal.itemCount} 项具体任务 · 完整内容见详情</small> : summary.goal.truncated ? <small>完整内容见详情</small> : null}
- </section>
- <div className="roundSummaryFacts">
- <SummaryBlock label="尝试方案" value={approach} />
- <SummaryBlock label="主 Agent 决策" value={decisionMeta} detail={summary.decision.batchCount ? summary.decision.summary : undefined} tone={summary.decision.batchCount ? "decision" : "warning"} />
- <SummaryBlock label="本轮产出" values={outputs} tone={summary.output.mergedContentCount || summary.output.domainFactCount ? "success" : "normal"} />
- </div>
- </div>
- <footer className="cardFooter"><button type="button" className="nodeAction nodrag nopan" onClick={(event) => { event.stopPropagation(); onToggleRound(item.roundIndex); }}><ChevronDown size={14} />展开本轮</button>{item.detailRef ? <button type="button" className="nodeAction nodrag nopan" data-focus-return={`${item.id}:business`} onClick={(event) => { event.stopPropagation(); onOpen(item, "business"); }}>查看详情<ArrowRight size={14} /></button> : null}<SourceComparisonButton item={item} onOpen={onOpen} /><button type="button" className="nodeAction nodrag nopan artifactAction" data-focus-return={`${item.id}:artifact`} onClick={(event) => { event.stopPropagation(); onOpen(item, "artifact"); }}><FileText size={14} />查看本轮产出脚本表</button></footer>
- </article>;
- }
- function SummaryBlock({ label, value, values, detail, tone = "normal" }: { label: string; value?: string; values?: string[]; detail?: string; tone?: "normal" | "decision" | "success" | "warning" }) {
- const lines = values || (value ? [value] : []);
- return <section className={`roundSummaryBlock summaryTone-${tone}`}><b>{label}</b><div>{lines.map((line) => <p key={line}>{line}</p>)}{detail && detail !== value ? <small>{detail}</small> : null}</div></section>;
- }
|