'use client'; import React, { useState, useMemo, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Network, Clock, CheckCircle, Search, ChevronRight, ChevronDown, RefreshCw, XCircle, Info, Activity, Pause } from 'lucide-react'; import { cn } from '@/public/lib/utils'; interface TaskExecution { id: string; name: string; displayName: string; status: string; statusCode: number; type: string; dataSize: number; executionTime: number; startTime: number; endTime: number; progress: number; level: number; // 层级:0=主任务,1=子任务 parentId?: string; // 父任务ID isExpanded?: boolean; // 是否展开子任务 subTasks?: TaskExecution[]; // 子任务列表 phases: { initialization?: number; dataLoading?: number; aiProcessing?: number; resultGeneration?: number; dataTransfer?: number; completion?: number; }; taskResult?: any; } interface TaskTimelineProps { tasks: any[]; className?: string; onRefresh?: () => void; isRefreshing?: boolean; onRetryTask?: (taskId: string) => Promise; // 实时监控相关 isPolling?: boolean; lastUpdate?: Date; onTogglePolling?: () => void; } export function NetworkTimeline({ tasks, className, onRefresh, isRefreshing = false, onRetryTask, isPolling = false, lastUpdate, onTogglePolling }: TaskTimelineProps) { const [selectedTask, setSelectedTask] = useState(null); const [filterType, setFilterType] = useState('all'); const [searchTerm, setSearchTerm] = useState(''); const [expandedTasks, setExpandedTasks] = useState>(new Set()); const [timeAgo, setTimeAgo] = useState(''); // 更新时间显示 useEffect(() => { if (!lastUpdate) return; const updateTimeAgo = () => { const now = new Date(); const diff = Math.floor((now.getTime() - lastUpdate.getTime()) / 1000); if (diff < 60) { setTimeAgo(`${diff}秒前`); } else if (diff < 3600) { setTimeAgo(`${Math.floor(diff / 60)}分钟前`); } else { setTimeAgo(`${Math.floor(diff / 3600)}小时前`); } }; updateTimeAgo(); const interval = setInterval(updateTimeAgo, 1000); return () => clearInterval(interval); }, [lastUpdate]); // 计算任务统计信息 const taskStats = useMemo(() => { if (!tasks || tasks.length === 0) { return { total: 0, completed: 0, inProgress: 0, failed: 0, pending: 0 }; } const stats = { total: tasks.length, completed: 0, inProgress: 0, failed: 0, pending: 0 }; tasks.forEach((task: any) => { switch (task.task_status) { case 'COMPLETED': stats.completed++; break; case 'IN_PROGRESS': stats.inProgress++; break; case 'FAILED': stats.failed++; break; case 'PENDING': default: stats.pending++; break; } }); return stats; }, [tasks]); // 将任务转换为执行时间线格式(支持层级结构) const taskExecutions = useMemo((): TaskExecution[] => { if (!tasks || tasks.length === 0) return []; const startTime = Math.min(...tasks.map(task => new Date(task.created_at).getTime())); const result: TaskExecution[] = []; tasks.forEach((task: any) => { const taskStartTime = new Date(task.created_at).getTime(); const taskEndTime = new Date(task.updated_at).getTime(); const duration = taskEndTime - taskStartTime; // 任务执行阶段分解 const phases = { initialization: Math.min(duration * 0.05, 1000), dataLoading: Math.min(duration * 0.1, 2000), aiProcessing: duration * 0.7, resultGeneration: Math.min(duration * 0.1, 1000), dataTransfer: Math.min(duration * 0.05, 500), completion: Math.min(duration * 0.05, 500) }; // 主任务 const mainTask: TaskExecution = { id: task.task_id, name: task.task_name, displayName: getTaskDisplayName(task.task_name), status: task.task_status, statusCode: getTaskStatusCode(task.task_status), type: getTaskType(task.task_name), dataSize: getTaskDataSize(task), executionTime: duration, startTime: taskStartTime - startTime, endTime: taskEndTime - startTime, progress: task.task_result?.progress_percentage || 0, level: 0, isExpanded: expandedTasks.has(task.task_id), subTasks: [], phases, taskResult: task.task_result }; result.push(mainTask); // 处理子任务(如果有的话) if (task.task_result?.data && Array.isArray(task.task_result.data)) { const subTasks = task.task_result.data.map((subItem: any, subIndex: number) => { // 为子任务计算时间分布 const totalCount = task.task_result.total_count || task.task_result.data.length; const subDuration = duration / totalCount; const subStartTime = taskStartTime - startTime + (subIndex * subDuration); const subEndTime = subStartTime + subDuration; const subPhases = { initialization: subDuration * 0.05, dataLoading: subDuration * 0.1, aiProcessing: subDuration * 0.7, resultGeneration: subDuration * 0.1, dataTransfer: subDuration * 0.03, completion: subDuration * 0.02 }; return { id: subItem.video_id || `${task.task_id}-sub-${subIndex}`, name: `${task.task_name}_item_${subIndex + 1}`, displayName: `${getTaskDisplayName(task.task_name)} - 项目 ${subIndex + 1}`, status: subItem.video_status === 1 ? 'COMPLETED' : subIndex < (task.task_result.completed_count || 0) ? 'COMPLETED' : subIndex === (task.task_result.completed_count || 0) ? 'IN_PROGRESS' : 'PENDING', statusCode: subItem.video_status === 1 ? 200 : subIndex < (task.task_result.completed_count || 0) ? 200 : subIndex === (task.task_result.completed_count || 0) ? 202 : 100, type: getTaskType(task.task_name), dataSize: getSubTaskDataSize(subItem), executionTime: subDuration, startTime: subStartTime, endTime: subEndTime, progress: subItem.video_status === 1 ? 100 : subIndex < (task.task_result.completed_count || 0) ? 100 : subIndex === (task.task_result.completed_count || 0) ? (task.task_result.progress_percentage || 0) : 0, level: 1, parentId: task.task_id, phases: subPhases, taskResult: subItem }; }); mainTask.subTasks = subTasks; // 如果主任务展开,将子任务添加到结果中 if (expandedTasks.has(task.task_id)) { result.push(...subTasks); } } }); return result; }, [tasks, expandedTasks]); // 过滤后的任务执行列表 const filteredTaskExecutions = useMemo(() => { let filtered = taskExecutions; // 按类型过滤 if (filterType !== 'all') { filtered = filtered.filter(task => task.type === filterType); } // 按搜索词过滤 if (searchTerm.trim()) { const searchLower = searchTerm.toLowerCase(); filtered = filtered.filter(task => task.displayName.toLowerCase().includes(searchLower) || task.name.toLowerCase().includes(searchLower) || task.status.toLowerCase().includes(searchLower) || task.type.toLowerCase().includes(searchLower) ); } return filtered; }, [taskExecutions, filterType, searchTerm]); // 辅助函数 function getTaskDisplayName(taskName: string): string { const nameMap: Record = { 'generate_character': '角色生成', 'generate_sketch': '草图生成', 'generate_shot_sketch': '分镜生成', 'generate_video': '视频生成', 'generate_videos': '视频生成', 'generate_music': '音乐生成', 'final_composition': '最终合成' }; return nameMap[taskName] || taskName; } function getTaskMethod(taskName: string): string { return taskName.includes('generate') ? 'POST' : 'GET'; } function getTaskStatusCode(status: string): number { const statusMap: Record = { 'COMPLETED': 200, 'IN_PROGRESS': 202, 'PENDING': 100, 'FAILED': 500 }; return statusMap[status] || 200; } function getTaskType(taskName: string): string { const typeMap: Record = { 'generate_character': 'AI', 'generate_sketch': 'AI', 'generate_shot_sketch': 'AI', 'generate_video': 'Video', 'generate_videos': 'Video', 'generate_music': 'Audio', 'final_composition': 'Comp' }; return typeMap[taskName] || 'Task'; } function getTaskDataSize(task: any): number { const baseSize = 1024; // 1KB base const dataCount = task.task_result?.total_count || 1; const completedCount = task.task_result?.completed_count || 0; return baseSize * completedCount * (Math.random() * 5 + 1); } function getSubTaskDataSize(subItem: any): number { const baseSize = 512; // 0.5KB base for sub-items if (subItem.urls && subItem.urls.length > 0) { // 如果有URL,估算为较大的文件 return baseSize * 10 * (Math.random() * 3 + 1); } return baseSize * (Math.random() * 2 + 1); } // 获取任务进度数量显示 - 与任务执行状态面板完全一致 function getTaskProgressCount(task: any): string { const completed = task.task_result?.completed_count; const total = task.task_result?.total_count || 0; // 如果任务已完成但没有子任务数据,显示 1/1 if (task.task_status === 'COMPLETED' && total === 0) { return '1/1'; } // 如果任务已完成且有子任务数据,确保completed等于total if (task.task_status === 'COMPLETED' && total > 0) { // 如果没有completed_count字段,但任务已完成,说明全部完成 const actualCompleted = completed !== undefined ? completed : total; return `${actualCompleted}/${total}`; } // 其他情况显示实际数据 const actualCompleted = completed || 0; return `${actualCompleted}/${total}`; } // 获取任务进度百分比显示 - 与任务执行状态面板完全一致 function getTaskProgressPercentage(task: any): string { const completed = task.task_result?.completed_count || 0; const total = task.task_result?.total_count || 0; const progress = task.task_result?.progress_percentage || 0; // 如果任务已完成,显示100% if (task.task_status === 'COMPLETED') { return '100%'; } // 如果任务进行中,优先使用API返回的progress_percentage if (task.task_status === 'IN_PROGRESS') { if (progress > 0) { return `${progress}%`; } // 如果没有progress_percentage,根据completed/total计算 if (total > 0) { return `${Math.round((completed / total) * 100)}%`; } } // 其他情况显示0% return '0%'; } // 获取任务状态显示文本 - 与任务执行状态面板完全一致 function getTaskStatusText(status: string): string { switch (status) { case 'COMPLETED': return 'COMPLETED'; case 'IN_PROGRESS': return 'IN_PROGRESS'; case 'FAILED': return 'FAILED'; case 'PENDING': default: return 'PENDING'; } } // 获取任务状态颜色 - 更明显的颜色区分 function getTaskStatusColor(status: string): string { switch (status) { case 'COMPLETED': return 'text-emerald-400'; // 更亮的绿色 case 'IN_PROGRESS': return 'text-cyan-400'; // 更亮的蓝色 case 'FAILED': return 'text-rose-400'; // 更亮的红色 case 'PENDING': default: return 'text-amber-400'; // 黄色表示等待 } } // 获取进度条背景颜色类名 function getProgressBarColor(status: string): string { switch (status) { case 'COMPLETED': return 'bg-emerald-500'; // 绿色 case 'IN_PROGRESS': return 'bg-cyan-500'; // 蓝色 case 'FAILED': return 'bg-rose-500'; // 红色 case 'PENDING': default: return 'bg-amber-500'; // 黄色 } } // 获取任务错误信息 function getTaskErrorInfo(task: any): { hasError: boolean; errorMessage: string; errorCode?: string } { if (task.task_status !== 'FAILED') { return { hasError: false, errorMessage: '' }; } // 从task_result中提取错误信息 const errorMessage = task.task_result?.error_message || task.task_result?.message || task.error_message || '任务执行失败,请重试'; const errorCode = task.task_result?.error_code || task.error_code || 'UNKNOWN_ERROR'; return { hasError: true, errorMessage, errorCode }; } // 处理重试任务 const handleRetryTask = async (taskId: string) => { if (onRetryTask) { try { await onRetryTask(taskId); // 重试成功后可以显示成功提示 } catch (error) { console.error('重试任务失败:', error); // 可以显示错误提示 } } }; // 格式化文件大小 function formatSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } // 智能时间格式化 - 提升用户体验 function formatTime(ms: number): string { if (ms < 1000) { return `${Math.round(ms)}ms`; } const seconds = Math.floor(ms / 1000); // 小于1分钟:显示秒 if (seconds < 60) { return `${seconds}s`; } // 1分钟到59分钟:显示分钟和秒 if (seconds < 3600) { const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; if (remainingSeconds === 0) { return `${minutes}m`; } return `${minutes}m ${remainingSeconds}s`; } // 1小时到23小时:显示小时和分钟 if (seconds < 86400) { const hours = Math.floor(seconds / 3600); const remainingMinutes = Math.floor((seconds % 3600) / 60); if (remainingMinutes === 0) { return `${hours}h`; } return `${hours}h ${remainingMinutes}m`; } // 24小时以上:显示天和小时 const days = Math.floor(seconds / 86400); const remainingHours = Math.floor((seconds % 86400) / 3600); if (remainingHours === 0) { return `${days}d`; } return `${days}d ${remainingHours}h`; } // 获取时间显示的颜色类名(基于时长) function getTimeDisplayColor(ms: number): string { const seconds = Math.floor(ms / 1000); if (ms < 1000) return 'text-gray-400'; // 毫秒 - 灰色 if (seconds < 60) return 'text-gray-200'; // 秒 - 浅灰 if (seconds < 3600) return 'text-white'; // 分钟 - 白色 if (seconds < 86400) return 'text-amber-300'; // 小时 - 琥珀色 return 'text-rose-300'; // 天 - 玫瑰色(警示长时间) } // 估算剩余时间(基于当前进度和已用时间) function estimateRemainingTime(task: any): string | null { if (task.task_status !== 'IN_PROGRESS') return null; const progress = task.task_result?.progress_percentage || 0; if (progress <= 0 || progress >= 100) return null; const now = new Date().getTime(); const startTime = new Date(task.created_at).getTime(); const elapsedTime = now - startTime; // 基于当前进度估算总时间 const estimatedTotalTime = (elapsedTime / progress) * 100; const remainingTime = estimatedTotalTime - elapsedTime; if (remainingTime <= 0) return null; return formatTime(remainingTime); } // 获取状态颜色 function getStatusColor(status: number): string { if (status >= 200 && status < 300) return 'text-green-400'; if (status >= 300 && status < 400) return 'text-yellow-400'; if (status >= 400) return 'text-red-400'; return 'text-blue-400'; } // 展开/折叠任务 const toggleTaskExpansion = (taskId: string) => { setExpandedTasks(prev => { const newSet = new Set(prev); if (newSet.has(taskId)) { newSet.delete(taskId); } else { newSet.add(taskId); } return newSet; }); }; // 计算时间线位置 const maxTime = Math.max(...(filteredTaskExecutions.length > 0 ? filteredTaskExecutions.map((task: TaskExecution) => task.endTime) : [0])); return (
{/* 工具栏 */}

任务执行时间线

{/* 任务统计 */}
总任务: {taskStats.total}
{taskStats.completed}
{taskStats.inProgress}
{taskStats.failed > 0 && (
{taskStats.failed}
)}
{taskStats.pending}
{/* 实时监控控制 */} {onTogglePolling && (
{lastUpdate && ( 最后更新: {timeAgo} )}
)} {/* 搜索框 */}
setSearchTerm(e.target.value)} className="pl-10 pr-10 py-2 bg-gray-800 border border-gray-700 rounded-lg text-sm text-white placeholder-gray-400 focus:outline-none focus:border-cyan-500 focus:ring-2 focus:ring-cyan-500/20 transition-all duration-200 hover:border-gray-600" /> {searchTerm && ( )}
{/* 类型过滤 */}
{/* 重置过滤器按钮 */} {(searchTerm || filterType !== 'all') && ( )}
{/* 主内容区域 */}
{/* 左侧任务列表 */}
{/* 列表头部 */}
状态
{/* 展开/折叠按钮列 */}
任务名称
状态
类型
进度
时间
{/* 任务列表 */}
{filteredTaskExecutions.length === 0 ? (

没有找到匹配的任务

{(searchTerm || filterType !== 'all') && (

尝试调整搜索条件或过滤器

)}
) : ( filteredTaskExecutions.map((task, index) => ( setSelectedTask(selectedTask === task.id ? null : task.id)} initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: index * 0.05 }} > {/* 状态图标 */}
{task.statusCode === 200 && } {task.statusCode === 202 && } {task.statusCode === 100 && } {task.statusCode >= 400 && }
{/* 展开/折叠按钮 */}
{task.level === 0 && task.subTasks && task.subTasks.length > 0 && ( )}
{/* 任务名称 */}
{task.level === 1 && "└ "} {task.displayName} {/* 错误信息和重试按钮 */} {task.statusCode >= 400 && (
{onRetryTask && ( )}
)}
{/* 状态 */}
{getTaskStatusText(task.status)}
{/* 类型 */}
{task.type}
{/* 进度 */}
{task.level === 0 ? getTaskProgressCount(tasks.find((t: any) => t.task_id === task.id) || {}) : task.status === 'IN_PROGRESS' ? `${task.progress}%` : task.status === 'COMPLETED' ? '100%' : '0%' }
{/* 时间 */}
{formatTime(task.executionTime)} {/* 剩余时间估算 */} {task.level === 0 && (() => { const originalTask = tasks.find((t: any) => t.task_id === task.id); const remainingTime = originalTask ? estimateRemainingTime(originalTask) : null; return remainingTime ? ( ~{remainingTime} ) : null; })()}
)) )}
{/* 右侧时间线 */}
时间线视图 (总耗时: {formatTime(maxTime)})
{/* 时间刻度 */}
{Array.from({ length: 6 }, (_, i) => { const timeValue = (maxTime / 5) * i; return ( {formatTime(timeValue)} ); })}
{/* 时间刻度线 */}
{Array.from({ length: 6 }, (_, i) => (
))}
{/* 瀑布图 */}
{filteredTaskExecutions.length === 0 ? (

没有任务数据

) : ( filteredTaskExecutions.map((task) => (
{/* 任务条 */}
= 400 ? "bg-rose-500" : "bg-amber-500", selectedTask === task.id ? "opacity-100" : "opacity-80 hover:opacity-95", task.level === 1 && "opacity-70" )} style={{ left: `${(task.startTime / maxTime) * 100}%`, width: `${Math.max((task.executionTime / maxTime) * 100, 0.5)}%` }} onClick={() => setSelectedTask(task.id)} title={`${task.displayName} 执行时间: ${formatTime(task.executionTime)} 状态: ${getTaskStatusText(task.status)} ${task.status === 'IN_PROGRESS' ? `进度: ${task.progress}%` : ''}`} > {/* 基于状态的单色进度条 */}
{/* 背景条 */}
{/* 进度条 - 根据任务状态和进度显示 */} {task.status === 'IN_PROGRESS' && (
)} {/* 完成状态显示满进度 */} {task.status === 'COMPLETED' && (
)} {/* 失败状态显示部分进度 */} {task.status === 'FAILED' && (
)} {/* 等待状态显示微弱指示 */} {task.status === 'PENDING' && (
)}
{/* 子任务进度指示器 */} {task.level === 1 && task.status === 'IN_PROGRESS' && (
)}
)) )}
{/* 详细信息面板 */} {selectedTask && ( {(() => { const task = taskExecutions.find((t: TaskExecution) => t.id === selectedTask); if (!task) return null; return (

{task.level === 0 ? '主任务详情' : '子任务详情'}

任务名称: {task.displayName}
任务ID: {task.name}
状态: {getTaskStatusText(task.status)}
类型: {task.type}
层级: {task.level === 0 ? '主任务' : '子任务'}
{task.status === 'IN_PROGRESS' && ( <>
进度: {task.progress}%
{(() => { const originalTask = tasks.find((t: any) => t.task_id === task.id); const remainingTime = originalTask ? estimateRemainingTime(originalTask) : null; return remainingTime ? (
预计剩余: {remainingTime}
) : null; })()} )} {task.parentId && (
父任务: {task.parentId}
)} {/* 错误信息显示 */} {task.statusCode >= 400 && (() => { const originalTask = tasks.find((t: any) => t.task_id === task.id); if (originalTask) { const errorInfo = getTaskErrorInfo(originalTask); return (
错误信息
{errorInfo.errorMessage}
{errorInfo.errorCode && (
代码: {errorInfo.errorCode}
)} {onRetryTask && ( )}
); } return null; })()}

执行分析

总时间: {formatTime(task.executionTime)}
AI处理: {formatTime(task.phases.aiProcessing || 0)}
数据大小: {formatSize(task.dataSize)}
{task.level === 0 && task.taskResult?.total_count && (
子任务数: {task.taskResult.total_count}
)} {task.level === 0 && task.taskResult?.completed_count !== undefined && (
已完成: {task.taskResult.completed_count}/{task.taskResult.total_count}
)} {task.level === 1 && task.taskResult?.urls && (
输出文件: {task.taskResult.urls.length} 个
)}
); })()}
)}
); } export default NetworkTimeline;