refactor: integrate export records into project details page

- Add export records tab to ProjectDetails page
- Optimize ExportRecordManager for compact tab display
- Remove global export records navigation (now project-level)
- Add showHeader and compact props to ExportRecordManager
- Remove standalone ExportRecordsPage (no longer needed)
- Update navigation structure to reflect project-level functionality

Export records are now properly scoped to individual projects and accessible through the project details interface.
This commit is contained in:
imeepos 2025-07-17 13:14:40 +08:00
parent c190fdae1b
commit 9f84ffe7f4
5 changed files with 67 additions and 52 deletions

View File

@ -8,7 +8,6 @@ import ModelDetail from './pages/ModelDetail';
import AiClassificationSettings from './pages/AiClassificationSettings';
import TemplateManagement from './pages/TemplateManagement';
import { MaterialModelBinding } from './pages/MaterialModelBinding';
import ExportRecordsPage from './pages/ExportRecordsPage';
import Navigation from './components/Navigation';
import { NotificationSystem, useNotifications } from './components/NotificationSystem';
import { useProjectStore } from './store/projectStore';
@ -79,7 +78,6 @@ function App() {
<Route path="/ai-classification-settings" element={<AiClassificationSettings />} />
<Route path="/templates" element={<TemplateManagement />} />
<Route path="/material-model-binding" element={<MaterialModelBinding />} />
<Route path="/export-records" element={<ExportRecordsPage />} />
</Routes>
</div>
</main>

View File

@ -11,11 +11,15 @@ import {
interface ExportRecordManagerProps {
projectId?: string;
matchingResultId?: string;
showHeader?: boolean; // 是否显示标题和描述
compact?: boolean; // 紧凑模式适用于tab显示
}
const ExportRecordManager: React.FC<ExportRecordManagerProps> = ({
projectId,
matchingResultId
matchingResultId,
showHeader = true,
compact = false
}) => {
const [records, setRecords] = useState<ExportRecord[]>([]);
const [statistics, setStatistics] = useState<ExportRecordStatistics | null>(null);
@ -184,31 +188,35 @@ const ExportRecordManager: React.FC<ExportRecordManagerProps> = ({
}, [projectId, matchingResultId, filters, pagination.page]);
return (
<div className="export-record-manager p-6">
<div className="header mb-6">
<h2 className="text-2xl font-bold text-gray-800 mb-4">
{projectId && <span className="text-sm text-gray-600 ml-2">()</span>}
{matchingResultId && <span className="text-sm text-gray-600 ml-2">()</span>}
</h2>
<div className={`export-record-manager ${compact ? 'p-0' : 'p-6'}`}>
{showHeader && (
<div className="header mb-6">
<h2 className="text-2xl font-bold text-gray-800 mb-4">
{projectId && <span className="text-sm text-gray-600 ml-2">()</span>}
{matchingResultId && <span className="text-sm text-gray-600 ml-2">()</span>}
</h2>
</div>
)}
<div className="content">
{/* 统计信息 */}
{statistics && (
<div className="stats-grid grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<div className="stat-card bg-blue-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-blue-600">{statistics.total_exports}</div>
<div className={`stats-grid grid ${compact ? 'grid-cols-2 md:grid-cols-4 gap-3 mb-4' : 'grid-cols-2 md:grid-cols-4 gap-4 mb-6'}`}>
<div className={`stat-card bg-blue-50 ${compact ? 'p-3' : 'p-4'} rounded-lg`}>
<div className={`${compact ? 'text-xl' : 'text-2xl'} font-bold text-blue-600`}>{statistics.total_exports}</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="stat-card bg-green-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-green-600">{statistics.successful_exports}</div>
<div className={`stat-card bg-green-50 ${compact ? 'p-3' : 'p-4'} rounded-lg`}>
<div className={`${compact ? 'text-xl' : 'text-2xl'} font-bold text-green-600`}>{statistics.successful_exports}</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="stat-card bg-red-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-red-600">{statistics.failed_exports}</div>
<div className={`stat-card bg-red-50 ${compact ? 'p-3' : 'p-4'} rounded-lg`}>
<div className={`${compact ? 'text-xl' : 'text-2xl'} font-bold text-red-600`}>{statistics.failed_exports}</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="stat-card bg-purple-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-purple-600">
<div className={`stat-card bg-purple-50 ${compact ? 'p-3' : 'p-4'} rounded-lg`}>
<div className={`${compact ? 'text-xl' : 'text-2xl'} font-bold text-purple-600`}>
{formatFileSize(statistics.total_file_size)}
</div>
<div className="text-sm text-gray-600"></div>

View File

@ -5,8 +5,7 @@ import {
UserGroupIcon,
CpuChipIcon,
DocumentDuplicateIcon,
LinkIcon,
DocumentArrowDownIcon
LinkIcon
} from '@heroicons/react/24/outline';
const Navigation: React.FC = () => {
@ -42,12 +41,6 @@ const Navigation: React.FC = () => {
href: '/ai-classification-settings',
icon: CpuChipIcon,
description: '管理AI视频分类规则'
},
{
name: '导出记录',
href: '/export-records',
icon: DocumentArrowDownIcon,
description: '查看和管理导出记录'
}
];

View File

@ -1,16 +0,0 @@
import React from 'react';
import ExportRecordManager from '../components/ExportRecordManager';
const ExportRecordsPage: React.FC = () => {
return (
<div className="export-records-page min-h-screen bg-gray-50">
<div className="container mx-auto px-4 py-8">
<div className="bg-white rounded-lg shadow-sm">
<ExportRecordManager />
</div>
</div>
</div>
);
};
export default ExportRecordsPage;

View File

@ -1,6 +1,6 @@
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, Shuffle } from 'lucide-react';
import { ArrowLeft, FolderOpen, Upload, FileVideo, FileAudio, FileImage, HardDrive, Brain, Loader2, Link, Layers, Calendar, MapPin, Users, CheckCircle, Filter, Shuffle, Download } from 'lucide-react';
import { invoke } from '@tauri-apps/api/core';
import { useProjectStore } from '../store/projectStore';
import { useMaterialStore } from '../store/materialStore';
@ -16,6 +16,7 @@ import { MaterialEditDialog } from '../components/MaterialEditDialog';
import { VideoClassificationProgress } from '../components/VideoClassificationProgress';
import { AiAnalysisLogViewer } from '../components/AiAnalysisLogViewer';
import MaterialCardSkeleton from '../components/MaterialCardSkeleton';
import ExportRecordManager from '../components/ExportRecordManager';
import { ProjectTemplateBindingList } from '../components/ProjectTemplateBindingList';
import { ProjectTemplateBindingForm } from '../components/ProjectTemplateBindingForm';
import { MaterialMatchingResultDialog } from '../components/MaterialMatchingResultDialog';
@ -123,7 +124,7 @@ export const ProjectDetails: React.FC = () => {
const [editingBinding, setEditingBinding] = useState<ProjectTemplateBindingDetail | null>(null);
const [showMaterialEditDialog, setShowMaterialEditDialog] = useState(false);
const [editingMaterial, setEditingMaterial] = useState<Material | null>(null);
const [activeTab, setActiveTab] = useState<'overview' | 'materials' | 'segments' | 'templates' | 'matching-results' | 'usage-stats' | 'ai-logs'>('overview');
const [activeTab, setActiveTab] = useState<'overview' | 'materials' | 'segments' | 'templates' | 'matching-results' | 'export-records' | 'usage-stats' | 'ai-logs'>('overview');
const [_batchClassificationResult, setBatchClassificationResult] = useState<ProjectBatchClassificationResponse | null>(null);
// 素材匹配状态
@ -976,6 +977,20 @@ export const ProjectDetails: React.FC = () => {
<span className="sm:hidden"></span>
</div>
</button>
<button
onClick={() => setActiveTab('export-records')}
className={`py-3 px-4 font-medium text-sm transition-all duration-200 whitespace-nowrap rounded-t-lg relative ${
activeTab === 'export-records'
? 'text-primary-600 border-b-2 border-primary-500'
: 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
}`}
>
<div className="flex items-center space-x-2">
<Download className="w-4 h-4" />
<span className="hidden sm:inline"></span>
<span className="sm:hidden"></span>
</div>
</button>
<button
onClick={() => setActiveTab('usage-stats')}
className={`py-3 px-4 font-medium text-sm transition-all duration-200 whitespace-nowrap rounded-t-lg relative ${
@ -1402,6 +1417,23 @@ export const ProjectDetails: React.FC = () => {
</div>
)}
{/* 导出记录选项卡 */}
{activeTab === 'export-records' && project && (
<div className="p-4 md:p-6 space-y-6">
<div className="mb-4">
<h3 className="text-lg font-medium text-gray-900 mb-2"></h3>
<p className="text-sm text-gray-600">
</p>
</div>
<ExportRecordManager
projectId={project.id}
showHeader={false}
compact={true}
/>
</div>
)}
{/* 素材使用状态选项卡 */}
{activeTab === 'usage-stats' && project && (
<div className="p-4 md:p-6 space-y-6">