diff --git a/apps/web/components/review/ReviewReport.tsx b/apps/web/components/review/ReviewReport.tsx index 79d2630..33e66c0 100644 --- a/apps/web/components/review/ReviewReport.tsx +++ b/apps/web/components/review/ReviewReport.tsx @@ -66,6 +66,9 @@ import { ReviewSummaryRail } from "./ReviewSummaryRail"; import { StylePanel } from "@/components/style/StylePanel"; import { RefineView } from "@/components/style/RefineView"; +// 命中区闪烁高亮持续时长(朱砂脉冲)。 +const FLASH_DURATION_MS = 900; + interface ReviewReportProps { project: ProjectResponse; chapterNo: number; @@ -112,6 +115,16 @@ export function ReviewReport({ null, ); const seededRef = useRef(false); + // flashMark 的收尾定时器句柄:组件卸载或重复触发时清除,避免定时器泄漏。 + const flashTimerRef = useRef(null); + useEffect( + () => () => { + if (flashTimerRef.current !== null) { + window.clearTimeout(flashTimerRef.current); + } + }, + [], + ); // 进页一次性把历史留痕(冲突 + 伏笔建议 + 节奏)种入流状态(无需重审即可裁决/查看)。 const seededConflicts = useMemo( @@ -333,7 +346,13 @@ export function ReviewReport({ const mark = document.getElementById(`draft-mark-${index}`); if (!mark) return; mark.classList.add("draft-mark-flash"); - window.setTimeout(() => mark.classList.remove("draft-mark-flash"), 900); + if (flashTimerRef.current !== null) { + window.clearTimeout(flashTimerRef.current); + } + flashTimerRef.current = window.setTimeout(() => { + mark.classList.remove("draft-mark-flash"); + flashTimerRef.current = null; + }, FLASH_DURATION_MS); }; // 跳转/锚点:在可编辑正文里定位、选中并滚动到该冲突对应区域 + 闪烁;定位不到则提示手动查找。