From 52ce437e639645a1bef89d93565f725b509489ed Mon Sep 17 00:00:00 2001 From: imeepos Date: Wed, 16 Jul 2025 18:33:57 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DProjectDetails.tsx?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E6=97=A0=E9=99=90=E8=AF=B7=E6=B1=82=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E5=92=8C=E6=A8=A1=E7=89=B9=E5=90=8D=E7=A7=B0=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复loadProjectClassificationStats函数的无限循环问题 - 使用useRef跟踪分类统计加载状态,避免重复请求 - 添加模特信息加载功能,显示真实模特名称而非model_id - 优化useEffect依赖,防止不必要的重新渲染和请求 - 在项目切换和导入完成时正确重置加载状态 --- .../presentation/commands/model_commands.rs | 3 - apps/desktop/src/pages/ProjectDetails.tsx | 69 +++++++++++++++---- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/apps/desktop/src-tauri/src/presentation/commands/model_commands.rs b/apps/desktop/src-tauri/src/presentation/commands/model_commands.rs index 9d0799d..73ea084 100644 --- a/apps/desktop/src-tauri/src/presentation/commands/model_commands.rs +++ b/apps/desktop/src-tauri/src/presentation/commands/model_commands.rs @@ -29,7 +29,6 @@ pub async fn get_model_by_id( state: State<'_, AppState>, id: String, ) -> Result, String> { - println!("get_model_by_id 命令开始执行,ID: {}", id); let repository_guard = state.get_model_repository() .map_err(|e| { @@ -43,14 +42,12 @@ pub async fn get_model_by_id( "模特仓库未初始化".to_string() })?; - println!("调用 ModelService::get_model_by_id"); let result = ModelService::get_model_by_id(repository, &id) .map_err(|e| { println!("ModelService::get_model_by_id 失败: {}", e); e.to_string() }); - println!("get_model_by_id 命令执行完成,结果: {:?}", result.is_ok()); result } diff --git a/apps/desktop/src/pages/ProjectDetails.tsx b/apps/desktop/src/pages/ProjectDetails.tsx index 9e5e2d3..4760b75 100644 --- a/apps/desktop/src/pages/ProjectDetails.tsx +++ b/apps/desktop/src/pages/ProjectDetails.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useCallback, useMemo } from 'react'; +import React, { useEffect, useState, useCallback, useMemo, useRef } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { ArrowLeft, FolderOpen, Upload, FileVideo, FileAudio, FileImage, HardDrive, Brain, Loader2, Link, Layers, Calendar, MapPin, Users, CheckCircle, Filter } from 'lucide-react'; import { invoke } from '@tauri-apps/api/core'; @@ -134,6 +134,10 @@ export const ProjectDetails: React.FC = () => { const [materialModelFilter, setMaterialModelFilter] = useState('全部'); const [materialUsageFilter, setMaterialUsageFilter] = useState('全部'); const [materialClassificationRecords, setMaterialClassificationRecords] = useState<{[materialId: string]: any[]}>({}); + const [modelsMap, setModelsMap] = useState<{[modelId: string]: any}>({}); + + // 用于跟踪分类统计是否已加载的ref + const classificationStatsLoadedRef = useRef(null); // 加载片段统计数据 const loadSegmentStats = useCallback(async (projectId: string) => { @@ -155,12 +159,30 @@ export const ProjectDetails: React.FC = () => { } }, []); - // 加载项目分类统计信息 - const loadProjectClassificationStats = useCallback(async (projectId: string) => { + // 加载所有模特信息 + const loadAllModels = useCallback(async () => { try { + const models = await invoke('get_all_models') as any[]; + const modelMap: {[modelId: string]: any} = {}; + models.forEach(model => { + modelMap[model.id] = model; + }); + setModelsMap(modelMap); + } catch (error) { + console.error('Failed to load models:', error); + setModelsMap({}); + } + }, []); + + // 加载项目分类统计信息 + const loadProjectClassificationStats = useCallback(async (projectId: string, materialList?: any[]) => { + try { + // 使用传入的素材列表或当前的素材列表 + const materialsToProcess = materialList || materials; + // 获取每个素材的分类记录 const classificationRecords: {[materialId: string]: any[]} = {}; - for (const material of materials) { + for (const material of materialsToProcess) { try { const records = await invoke('get_material_classification_records', { materialId: material.id }) as any[]; classificationRecords[material.id] = records; @@ -175,7 +197,7 @@ export const ProjectDetails: React.FC = () => { console.error('Failed to load project classification stats:', error); setMaterialClassificationRecords({}); } - }, [materials]); + }, []); // 加载项目详情 useEffect(() => { @@ -192,6 +214,9 @@ export const ProjectDetails: React.FC = () => { // 加载项目素材 if (foundProject) { + // 重置分类统计加载状态 + classificationStatsLoadedRef.current = null; + loadMaterials(foundProject.id); loadMaterialStats(foundProject.id); // 加载项目的模板绑定 @@ -200,18 +225,20 @@ export const ProjectDetails: React.FC = () => { loadSegmentStats(foundProject.id); // 加载素材使用状态概览 loadUsageOverview(foundProject.id); - // 加载项目分类统计信息 - loadProjectClassificationStats(foundProject.id); + // 加载所有模特信息 + loadAllModels(); + // 加载项目分类统计信息将在素材加载完成后执行 } } - }, [id, projects, loadMaterials, loadMaterialStats, bindingActions.fetchTemplatesByProject, loadSegmentStats, loadUsageOverview, loadProjectClassificationStats]); + }, [id, projects, loadMaterials, loadMaterialStats, bindingActions.fetchTemplatesByProject, loadSegmentStats, loadUsageOverview, loadAllModels, loadProjectClassificationStats]); - // 当素材列表变化时,重新加载分类统计信息 + // 当素材加载完成后,加载分类统计信息 useEffect(() => { - if (project && materials.length > 0) { - loadProjectClassificationStats(project.id); + if (project && materials.length > 0 && classificationStatsLoadedRef.current !== project.id) { + classificationStatsLoadedRef.current = project.id; + loadProjectClassificationStats(project.id, materials); } - }, [materials.length, project, loadProjectClassificationStats]); + }, [project?.id, materials.length, loadProjectClassificationStats]); // 加载模板列表 useEffect(() => { @@ -284,6 +311,8 @@ export const ProjectDetails: React.FC = () => { console.log('导入完成:', result); // 重新加载素材列表 if (project) { + // 重置分类统计加载状态,以便重新加载 + classificationStatsLoadedRef.current = null; loadMaterials(project.id); loadMaterialStats(project.id); } @@ -591,7 +620,6 @@ export const ProjectDetails: React.FC = () => { const modelCounts: {[key: string]: number} = {}; materials.forEach(material => { if (material.model_id) { - // 这里需要根据model_id获取模特名称,暂时使用model_id const modelKey = material.model_id; modelCounts[modelKey] = (modelCounts[modelKey] || 0) + 1; } else { @@ -600,15 +628,26 @@ export const ProjectDetails: React.FC = () => { }); Object.entries(modelCounts).forEach(([modelKey, count]) => { + let label = '未指定'; + if (modelKey !== '未指定') { + // 从modelsMap中获取模特的真实名称 + const model = modelsMap[modelKey]; + if (model) { + label = model.stage_name || model.name; + } else { + label = `模特-${modelKey}`; + } + } + options.push({ - label: modelKey === '未指定' ? '未指定' : `模特-${modelKey}`, + label, value: modelKey, count }); }); return options; - }, [materials]); + }, [materials, modelsMap]); const materialUsageOptions = useMemo(() => { // 计算使用状态统计