diff --git a/apps/desktop/src/components/MaterialSegmentStats.tsx b/apps/desktop/src/components/MaterialSegmentStats.tsx index f3099de..a275a89 100644 --- a/apps/desktop/src/components/MaterialSegmentStats.tsx +++ b/apps/desktop/src/components/MaterialSegmentStats.tsx @@ -54,10 +54,14 @@ export const MaterialSegmentStats: React.FC = ({ // 获取前5个分类/模特 const getTopItems = () => { - const counts = viewMode === MaterialSegmentViewMode.ByClassification - ? stats.classification_counts + if (!stats) return []; + + const counts = viewMode === MaterialSegmentViewMode.ByClassification + ? stats.classification_counts : stats.model_counts; - + + if (!counts) return []; + return Object.entries(counts) .sort(([, a], [, b]) => b - a) .slice(0, 5); @@ -65,6 +69,27 @@ export const MaterialSegmentStats: React.FC = ({ const topItems = getTopItems(); + // 如果没有统计数据,显示加载状态 + if (!stats) { + return ( +
+
+ {[...Array(4)].map((_, i) => ( +
+
+
+
+
+
+
+
+
+ ))} +
+
+ ); + } + return (
{/* 总体统计 */} @@ -74,7 +99,7 @@ export const MaterialSegmentStats: React.FC = ({

总片段数

-

{stats.total_segments}

+

{stats.total_segments || 0}

@@ -85,7 +110,7 @@ export const MaterialSegmentStats: React.FC = ({

已分类

-

{stats.classified_segments}

+

{stats.classified_segments || 0}

@@ -96,7 +121,7 @@ export const MaterialSegmentStats: React.FC = ({

未分类

-

{stats.unclassified_segments}

+

{stats.unclassified_segments || 0}

@@ -107,7 +132,7 @@ export const MaterialSegmentStats: React.FC = ({

总时长

-

{formatDuration(stats.total_duration)}

+

{formatDuration(stats.total_duration || 0)}

@@ -121,36 +146,36 @@ export const MaterialSegmentStats: React.FC = ({ 分类覆盖率 -
- {formatPercentage(stats.classification_coverage)} +
+ {formatPercentage(stats.classification_coverage || 0)}
{/* 进度条 */}
- 已分类 {stats.classified_segments} / {stats.total_segments} - {formatPercentage(stats.classification_coverage)} + 已分类 {stats.classified_segments || 0} / {stats.total_segments || 0} + {formatPercentage(stats.classification_coverage || 0)}
-
= 0.8 - ? 'bg-green-500' - : stats.classification_coverage >= 0.6 - ? 'bg-yellow-500' + (stats.classification_coverage || 0) >= 0.8 + ? 'bg-green-500' + : (stats.classification_coverage || 0) >= 0.6 + ? 'bg-yellow-500' : 'bg-red-500' }`} - style={{ width: `${stats.classification_coverage * 100}%` }} + style={{ width: `${(stats.classification_coverage || 0) * 100}%` }} />
{/* 分类建议 */}
- {stats.classification_coverage >= 0.8 ? ( + {(stats.classification_coverage || 0) >= 0.8 ? (

✓ 分类覆盖率良好,大部分片段已完成分类

- ) : stats.classification_coverage >= 0.6 ? ( + ) : (stats.classification_coverage || 0) >= 0.6 ? (

⚠ 分类覆盖率中等,建议继续完善分类

) : (

⚠ 分类覆盖率较低,需要加强AI分类处理

@@ -179,7 +204,7 @@ export const MaterialSegmentStats: React.FC = ({
{topItems.length > 0 ? ( topItems.map(([name, count], _index) => { - const percentage = (count / stats.total_segments) * 100; + const percentage = (count / (stats.total_segments || 1)) * 100; const isUnknown = name === '未分类' || name === '未关联模特'; return ( diff --git a/apps/desktop/src/pages/ProjectDetails.tsx b/apps/desktop/src/pages/ProjectDetails.tsx index 6042f14..6520591 100644 --- a/apps/desktop/src/pages/ProjectDetails.tsx +++ b/apps/desktop/src/pages/ProjectDetails.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { ArrowLeft, FolderOpen, Upload, FileVideo, FileAudio, FileImage, HardDrive, Brain, Loader2, Link, Layers, Calendar, MapPin } from 'lucide-react'; import { invoke } from '@tauri-apps/api/core'; @@ -96,7 +96,7 @@ export const ProjectDetails: React.FC = () => { return bindingDetails.filter(detail => filteredBindings.some(binding => binding.id === detail.binding.id) ); - }, [bindingDetails, bindingFilters, bindingActions]); + }, [bindingDetails, bindingFilters, bindingActions.getFilteredBindings]); // 模板状态管理 const { templates, fetchTemplates } = useTemplateStore(); @@ -118,10 +118,10 @@ export const ProjectDetails: React.FC = () => { const [currentMatchingBinding, setCurrentMatchingBinding] = useState(null); // 加载片段统计数据 - const loadSegmentStats = async (projectId: string) => { + const loadSegmentStats = useCallback(async (projectId: string) => { try { - const stats = await invoke('get_material_segment_stats', { projectId }); - setSegmentStats(stats); + const segmentView = await invoke('get_project_segment_view', { projectId }) as any; + setSegmentStats(segmentView.stats); } catch (error) { console.error('Failed to load segment stats:', error); // 设置默认统计数据 @@ -129,14 +129,13 @@ export const ProjectDetails: React.FC = () => { total_segments: 0, total_duration: 0, classified_segments: 0, + unclassified_segments: 0, classification_coverage: 0, - by_classification: {}, - by_model: {}, - by_duration_range: {}, - avg_segment_duration: 0 + classification_counts: {}, + model_counts: {} }); } - }; + }, []); // 加载项目详情 useEffect(() => { @@ -161,7 +160,7 @@ export const ProjectDetails: React.FC = () => { loadSegmentStats(foundProject.id); } } - }, [id, projects, loadMaterials, loadMaterialStats, bindingActions, loadSegmentStats]); + }, [id, projects, loadMaterials, loadMaterialStats, bindingActions.fetchTemplatesByProject, loadSegmentStats]); // 加载模板列表 useEffect(() => {