diff --git a/frontend-web/src/roundtable-planning/pages/DashboardAgentPage.tsx b/frontend-web/src/roundtable-planning/pages/DashboardAgentPage.tsx index 897bf4e3..6ce2ccb1 100644 --- a/frontend-web/src/roundtable-planning/pages/DashboardAgentPage.tsx +++ b/frontend-web/src/roundtable-planning/pages/DashboardAgentPage.tsx @@ -122,6 +122,15 @@ export default function DashboardAgentPage() { } }, [availableModels, selectedModel]); + // init 只跑一次:start / hydrateTranscript 用 ref 持有,effect 只依赖 [taskId]。 + // 否则它们的 identity 会随 selectedModel/themeMode 变化重建(模型挂载后异步加载即触发), + // 让 effect 在 await 期间 cleanup → 二次进入被 didInitRef 挡掉 → 首跑的 finally 因 cancelled + // 跳过 setLoading(false),loading 永远停在 true(白转圈,卡在「正在加载任务大屏…」)。 + const startRef = useRef(start); + startRef.current = start; + const hydrateTranscriptRef = useRef(hydrateTranscript); + hydrateTranscriptRef.current = hydrateTranscript; + // ── 挂载:按 taskId 取会话 → 决定直显大屏 / 看生成过程(只跑一次) ── const didInitRef = useRef(false); useEffect(() => { @@ -131,37 +140,38 @@ export default function DashboardAgentPage() { return; } didInitRef.current = true; - let cancelled = false; void (async () => { try { - const session = await getDashboardSessionByTask(taskId).catch(() => null); - if (cancelled) return; + // 会话 + 输入 JSON 并行取。输入**始终**备好并写进 inputRef —— 否则恢复历史 + // 后用户点「重新绘制」/续问时 buildInitialPrompt 拿不到源数据,会回退到空的 + // 圆桌 prompt,智能体没东西可分析 → 生成不出数据(即「模拟数据不生效」)。 + const [session, input] = await Promise.all([ + getDashboardSessionByTask(taskId).catch(() => null), + fetchDashboardInputJson(taskId).catch(() => null), + ]); + inputRef.current = input; const savedDialogues = (session?.transcript as Step2Dialogue[] | undefined) ?? []; const validReport = session?.reportJson && parseReportJson(session.reportJson); if (validReport) { // 有有效结果 → 恢复并直接展示大屏页面。 - hydrateTranscript(savedDialogues, session!.reportJson); + hydrateTranscriptRef.current(savedDialogues, session!.reportJson); setView("page"); } else if (savedDialogues.length > 0) { - // 有历史聊天但无有效结果 → 进生成过程展示历史,不自动重跑。 - hydrateTranscript(savedDialogues, session?.reportJson ?? null); + // 有历史聊天但无有效结果 → 进生成过程展示历史,不自动重跑(点「重新绘制」可重生成)。 + hydrateTranscriptRef.current(savedDialogues, session?.reportJson ?? null); setView("chat"); } else { - // 无任何记录 → 拉输入 JSON 自动开跑,让用户看生成过程(不自动跳)。 - const input = await fetchDashboardInputJson(taskId).catch(() => null); - if (cancelled) return; - inputRef.current = input; + // 无任何记录 → 自动开跑,让用户看生成过程(不自动跳)。 setView("chat"); - if (input) start(); + if (input) startRef.current(); } } finally { - if (!cancelled) setLoading(false); + setLoading(false); } })(); - return () => { - cancelled = true; - }; - }, [taskId, hydrateTranscript, start]); + // 只依赖 taskId:start/hydrateTranscript 经 ref 读取,避免其 identity 变化重跑本 effect。 + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [taskId]); // ── 防抖回存聊天记录 + 最新 report-json(按 taskId 共享) ── useEffect(() => { @@ -245,9 +255,9 @@ export default function DashboardAgentPage() { } return ( -