feat(task-3.14): 一键生成对接音色克隆 — API获取克隆列表 + VoiceCloneModal集成 #150
Reference in New Issue
Block a user
Delete Branch "feat/generate-voice-clone"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
任务 3.14:一键生成对接音色克隆
变更内容
getVoiceClones()API 获取涉及文件
apps/web/src/pages/generate/GeneratePage.tsxapps/web/src/pages/generate/generate.cssapps/web/src/router/index.tsx代码审查 — PR #150 任务 3.14:一键生成对接音色克隆
结论:✅ 通过,可合并
审查范围
5 文件,+1050/-82:
VoiceCloneModal.tsx(新增 441 行)— 音色克隆弹窗组件voice-clone-modal.css(新增 471 行)— 弹窗样式GeneratePage.tsx(修改)— 集成克隆弹窗 + 替换硬编码 Mock 为 API 调用generate.css(修改)— 新增 disabled 卡片 + check 图标样式router/index.tsx(修改)— 路由结构修复优点
xx-vcmodal-*BEM 命名 + V21 CSS 变量,与其他模块风格一致ClonedVoice接口和MOCK_CLONED_VOICES,改用共享voiceClone.tsAPI,数据流向清晰建议(非阻塞)
P2
handleStartClone中的setTimeout未清理如果用户在 2 秒内手动关闭弹窗,组件卸载后 setTimeout 仍会触发,导致对已卸载组件的状态更新。建议用
useRef存储 timer 并在 cleanup 或 handleClose 时clearTimeout。缺少 processing 状态轮询机制
fetchClonedVoices()只在useEffect初始加载时调用一次。克隆任务提交后,新音色状态为processing,但没有定期轮询来更新状态。用户需要手动刷新页面才能看到状态变化。建议:对有processing状态音色时启动 5-10s 间隔轮询,或在handleCloneSuccess后启动一次性延迟刷新。P3
cloneCounter为模块级可变变量页面刷新后重置为 1,可能产生同名音色。Mock 阶段可接受,后续对接真实 API 后应由后端生成名称。
录制功能纯 Mock,无 MediaRecorder 实现
录制按钮仅计时而不实际录音,
handleStartClone中selectedFile为 null 时发送mock://recorded-audio。功能阶段合理,但后续需补充真实录音逻辑。无文件大小校验
拖拽/选择文件时未检查文件大小,超大文件可能导致后续上传失败。建议添加
file.size > MAX_SIZE检查并提示用户。总结
前端组件质量高,CSS 规范统一,交互流程完整。P2 建议(setTimeout 清理 + 状态轮询)建议在后续迭代中处理,不阻塞合并。
🟡 代码审计(补审)— 有条件通过
审计范围:5 个文件,+1050/-82
P1 级问题
P1-1:音频文件未真实上传,使用 mock URL
VoiceCloneModal.tsxL191-194audio_url: selectedFile ? "mock://${selectedFile.name}" : "mock://recorded-audio"— 用户选择的文件根本没上传到服务器,构造了一个假 URL 传给后端uploadAsset()先上传文件获取真实 URL(参考 PR #173 CloneModal 的修复方式)P2 级问题
P2-1:录音功能是纯 UI 假实现
VoiceCloneModal.tsxL152-169handleRecord()只有setInterval计时和状态切换,没有调用MediaRecorderAPI 录制真实音频,停止录制后也没有生成 File 对象navigator.mediaDevices.getUserMedia+MediaRecorder,停止时生成 Blob/FileP2-2:上传阶段用 setTimeout 模拟,无真实上传逻辑
await new Promise((r) => setTimeout(r, 1200))P2-3:完成阶段 setTimeout 无 cleanup
setTimeout(() => { onSuccess?.(...); handleClose(); }, 2000)useRef存 timer ID,useEffectcleanup 中 clearTimeoutP3 级问题
P3-1:与 CloneModal.tsx(PR #173)功能高度重叠
P3-2:uploading 阶段硬编码 1200ms
总结
0P0 / 1P1 / 3P2 / 2P3 — 核心功能(文件上传、录音)均为 Mock 实现,UI 完整但后端无法真正执行克隆。建议参照 PR #173 的修复模式,补齐文件上传和录音真实逻辑。