diff --git a/apps/web/src/pages/voice-materials/components/voice-material-row/useRowProgress.ts b/apps/web/src/pages/voice-materials/components/voice-material-row/useRowProgress.ts index 3fc8e5e9b..d758b87c1 100755 --- a/apps/web/src/pages/voice-materials/components/voice-material-row/useRowProgress.ts +++ b/apps/web/src/pages/voice-materials/components/voice-material-row/useRowProgress.ts @@ -1,4 +1,4 @@ -import { useRef, useCallback } from "react" +import { useRef, useCallback, useEffect } from "react" interface UseRowProgressOptions { duration: number @@ -7,6 +7,22 @@ interface UseRowProgressOptions { export function useRowProgress({ duration, onSeek }: UseRowProgressOptions) { const progressRef = useRef(null) + const listenersRef = useRef<{ move: ((e: MouseEvent) => void) | null; up: (() => void) | null }>({ + move: null, + up: null, + }) + + const cleanupListeners = useCallback(() => { + const { move, up } = listenersRef.current + if (move) { + document.removeEventListener("mousemove", move) + listenersRef.current.move = null + } + if (up) { + document.removeEventListener("mouseup", up) + listenersRef.current.up = null + } + }, []) const handleMouseDown = useCallback( (e: React.MouseEvent) => { @@ -16,6 +32,7 @@ export function useRowProgress({ duration, onSeek }: UseRowProgressOptions) { const doSeek = (ev: MouseEvent) => { if (!progressRef.current) return const rect = progressRef.current.getBoundingClientRect() + if (rect.width <= 0) return const percent = Math.max(0, Math.min(1, (ev.clientX - rect.left) / rect.width)) onSeek(percent * duration) } @@ -24,15 +41,26 @@ export function useRowProgress({ duration, onSeek }: UseRowProgressOptions) { const handleMove = (ev: MouseEvent) => doSeek(ev) const handleUp = () => { - document.removeEventListener("mousemove", handleMove) - document.removeEventListener("mouseup", handleUp) + cleanupListeners() } + // 先清理旧的,再添加新的 + cleanupListeners() + listenersRef.current.move = handleMove + listenersRef.current.up = handleUp + document.addEventListener("mousemove", handleMove) document.addEventListener("mouseup", handleUp) }, - [duration, onSeek], + [duration, onSeek, cleanupListeners], ) + // 组件卸载时清理事件监听器 + useEffect(() => { + return () => { + cleanupListeners() + } + }, [cleanupListeners]) + return { progressRef, handleMouseDown } } diff --git a/apps/web/src/test/pages/voice-materials/smoke.test.tsx b/apps/web/src/test/pages/voice-materials/smoke.test.tsx index 379d72ed9..946a442db 100755 --- a/apps/web/src/test/pages/voice-materials/smoke.test.tsx +++ b/apps/web/src/test/pages/voice-materials/smoke.test.tsx @@ -20,6 +20,8 @@ import "@/pages/voice-materials/components/voice-material-card/CardActions" import "@/pages/voice-materials/components/voice-material-card/BatchCheckbox" import "@/pages/voice-materials/components/voice-material-card/types" import "@/pages/voice-materials/components/VoiceMaterialRow" +import "@/pages/voice-materials/components/voice-material-row/useRowProgress" +import "@/pages/voice-materials/components/voice-material-row/TagDisplay" import "@/pages/voice-materials/components/Toolbar" import "@/pages/voice-materials/components/TagFilterBar" import "@/pages/voice-materials/components/BatchBar"