diff --git a/apps/web/src/components/voice/CloneModal.tsx b/apps/web/src/components/voice/CloneModal.tsx index 1107862e8..29d4d0b81 100644 --- a/apps/web/src/components/voice/CloneModal.tsx +++ b/apps/web/src/components/voice/CloneModal.tsx @@ -44,15 +44,8 @@ const ACCEPTED_EXTENSIONS = ["mp3", "wav", "m4a"]; const ACCEPTED_MIME = ".mp3,.wav,.m4a,audio/mpeg,audio/wav,audio/mp4"; const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB -/* ── 默认音色名称计数器 ─────────────────────────────────── */ - -let cloneCounter = 1; - -const getNextDefaultName = (): string => { - const name = `我的声音 ${cloneCounter}`; - cloneCounter += 1; - return name; -}; +/** 最长录制时长:5 分钟(秒) */ +const MAX_RECORD_SECONDS = 5 * 60; /* ── 组件 ───────────────────────────────────────────────── */ @@ -78,6 +71,15 @@ const CloneModal: React.FC = ({ const recordTimerRef = useRef | null>(null); const mediaRecorderRef = useRef(null); const audioChunksRef = useRef([]); + /** 默认音色名称计数器(组件级 ref,避免多实例串号) */ + const cloneCounterRef = useRef(1); + + /** 生成下一个默认音色名称 */ + const getNextDefaultName = useCallback((): string => { + const name = `我的声音 ${cloneCounterRef.current}`; + cloneCounterRef.current += 1; + return name; + }, []); /** 组件卸载时清理定时器和 MediaRecorder */ useEffect(() => { @@ -110,7 +112,7 @@ const CloneModal: React.FC = ({ mediaRecorderRef.current.stop(); } mediaRecorderRef.current = null; - }, []); + }, [getNextDefaultName]); /** 关闭弹窗 */ const handleClose = useCallback(() => { @@ -238,7 +240,25 @@ const CloneModal: React.FC = ({ setErrorMessage(""); recordTimerRef.current = setInterval(() => { - setRecordTime((prev) => prev + 1); + setRecordTime((prev) => { + const next = prev + 1; + if (next >= MAX_RECORD_SECONDS) { + // 达到 5 分钟上限,自动停止录制 + setTimeout(() => { + setIsRecording(false); + if (recordTimerRef.current) { + clearInterval(recordTimerRef.current); + recordTimerRef.current = null; + } + if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") { + mediaRecorderRef.current.stop(); + } + setErrorMessage("已达最长录制时长(5分钟),已自动停止"); + }, 0); + return MAX_RECORD_SECONDS; + } + return next; + }); }, 1000); } catch { setErrorMessage("无法访问麦克风,请检查浏览器权限设置"); @@ -446,7 +466,7 @@ const CloneModal: React.FC = ({ ? `录制中 ${formatRecordTime(recordTime)}` : recordedBlob ? `已录制 ${formatRecordTime(recordTime)}` - : "点击按钮开始录制你的声音"} + : "点击按钮开始录制(最长 5 分钟)"}

{isRecording && (