diff --git a/apps/desktop/src-tauri/src/business/services/material_service.rs b/apps/desktop/src-tauri/src/business/services/material_service.rs index 888f588..7b9e391 100644 --- a/apps/desktop/src-tauri/src/business/services/material_service.rs +++ b/apps/desktop/src-tauri/src/business/services/material_service.rs @@ -919,6 +919,28 @@ impl MaterialService { }, }) } + + /// 获取全局模特绑定统计 + pub fn get_global_model_binding_stats( + repository: &MaterialRepository, + ) -> Result { + debug!("获取全局模特绑定统计"); + + let bound_count = repository.count_global_bound_materials()?; + let unbound_count = repository.count_global_unbound_materials()?; + let total_count = bound_count + unbound_count; + + Ok(ProjectModelBindingStats { + total_materials: total_count, + bound_materials: bound_count, + unbound_materials: unbound_count, + binding_rate: if total_count > 0 { + (bound_count as f64 / total_count as f64) * 100.0 + } else { + 0.0 + }, + }) + } } /// 项目模特绑定统计信息 diff --git a/apps/desktop/src-tauri/src/data/repositories/material_repository.rs b/apps/desktop/src-tauri/src/data/repositories/material_repository.rs index 970f1a0..adaffe8 100644 --- a/apps/desktop/src-tauri/src/data/repositories/material_repository.rs +++ b/apps/desktop/src-tauri/src/data/repositories/material_repository.rs @@ -643,6 +643,32 @@ impl MaterialRepository { Ok(count as u32) } + + /// 获取全局绑定了模特的素材数量 + pub fn count_global_bound_materials(&self) -> Result { + let conn = self.connection.lock().unwrap(); + + let count: i64 = conn.query_row( + "SELECT COUNT(*) FROM materials WHERE model_id IS NOT NULL", + [], + |row| row.get(0), + )?; + + Ok(count as u32) + } + + /// 获取全局未绑定模特的素材数量 + pub fn count_global_unbound_materials(&self) -> Result { + let conn = self.connection.lock().unwrap(); + + let count: i64 = conn.query_row( + "SELECT COUNT(*) FROM materials WHERE model_id IS NULL", + [], + |row| row.get(0), + )?; + + Ok(count as u32) + } } /// 模特素材统计信息 diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index 4bcc365..044e3be 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -80,6 +80,7 @@ pub fn run() { commands::material_commands::switch_material_model, commands::material_commands::get_model_material_statistics, commands::material_commands::get_project_model_binding_stats, + commands::material_commands::get_global_model_binding_stats, commands::material_commands::update_material, // 模特管理命令 commands::model_commands::create_model, diff --git a/apps/desktop/src-tauri/src/presentation/commands/material_commands.rs b/apps/desktop/src-tauri/src/presentation/commands/material_commands.rs index 48cc3c3..be2e2aa 100644 --- a/apps/desktop/src-tauri/src/presentation/commands/material_commands.rs +++ b/apps/desktop/src-tauri/src/presentation/commands/material_commands.rs @@ -1013,6 +1013,21 @@ pub async fn get_project_model_binding_stats( .map_err(|e| format!("获取项目模特绑定统计失败: {}", e)) } +/// 获取全局模特绑定统计信息命令 +#[command] +pub async fn get_global_model_binding_stats( + state: State<'_, AppState>, +) -> Result { + let repository_guard = state.get_material_repository() + .map_err(|e| format!("获取素材仓库失败: {}", e))?; + + let repository = repository_guard.as_ref() + .ok_or("素材仓库未初始化")?; + + MaterialService::get_global_model_binding_stats(repository) + .map_err(|e| format!("获取全局模特绑定统计失败: {}", e)) +} + /// 更新素材信息命令(包括模特绑定) #[command] pub async fn update_material( diff --git a/apps/desktop/src/pages/MaterialModelBinding.tsx b/apps/desktop/src/pages/MaterialModelBinding.tsx index e9ea5f6..168db18 100644 --- a/apps/desktop/src/pages/MaterialModelBinding.tsx +++ b/apps/desktop/src/pages/MaterialModelBinding.tsx @@ -35,7 +35,7 @@ export const MaterialModelBinding: React.FC = () => { const [materials, setMaterials] = useState([]); const [models, setModels] = useState([]); - const [stats] = useState(null); + const [stats, setStats] = useState(null); const [selectedModel, setSelectedModel] = useState(''); const [searchQuery, setSearchQuery] = useState(''); const [filterType, setFilterType] = useState<'all' | 'bound' | 'unbound'>('all'); @@ -50,6 +50,13 @@ export const MaterialModelBinding: React.FC = () => { loadData(); }, []); + // 当搜索条件变化时重新加载素材 + useEffect(() => { + if (searchQuery !== '' || filterType !== 'all' || selectedModel !== '') { + loadMaterials(); + } + }, [searchQuery, filterType, selectedModel]); + const loadData = async () => { setLoading(true); try { @@ -92,9 +99,8 @@ export const MaterialModelBinding: React.FC = () => { const loadStats = async () => { try { - // 这里需要一个获取全局统计的API,暂时跳过 - // const statsData = await invoke('get_global_model_binding_stats'); - // setStats(statsData); + const statsData = await MaterialModelBindingService.getGlobalModelBindingStats(); + setStats(statsData); } catch (err) { console.error('加载统计失败:', err); } diff --git a/apps/desktop/src/pages/ProjectDetails.tsx b/apps/desktop/src/pages/ProjectDetails.tsx index c54913b..654cc5f 100644 --- a/apps/desktop/src/pages/ProjectDetails.tsx +++ b/apps/desktop/src/pages/ProjectDetails.tsx @@ -61,6 +61,16 @@ export const ProjectDetails: React.FC = () => { actions: bindingActions } = useProjectTemplateBindingStore(); + // 获取过滤后的绑定详情 + const filteredBindingDetails = React.useMemo(() => { + if (!bindingDetails.length) return []; + + const filteredBindings = bindingActions.getFilteredBindings(); + return bindingDetails.filter(detail => + filteredBindings.some(binding => binding.id === detail.binding.id) + ); + }, [bindingDetails, bindingFilters, bindingActions]); + // 模板状态管理 const { templates, fetchTemplates } = useTemplateStore(); @@ -607,7 +617,7 @@ export const ProjectDetails: React.FC = () => { )} { + return await invoke('get_global_model_binding_stats'); + } + /** * 更新素材信息 */ @@ -156,32 +163,34 @@ export class MaterialModelBindingService { bound?: boolean; search?: string; }): Promise { + let materials: Material[] = []; + + // 首先根据主要过滤条件获取素材 if (filter.modelId) { - return this.getMaterialsByModel(filter.modelId); - } - - if (filter.bound === false) { - return this.getUnboundMaterials(filter.projectId); - } - - if (filter.projectId) { - const materials = await this.getProjectMaterials(filter.projectId); - + materials = await this.getMaterialsByModel(filter.modelId); + } else if (filter.bound === false) { + materials = await this.getUnboundMaterials(filter.projectId); + } else if (filter.projectId) { + materials = await this.getProjectMaterials(filter.projectId); + + // 如果指定了绑定状态过滤 if (filter.bound === true) { - return materials.filter(m => m.model_id); + materials = materials.filter(m => m.model_id); } - - if (filter.search) { - return materials.filter(m => - m.name.toLowerCase().includes(filter.search!.toLowerCase()) - ); - } - - return materials; + } else { + // 如果没有指定项目,返回空数组 + return []; } - - // 如果没有指定项目,返回空数组 - return []; + + // 应用搜索过滤 + if (filter.search && filter.search.trim()) { + const searchLower = filter.search.toLowerCase(); + materials = materials.filter(m => + m.name.toLowerCase().includes(searchLower) + ); + } + + return materials; } /**