| 12345678910111213141516171819202122232425262728293031323334353637383940 |
- import { ArrowRight, CheckCircle2, FileCheck2 } from "lucide-react";
- import { Handle, Position, type NodeProps } from "@xyflow/react";
- import type { CanvasNodeData } from "@/lib/layout";
- import { runResultFallback } from "@/lib/status-copy";
- import { SourceNoticeBadge, StatusBadge } from "@/components/ui/Badges";
- import { SourceComparisonButton } from "@/components/flow/SourceComparisonButton";
- export function RunResultCard({ data }: NodeProps) {
- const { item, onOpen } = data as unknown as CanvasNodeData;
- if (!item || item.itemType !== "final-result") return null;
- const result = item.result;
- const artifact = result.artifact;
- const summary = result.summary || result.errorMessage || runResultFallback(result.status, result.terminal);
- const summaryPreview = resultSummaryPreview(summary);
- const adoptedCount = result.branchCounts.merged ?? 0;
- return <article className="flowCard runResultCard" data-focus-return={`${item.id}:card`} aria-label="最终结果" tabIndex={0} onKeyDown={(event) => { if (event.target === event.currentTarget && (event.key === "Enter" || event.key === " ")) { event.preventDefault(); onOpen(item, "business"); } }}>
- <Handle type="target" position={Position.Left} className="flowHandle" />
- <header className="cardHeader"><span className="finalLabel"><FileCheck2 size={14} />最终结果</span><StatusBadge status={result.status} /></header>
- {result.title !== "最终结果" ? <h3>{result.title}</h3> : null}
- <div className="finalOutcome"><span><CheckCircle2 size={18} /></span><div><b>{result.terminal ? "当前主脚本已经形成" : "脚本构建仍在推进"}</b><p>{result.roundCount} 轮构建 · {result.branchCounts.total} 个候选方案</p></div></div>
- {summaryPreview ? <section className="finalSummaryPreview"><b>构建摘要</b><p>{summaryPreview}</p></section> : null}
- <div className="artifactStats"><span><b>{artifact.paragraphCount ?? 0}</b>段落</span><span><b>{artifact.elementCount ?? 0}</b>元素</span><span><b>{artifact.linkCount ?? 0}</b>关联</span></div>
- <div className="finalRunFacts"><span><b>{adoptedCount}</b> 个方案已采用</span><span><b>{result.branchCounts.discarded ?? 0}</b> 个未采用</span></div>
- <footer className="cardFooter"><button type="button" className="nodeAction nodrag nopan" data-focus-return={`${item.id}:business`} onClick={(event) => { event.stopPropagation(); onOpen(item, "business"); }}>查看结果详情</button><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"); }}>查看完整主脚本<ArrowRight size={14} /></button></footer>
- <SourceNoticeBadge notice={result.sourceNotice} />
- </article>;
- }
- function resultSummaryPreview(value: string) {
- const lines = value
- .replace(/\\n/g, "\n")
- .split(/\n+/)
- .map((line) => line.replace(/^\s*(?:#{1,6}\s*|[-*+]\s+|\d+[.、)]\s*)/, "").replace(/[`*_]/g, "").trim())
- .filter(Boolean);
- const text = lines.find((line) => line.length >= 18) || lines[0] || "";
- if (text.length <= 150) return text;
- const candidate = text.slice(0, 151);
- const punctuation = Math.max(...["。", "!", "?", ";"].map((mark) => candidate.lastIndexOf(mark)));
- return punctuation >= 72 ? candidate.slice(0, punctuation + 1) : `${text.slice(0, 149).trim()}…`;
- }
|