diff --git a/components/dashboard/network-timeline.tsx b/components/dashboard/network-timeline.tsx index 3e47858..12a217d 100644 --- a/components/dashboard/network-timeline.tsx +++ b/components/dashboard/network-timeline.tsx @@ -13,7 +13,9 @@ import { XCircle, Info, Activity, - Pause + Pause, + Copy, + Check } from 'lucide-react'; import { cn } from '@/public/lib/utils'; @@ -72,6 +74,7 @@ export function NetworkTimeline({ const [expandedTasks, setExpandedTasks] = useState>(new Set()); const [timeAgo, setTimeAgo] = useState(''); const [retryingTasks, setRetryingTasks] = useState>(new Set()); + const [copiedTaskId, setCopiedTaskId] = useState(null); // 自动清理重试状态:当任务状态不再是RETRYING时,移除重试状态 useEffect(() => { @@ -843,6 +846,34 @@ export function NetworkTimeline({ } }; + // 复制错误信息到剪贴板 + const copyErrorInfo = async (taskId: string, errorMessage: string, errorCode?: string) => { + try { + const errorText = `任务ID: ${taskId}\n错误信息: ${errorMessage}${errorCode ? `\n错误代码: ${errorCode}` : ''}`; + await navigator.clipboard.writeText(errorText); + + // 显示复制成功反馈 + setCopiedTaskId(taskId); + setTimeout(() => { + setCopiedTaskId(null); + }, 2000); + } catch (error) { + console.error('复制失败:', error); + // 降级方案:使用传统的复制方法 + const textArea = document.createElement('textarea'); + textArea.value = `任务ID: ${taskId}\n错误信息: ${errorMessage}${errorCode ? `\n错误代码: ${errorCode}` : ''}`; + document.body.appendChild(textArea); + textArea.select(); + document.execCommand('copy'); + document.body.removeChild(textArea); + + setCopiedTaskId(taskId); + setTimeout(() => { + setCopiedTaskId(null); + }, 2000); + } + }; + // 格式化文件大小 function formatSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; @@ -1177,7 +1208,14 @@ export function NetworkTimeline({
类型
进度
时间信息
-
时间线
+
+
+ 时间线 + + (总时长: {formatTime(projectDurationMs)}) + +
+
{/* 任务列表 */} @@ -1197,11 +1235,9 @@ export function NetworkTimeline({
setSelectedTask(selectedTask === task.id ? null : task.id)} > {/* 状态图标 */}
@@ -1214,14 +1250,9 @@ export function NetworkTimeline({ {/* 展开/折叠按钮 */}
{task.level === 0 && task.subTasks && task.subTasks.length > 0 && ( - { - e.stopPropagation(); - toggleTaskExpansion(task.id); - }} - className="p-0.5 hover:bg-gray-700 rounded text-gray-400 hover:text-white transition-colors duration-150" + - + )}
{/* 任务名称 */} -
+
{ + // 如果是主任务且有子任务,切换展开状态 + if (task.level === 0 && task.subTasks && task.subTasks.length > 0) { + toggleTaskExpansion(task.id); + } + // 切换选中状态 + setSelectedTask(selectedTask === task.id ? null : task.id); + }} + >
{/* 错误标题 */} -
- -

任务执行失败

+
+
+ +

任务执行失败

+
+ {/* 复制按钮 */} + {(() => { + let errorInfo; + if (task.level === 0) { + const originalTask = tasks.find((t: any) => t.task_id === task.id); + if (!originalTask) return null; + errorInfo = getTaskErrorInfo(originalTask); + } else { + errorInfo = getSubTaskErrorInfo(task.id); + } + + if (!errorInfo.hasError) return null; + + return ( + + ); + })()}
{/* 错误详情 */} @@ -1609,24 +1694,50 @@ export function NetworkTimeline({ {errorInfo.errorCode && ( 代码: {errorInfo.errorCode} )} - {onRetryTask && ( +
+ {/* 复制错误信息按钮 */} - )} + {/* 重试按钮 */} + {onRetryTask && ( + + )} +
); })()}