'use client'; import React, { useState, useEffect } from 'react'; import { getProjectTaskList, retryTask, TaskItem } from '@/api/video_flow'; import { cn } from '@/public/lib/utils'; interface TaskDetailTableProps { projectIds: string[]; timeRange: number; } export default function TaskDetailTable({ projectIds, timeRange }: TaskDetailTableProps) { const [tasks, setTasks] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [sortBy, setSortBy] = useState<'created_at' | 'updated_at' | 'task_name' | 'task_status'>('updated_at'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); const [filterStatus, setFilterStatus] = useState('all'); const [searchTerm, setSearchTerm] = useState(''); const [currentPage, setCurrentPage] = useState(1); const [retryingTasks, setRetryingTasks] = useState>(new Set()); const itemsPerPage = 20; // 获取任务列表 const fetchTasks = async () => { if (projectIds.length === 0) { setTasks([]); return; } setLoading(true); setError(null); try { const allTasks: TaskItem[] = []; // 并行获取所有项目的任务 const promises = projectIds.map(async (projectId) => { try { const response = await getProjectTaskList({ project_id: projectId }); if (response.code === 0 && response.data) { return response.data.map(task => ({ ...task, project_id: projectId })); } return []; } catch (err) { console.error(`获取项目 ${projectId} 任务失败:`, err); return []; } }); const results = await Promise.all(promises); results.forEach(projectTasks => { allTasks.push(...projectTasks); }); setTasks(allTasks); } catch (err: any) { console.error('获取任务列表失败:', err); setError(err.message || '获取任务列表失败'); } finally { setLoading(false); } }; // 重试任务 const handleRetryTask = async (taskId: string) => { if (retryingTasks.has(taskId)) return; setRetryingTasks(prev => new Set(prev).add(taskId)); try { const response = await retryTask({ task_id: taskId }); if (response.code === 0 && response.data?.success) { // 乐观更新任务状态 setTasks(prevTasks => prevTasks.map(task => task.task_id === taskId ? { ...task, task_status: 'IN_PROGRESS' as const } : task ) ); // 延迟刷新获取最新状态 setTimeout(() => { fetchTasks(); }, 2000); } else { throw new Error(response.message || '重试失败'); } } catch (err: any) { console.error('重试任务失败:', err); alert(`重试任务失败: ${err.message}`); } finally { setRetryingTasks(prev => { const newSet = new Set(prev); newSet.delete(taskId); return newSet; }); } }; // 过滤和排序任务 const filteredAndSortedTasks = React.useMemo(() => { let filtered = tasks; // 状态过滤 if (filterStatus !== 'all') { filtered = filtered.filter(task => task.task_status === filterStatus); } // 搜索过滤 if (searchTerm) { filtered = filtered.filter(task => task.task_name.toLowerCase().includes(searchTerm.toLowerCase()) || task.task_id.toLowerCase().includes(searchTerm.toLowerCase()) || task.task_message.toLowerCase().includes(searchTerm.toLowerCase()) ); } // 排序 filtered.sort((a, b) => { let aValue: any = a[sortBy]; let bValue: any = b[sortBy]; if (sortBy === 'created_at' || sortBy === 'updated_at') { aValue = new Date(aValue).getTime(); bValue = new Date(bValue).getTime(); } if (sortOrder === 'asc') { return aValue > bValue ? 1 : -1; } else { return aValue < bValue ? 1 : -1; } }); return filtered; }, [tasks, filterStatus, searchTerm, sortBy, sortOrder]); // 分页 const paginatedTasks = React.useMemo(() => { const startIndex = (currentPage - 1) * itemsPerPage; return filteredAndSortedTasks.slice(startIndex, startIndex + itemsPerPage); }, [filteredAndSortedTasks, currentPage]); const totalPages = Math.ceil(filteredAndSortedTasks.length / itemsPerPage); useEffect(() => { fetchTasks(); }, [projectIds, timeRange]); useEffect(() => { setCurrentPage(1); }, [filterStatus, searchTerm]); const getStatusColor = (status: string) => { switch (status) { case 'COMPLETED': return 'text-emerald-400 bg-emerald-500/10'; case 'IN_PROGRESS': return 'text-blue-400 bg-blue-500/10'; case 'PENDING': return 'text-yellow-400 bg-yellow-500/10'; case 'FAILED': return 'text-red-400 bg-red-500/10'; default: return 'text-gray-400 bg-gray-500/10'; } }; const getStatusText = (status: string) => { switch (status) { case 'COMPLETED': return '已完成'; case 'IN_PROGRESS': return '进行中'; case 'PENDING': return '等待中'; case 'FAILED': return '失败'; default: return status; } }; const formatDuration = (startTime: string, endTime?: string) => { const start = new Date(startTime); const end = endTime ? new Date(endTime) : new Date(); const duration = end.getTime() - start.getTime(); const minutes = Math.floor(duration / 60000); const seconds = Math.floor((duration % 60000) / 1000); if (minutes > 0) { return `${minutes}分${seconds}秒`; } return `${seconds}秒`; }; return (
{/* 表格头部控制 */}

任务详情列表

{/* 搜索框 */}
setSearchTerm(e.target.value)} className="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded text-white placeholder-gray-400 text-sm focus:outline-none focus:border-blue-500" />
{/* 状态过滤 */} {/* 排序 */}
{/* 统计信息 */}
显示 {paginatedTasks.length} / {filteredAndSortedTasks.length} 个任务 {projectIds.length > 1 && ` (来自 ${projectIds.length} 个项目)`}
{/* 错误提示 */} {error && (
{error}
)} {/* 表格内容 */}
{loading ? (
加载任务数据...
) : paginatedTasks.length === 0 ? (
📋
{filteredAndSortedTasks.length === 0 ? '暂无任务数据' : '没有匹配的任务'}
) : ( {paginatedTasks.map((task, index) => ( ))}
任务信息 状态 进度 耗时 操作
{task.task_name}
{task.task_id}
{task.task_message && (
{task.task_message}
)}
{getStatusText(task.task_status)} {task.task_result?.progress_percentage !== undefined ? (
{task.task_result.progress_percentage.toFixed(1)}%
) : ( - )}
{formatDuration(task.created_at, task.updated_at)}
{new Date(task.updated_at).toLocaleString()}
{task.task_status === 'FAILED' && ( )}
)}
{/* 分页控制 */} {totalPages > 1 && (
第 {currentPage} 页,共 {totalPages} 页
)}
); }