import React, { useState, useEffect } from 'react'; import { Users, X, Search, Plus, Check } from 'lucide-react'; import { SegmentWithDetails } from '../types/materialSegmentView'; import { Model } from '../types/model'; import { invoke } from '@tauri-apps/api/core'; interface MaterialSegmentModelDialogProps { segments: SegmentWithDetails[]; isOpen: boolean; onClose: () => void; onConfirm: (modelId: string | null) => void; isLoading?: boolean; } /** * MaterialSegment模特关联对话框组件 * 遵循 Tauri 开发规范的组件设计模式 */ export const MaterialSegmentModelDialog: React.FC = ({ segments, isOpen, onClose, onConfirm, isLoading = false, }) => { const [models, setModels] = useState([]); const [selectedModelId, setSelectedModelId] = useState(null); const [searchTerm, setSearchTerm] = useState(''); const [loadingModels, setLoadingModels] = useState(false); // 加载模特列表 useEffect(() => { if (isOpen) { loadModels(); } }, [isOpen]); const loadModels = async () => { setLoadingModels(true); try { const modelList = await invoke('get_all_models'); setModels(modelList); } catch (error) { console.error('加载模特列表失败:', error); } finally { setLoadingModels(false); } }; if (!isOpen) return null; const isBatch = segments.length > 1; const currentModels = [...new Set(segments.map(s => s.model?.id).filter(Boolean))]; // 过滤模特列表 const filteredModels = models.filter(model => model.name.toLowerCase().includes(searchTerm.toLowerCase()) || model.id.toLowerCase().includes(searchTerm.toLowerCase()) ); // 格式化时长 const formatDuration = (seconds: number) => { const minutes = Math.floor(seconds / 60); const secs = Math.round(seconds % 60); return `${minutes}:${secs.toString().padStart(2, '0')}`; }; const handleConfirm = () => { onConfirm(selectedModelId); }; return (
{/* 对话框头部 */}

{isBatch ? '批量关联模特' : '关联模特'}

{isBatch ? `为 ${segments.length} 个片段关联模特` : '为该片段关联模特'}

{/* 对话框内容 */}
{/* 片段概览 */}

片段概览

片段数量 {segments.length}
总时长 {formatDuration(segments.reduce((sum, s) => sum + s.segment.duration, 0))}
已关联模特 {segments.filter(s => s.model).length}
未关联模特 {segments.filter(s => !s.model).length}
{/* 当前关联的模特 */} {currentModels.length > 0 && (
当前关联的模特:
{currentModels.map(modelId => { const model = models.find(m => m.id === modelId); return ( {model?.name || modelId?.slice(-8)} ); })}
)}
{/* 模特选择 */}

选择模特

{/* 搜索框 */}
setSearchTerm(e.target.value)} className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent text-sm" />
{/* 模特列表 */}
{loadingModels ? (

加载模特列表...

) : filteredModels.length > 0 ? ( filteredModels.map((model) => (
setSelectedModelId(model.id)} className={`p-3 border rounded-lg cursor-pointer transition-all ${ selectedModelId === model.id ? 'border-green-500 bg-green-50' : 'border-gray-200 hover:border-green-300 hover:bg-green-50' }`} >
{model.name}
{selectedModelId === model.id && ( )}
ID: {model.id.slice(-8)} 类型: {model.gender}
)) ) : (

{searchTerm ? '未找到匹配的模特' : '暂无可用模特'}

{!searchTerm && ( )}
)}
{/* 操作说明 */}

操作说明:

  • 选择模特后,所有选中的片段都将关联到该模特
  • 如果片段已关联其他模特,将会被覆盖
  • 选择"取消关联"将移除所有片段的模特关联
  • 模特关联不会影响AI分类结果
{/* 对话框底部 */}
); };