+ {/* 文本输入 */}
+
+
+
+
+ {/* 音色选择 */}
+
+
+
+ {/* 音色来源选择 */}
+
+
+ {/* 系统音色选择器 */}
+ {voiceSource === 'system' && (
+
+ )}
+
+ {/* 自定义音色选择 */}
+ {voiceSource === 'custom' && (
+
+
+ 自定义音色列表
+
+
+
+
+ {isLoadingVoices ? (
+
+
+ 加载中...
+
+ ) : voices.length === 0 ? (
+
+ ) : (
+ voices.map((voice) => (
+
handleVoiceSelect(voice.voice_id)}
+ >
+
+
+
{voice.voice_name || voice.voice_id}
+
ID: {voice.voice_id}
+
+ {voiceSource === 'custom' && selectedVoiceId === voice.voice_id && (
+
+ )}
+
+
+ ))
+ )}
+
+
+ )}
+
+
+ {/* 参数控制 */}
+
+
+
+
+
+
setSpeechRequest(prev => ({
+ ...prev,
+ speed: parseFloat(e.target.value)
+ }))}
+ disabled={speechState.status === SpeechGenerationStatus.GENERATING}
+ className="w-full"
+ />
+
+ 0.5x
+ 2.0x
+
+
+
+
+
setSpeechRequest(prev => ({
+ ...prev,
+ vol: parseFloat(e.target.value)
+ }))}
+ disabled={speechState.status === SpeechGenerationStatus.GENERATING}
+ className="w-full"
+ />
+
+ 0.1x
+ 2.0x
+
+
+
+
+
+ {/* 情感选择 */}
+
+
+
+
+
+ {/* 生成状态显示 */}
+ {speechState.status !== SpeechGenerationStatus.IDLE && (
+
+
+ {speechState.status === SpeechGenerationStatus.SUCCESS && (
+ <>
+
+
+ >
+ )}
+ {speechState.status === SpeechGenerationStatus.ERROR && (
+ <>
+
+
+
生成失败
+
{speechState.error}
+
+ >
+ )}
+ {speechState.status === SpeechGenerationStatus.GENERATING && (
+ <>
+
+
+
生成中
+
{speechState.progress}
+
+ >
+ )}
+
+
+ {/* 音频播放器 */}
+ {speechState.result?.data && speechState.status === SpeechGenerationStatus.SUCCESS && (
+
+
+ 生成的语音
+
+
+
+
+
+ )}
+
+ )}
+
+ {/* 操作按钮 */}
+
+
+
+
+
+
+ );
+};
diff --git a/apps/desktop/src/components/VoiceCloneModal.tsx b/apps/desktop/src/components/VoiceCloneModal.tsx
new file mode 100644
index 0000000..dada131
--- /dev/null
+++ b/apps/desktop/src/components/VoiceCloneModal.tsx
@@ -0,0 +1,356 @@
+import React, { useState, useCallback } from 'react';
+import {
+ Mic,
+ Upload,
+ Wand2,
+ CheckCircle,
+ XCircle,
+ Loader2,
+ Music,
+ FileAudio,
+ X
+} from 'lucide-react';
+import { invoke } from '@tauri-apps/api/core';
+import { open } from '@tauri-apps/plugin-dialog';
+import { Modal } from './Modal';
+import { useNotifications } from './NotificationSystem';
+import {
+ AudioUploadRequest,
+ AudioUploadResponse,
+ VoiceCloneRequest,
+ VoiceCloneResponse,
+ VoiceCloneStatus,
+ AudioFileInfo,
+ VoiceCloneState
+} from '../types/voiceClone';
+
+interface VoiceCloneModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ onSuccess?: (voiceId: string) => void;
+}
+
+/**
+ * 声音克隆Modal组件
+ * 封装声音克隆功能为独立的弹框组件
+ */
+export const VoiceCloneModal: React.FC