fix(voice): CloneModal 录音 5 分钟上限 + cloneCounter 改 useRef
Deploy / Build Production Runtime Images (push) Has been skipped
Deploy / Deploy Production (push) Has been skipped
Deploy / Production Browser E2E (push) Has been skipped
Deploy / Staging E2E Tests (push) Failing after 116h28m49s
Deploy / Deploy Staging (push) Failing after 116h30m30s
CI/CD Pipeline / Frontend Lint (push) Failing after 116h31m4s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 116h31m4s
Deploy / Build Production Runtime Images (push) Has been skipped
Deploy / Deploy Production (push) Has been skipped
Deploy / Production Browser E2E (push) Has been skipped
Deploy / Staging E2E Tests (push) Failing after 116h28m49s
Deploy / Deploy Staging (push) Failing after 116h30m30s
CI/CD Pipeline / Frontend Lint (push) Failing after 116h31m4s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 116h31m4s
P3 优化(代码审计建议): 1. 录音达到 5 分钟自动停止并提示用户,避免传超大文件 2. cloneCounter 从模块级变量改为组件内 useRef,避免多实例串号 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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<CloneModalProps> = ({
|
||||
const recordTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
const mediaRecorderRef = useRef<MediaRecorder | null>(null);
|
||||
const audioChunksRef = useRef<Blob[]>([]);
|
||||
/** 默认音色名称计数器(组件级 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<CloneModalProps> = ({
|
||||
mediaRecorderRef.current.stop();
|
||||
}
|
||||
mediaRecorderRef.current = null;
|
||||
}, []);
|
||||
}, [getNextDefaultName]);
|
||||
|
||||
/** 关闭弹窗 */
|
||||
const handleClose = useCallback(() => {
|
||||
@@ -238,7 +240,25 @@ const CloneModal: React.FC<CloneModalProps> = ({
|
||||
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<CloneModalProps> = ({
|
||||
? `录制中 ${formatRecordTime(recordTime)}`
|
||||
: recordedBlob
|
||||
? `已录制 ${formatRecordTime(recordTime)}`
|
||||
: "点击按钮开始录制你的声音"}
|
||||
: "点击按钮开始录制(最长 5 分钟)"}
|
||||
</p>
|
||||
{isRecording && (
|
||||
<div className="xx-clonemodal-record-wave">
|
||||
|
||||
Reference in New Issue
Block a user