From 46c3574e050a0a711a22601c181404e916d92ed5 Mon Sep 17 00:00:00 2001 From: HalfAnElephant <12142917@qq.com> Date: Thu, 12 Feb 2026 13:22:40 +0800 Subject: [PATCH 01/21] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E4=B8=8E=E8=BF=9B=E5=BA=A6=E9=9D=A2=E6=9D=BF=E4=BA=A4=E4=BA=92?= =?UTF-8?q?=E4=B8=8E=E4=B8=AD=E6=96=87=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.tsx | 213 +++++++++++++++++++++++++++++++++------- frontend/src/styles.css | 118 ++++++++++++++++++++++ 2 files changed, 294 insertions(+), 37 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 416a215..567e398 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -15,7 +15,7 @@ import { startTask, voteConflict } from "./api"; -import type { ConflictRecord, Evidence, ProgressEvent, TaskResponse } from "./types"; +import type { ConflictRecord, Evidence, ProgressEvent, TaskResponse, TaskStatus } from "./types"; const initialForm = { title: "大语言模型幻觉问题研究", @@ -26,6 +26,97 @@ const initialForm = { priority: 4 }; +type ActionKey = "start" | "pause" | "resume" | "abort" | "refresh"; +type StatusTone = "idle" | "running" | "paused" | "success" | "danger"; + +const STATUS_TEXT: Record = { + READY: "待启动", + PLANNING: "规划中", + EXECUTING: "执行中", + REVIEWING: "复核中", + SYNTHESIZING: "综合中", + FINALIZING: "收尾中", + COMPLETED: "已完成", + FAILED: "失败", + SUSPENDED: "已暂停", + ABORTED: "已终止" +}; + +const STATUS_HINT: Record = { + READY: "任务已创建,点击“开始执行”进入流程。", + PLANNING: "系统正在规划任务结构,可暂停或等待进入执行。", + EXECUTING: "系统正在检索与分析证据,可实时查看进度和事件。", + REVIEWING: "系统正在处理冲突或等待复核,可继续执行或暂停。", + SYNTHESIZING: "系统正在写作报告,建议等待完成。", + FINALIZING: "系统正在收尾,暂不建议操作。", + COMPLETED: "任务已结束,可下滑查看报告结果。", + FAILED: "任务执行失败,建议刷新后查看日志并重新创建任务。", + SUSPENDED: "任务已暂停,点击“继续执行”恢复。", + ABORTED: "任务已终止,如需继续请重新创建任务。" +}; + +const STATUS_TONE: Record = { + READY: "idle", + PLANNING: "running", + EXECUTING: "running", + REVIEWING: "running", + SYNTHESIZING: "running", + FINALIZING: "running", + COMPLETED: "success", + FAILED: "danger", + SUSPENDED: "paused", + ABORTED: "danger" +}; + +const RUNNING_STATUSES = new Set(["PLANNING", "EXECUTING", "REVIEWING", "SYNTHESIZING", "FINALIZING"]); + +const ACTION_TEXT: Record = { + start: "开始执行", + pause: "暂停任务", + resume: "继续执行", + abort: "终止任务", + refresh: "刷新状态" +}; + +const EVENT_TEXT: Record = { + TASK_CREATED: "任务已创建", + TASK_PROGRESS: "进度更新", + TASK_STARTED: "任务开始", + TASK_PAUSED: "任务暂停", + TASK_RESUMED: "任务恢复", + TASK_COMPLETED: "任务完成", + TASK_FAILED: "任务失败", + TASK_ABORTED: "任务终止" +}; + +function toStatusLabel(status: string): string { + return STATUS_TEXT[status as TaskStatus] ?? status; +} + +function getFlowStep(status: TaskStatus | undefined, hasTask: boolean): number { + if (!hasTask || status === "READY" || !status) return 1; + if (status === "COMPLETED" || status === "FAILED" || status === "ABORTED") return 3; + return 2; +} + +function getActionPlan(status: TaskStatus | undefined, hasTask: boolean): ActionKey[] { + if (!hasTask || !status) return []; + if (status === "READY") return ["start", "abort", "refresh"]; + if (status === "PLANNING" || status === "EXECUTING" || status === "REVIEWING" || status === "SYNTHESIZING") { + return ["pause", "abort", "refresh"]; + } + if (status === "SUSPENDED") return ["resume", "abort", "refresh"]; + return ["refresh"]; +} + +function formatEvent(payload: ProgressEvent): string { + const label = EVENT_TEXT[payload.event] ?? payload.event; + if (payload.event === "TASK_PROGRESS" && typeof payload.data.progress === "number") { + return `${payload.timestamp} ${label}:${payload.data.progress}%`; + } + return `${payload.timestamp} ${label}`; +} + export function App() { const [form, setForm] = useState(initialForm); const [task, setTask] = useState(null); @@ -39,7 +130,15 @@ export function App() { const [error, setError] = useState(""); const canControl = Boolean(task?.taskId); - const stateLabel = task?.status ?? "READY"; + const taskStatus = task?.status; + const stateLabel = taskStatus ? STATUS_TEXT[taskStatus] : "未创建任务"; + const statusHint = taskStatus ? STATUS_HINT[taskStatus] : "请先在左侧创建任务。"; + const statusTone = taskStatus ? STATUS_TONE[taskStatus] : "idle"; + const isRunning = taskStatus ? RUNNING_STATUSES.has(taskStatus) : false; + const flowStep = getFlowStep(taskStatus, canControl); + const actionPlan = useMemo(() => getActionPlan(taskStatus, canControl), [taskStatus, canControl]); + const primaryAction = actionPlan[0]; + const secondaryActions = actionPlan.slice(1); const openConflicts = useMemo(() => conflicts.filter((c) => c.resolutionStatus === "OPEN"), [conflicts]); const progressStyle = { "--value": `${progress}%` } as CSSProperties; @@ -47,11 +146,11 @@ export function App() { if (!task?.taskId) return; const ws = connectProgressWs(task.taskId, (msg) => { const payload = JSON.parse(msg.data) as ProgressEvent; - setEvents((prev) => [`${payload.timestamp} ${payload.event}`, ...prev].slice(0, 30)); + setEvents((prev) => [formatEvent(payload), ...prev].slice(0, 30)); if (payload.event === "TASK_PROGRESS" && typeof payload.data.progress === "number") { setProgress(payload.data.progress); } - if (payload.event === "TASK_COMPLETED") { + if (payload.event === "TASK_COMPLETED" || payload.event === "TASK_FAILED" || payload.event === "TASK_ABORTED") { refreshAll(task.taskId); } }); @@ -82,6 +181,9 @@ export function App() { setBusy(true); setError(""); try { + setProgress(0); + setEvents([]); + setReport(""); const created = await createTask({ title: form.title, description: form.description, @@ -101,11 +203,15 @@ export function App() { } } - async function onAction(action: "start" | "pause" | "resume" | "abort") { + async function onAction(action: ActionKey) { if (!task?.taskId) return; setBusy(true); setError(""); try { + if (action === "refresh") { + await refreshAll(task.taskId); + return; + } if (action === "start") await startTask(task.taskId); if (action === "pause") await pauseTask(task.taskId); if (action === "resume") await resumeTask(task.taskId); @@ -125,7 +231,7 @@ export function App() { evidenceId: selected, conflictId: conflict.conflictId, selectedEvidenceId: selected, - reason: "Single-user default decision." + reason: "单用户默认采信决策。" }); await refreshAll(task!.taskId); } @@ -133,8 +239,8 @@ export function App() { return (
-

Deep Research Local Console

-
State: {stateLabel} | Task: {task?.taskId ?? "未创建"}
+

Deep Research 本地控制台

+
任务状态:{stateLabel} | 任务 ID:{task?.taskId ?? "未创建"}
@@ -144,7 +250,7 @@ export function App() { setForm((f) => ({ ...f, title: e.target.value }))} />