dashboard数据面板样式修改

This commit is contained in:
qikongjian 2025-09-04 16:32:58 +08:00
parent 8fa3b16ecd
commit f8a2ac1e34

View File

@ -923,6 +923,45 @@ export function NetworkTimeline({
return formatTime(remainingTime);
}
// 获取任务日志内容(右侧面板展示)
function getTaskLogContent(viewTask: TaskExecution): string {
// 优先从原始任务中提取完整字段
let origin: any | null = null;
if (viewTask.level === 0) {
origin = tasks.find((t: any) => t.task_id === viewTask.id) || null;
} else if (viewTask.level === 1 && viewTask.parentId) {
const parent = tasks.find((t: any) => t.task_id === viewTask.parentId);
origin = parent?.sub_tasks?.find((st: any) => st.task_id === viewTask.id) || null;
}
if (!origin) return '无可用日志';
const resultObj = parseTaskResult(origin.task_result);
const payload: Record<string, any> = {
task_id: origin.task_id,
task_name: origin.task_name,
task_status: origin.task_status,
task_message: origin.task_message || null,
error_message: origin.error_message || resultObj?.error_message || null,
error_traceback: origin.error_traceback || resultObj?.error_traceback || null,
progress_percentage: resultObj?.progress_percentage ?? origin.progress ?? null,
params: origin.task_params || null,
result: resultObj || origin.task_result || null,
timestamps: {
created_at: origin.created_at || null,
updated_at: origin.updated_at || null,
start_time: origin.start_time || null,
end_time: origin.end_time || null,
},
};
try {
return JSON.stringify(payload, null, 2);
} catch {
return typeof origin.task_result === 'string' ? origin.task_result : '无可用日志';
}
}
// 获取状态颜色
function getStatusColor(status: number): string {
if (status >= 200 && status < 300) return 'text-green-400';
@ -950,13 +989,13 @@ export function NetworkTimeline({
return (
<div className={cn("h-full flex flex-col bg-gray-950 overflow-hidden", className)}>
{/* 工具栏 */}
<div className="flex items-center justify-between px-6 py-4 bg-gray-900 border-b border-gray-800 flex-shrink-0">
<div className="flex items-center gap-4">
<div className="flex items-center justify-between px-6 py-3 bg-gray-900 border-b border-gray-800 flex-shrink-0">
<div className="flex items-center gap-3">
<Network className="w-5 h-5 text-cyan-400" />
<h3 className="text-lg font-semibold text-white">线</h3>
{/* 任务统计 */}
<div className="flex items-center gap-3">
<div className="flex items-center gap-2.5">
<div className="flex items-center gap-1 px-3 py-1.5 bg-gray-800 rounded-md">
<span className="text-sm text-gray-400">:</span>
<span className="text-sm font-bold text-white">{taskStats.total}</span>
@ -1083,7 +1122,7 @@ export function NetworkTimeline({
{/* 左侧任务列表 */}
<div className="w-1/2 border-r border-gray-800 flex flex-col min-h-0">
{/* 列表头部 */}
<div className="flex items-center px-4 py-2 bg-gray-800/50 text-xs font-medium text-gray-400 border-b border-gray-800 flex-shrink-0">
<div className="flex items-center px-4 py-1.5 bg-gray-800/50 text-xs font-medium text-gray-400 border-b border-gray-800 flex-shrink-0">
<div className="w-8"></div>
<div className="w-6"></div> {/* 展开/折叠按钮列 */}
<div className="flex-1"></div>
@ -1094,7 +1133,7 @@ export function NetworkTimeline({
</div>
{/* 任务列表 */}
<div className="flex-1 overflow-y-auto min-h-0" style={{ maxHeight: 'calc(100vh - 200px)' }}>
<div className="flex-1 overflow-y-auto min-h-0">
{filteredTaskExecutions.length === 0 ? (
<div className="flex items-center justify-center py-8 text-gray-400">
<div className="text-center">
@ -1110,7 +1149,7 @@ export function NetworkTimeline({
<div
key={task.id}
className={cn(
"flex items-center px-4 py-2 text-sm border-b border-gray-800/30 cursor-pointer hover:bg-gray-800/50 transition-all duration-200 ease-out",
"flex items-center px-4 py-1.5 text-sm border-b border-gray-800/30 cursor-pointer hover:bg-gray-800/50 transition-all duration-200 ease-out",
selectedTask === task.id && "bg-blue-600/20",
task.level === 1 && "ml-4 bg-gray-900/30" // 子任务缩进和背景区分
)}
@ -1118,10 +1157,10 @@ export function NetworkTimeline({
>
{/* 状态图标 */}
<div className="w-8 flex justify-center">
{task.statusCode === 200 && <CheckCircle className="w-5 h-5 text-emerald-400" />}
{task.statusCode === 202 && <Clock className="w-5 h-5 text-cyan-400 animate-pulse" />}
{task.statusCode === 100 && <Pause className="w-5 h-5 text-amber-400" />}
{task.statusCode >= 400 && <XCircle className="w-5 h-5 text-rose-400" />}
{task.statusCode === 200 && <CheckCircle className="w-4 h-4 text-emerald-400" />}
{task.statusCode === 202 && <Clock className="w-4 h-4 text-cyan-400 animate-pulse" />}
{task.statusCode === 100 && <Pause className="w-4 h-4 text-amber-400" />}
{task.statusCode >= 400 && <XCircle className="w-4 h-4 text-rose-400" />}
</div>
{/* 展开/折叠按钮 */}
@ -1341,12 +1380,12 @@ export function NetworkTimeline({
{/* 右侧时间线 */}
<div className="w-1/2 flex flex-col min-h-0">
<div className="px-4 py-3 bg-gray-900/50 border-b border-gray-800 flex-shrink-0">
<div className="px-4 py-2.5 bg-gray-900/50 border-b border-gray-800 flex-shrink-0">
<div className="text-sm font-medium text-gray-300 text-center">
线 (: {formatTime(maxTime)})
</div>
</div>
<div className="flex-1 p-4 overflow-y-auto min-h-0" style={{ maxHeight: 'calc(100vh - 200px)' }}>
<div className="flex-1 p-3.5 overflow-y-auto min-h-0">
{/* 时间刻度 */}
<div className="relative mb-6">
@ -1386,13 +1425,13 @@ export function NetworkTimeline({
filteredTaskExecutions.map((task) => (
<div key={task.id} className={cn(
"relative",
task.level === 0 ? "h-6" : "h-4"
task.level === 0 ? "h-5" : "h-3.5"
)}>
{/* 任务条 */}
<div
className={cn(
"absolute rounded-sm cursor-pointer transition-opacity",
task.level === 0 ? "top-1 h-4" : "top-0.5 h-3",
task.level === 0 ? "top-0.5 h-3.5" : "top-0.5 h-2.5",
task.statusCode === 200 ? "bg-emerald-500" :
task.statusCode === 202 ? "bg-cyan-500" :
task.statusCode >= 400 ? "bg-rose-500" : "bg-amber-500",
@ -1575,23 +1614,48 @@ ${task.status === 'IN_PROGRESS' ? `进度: ${task.progress}%` : ''}
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="border-t border-gray-800 p-4"
className="border-t border-gray-800 p-3"
>
{(() => {
const task = taskExecutions.find((t: TaskExecution) => t.id === selectedTask);
if (!task) return null;
return (
<div className="grid grid-cols-2 gap-6">
<div className="grid grid-cols-2 gap-4">
<div>
<h4 className="text-base font-medium text-white mb-3">
{task.level === 0 ? '主任务详情' : '子任务详情'}
</h4>
{/* 顶部一行:任务关键信息 + 时间信息 */}
{(() => {
const t = getTaskTimes(task);
return (
<div className="flex flex-wrap items-center gap-2 text-xs sm:text-sm mb-2">
<span className="px-2 py-1 bg-gray-800/60 border border-gray-700 rounded text-white">
{task.displayName}
</span>
<span className={cn("px-2 py-1 rounded border", getTaskStatusColor(task.status), "border-gray-700/60 bg-gray-800/40 ")}>{getTaskStatusText(task.status)}</span>
<span className="px-2 py-1 bg-gray-800/40 border border-gray-700 rounded text-gray-300">{task.type}</span>
{t.startTime && (
<span className="px-2 py-1 bg-gray-900/40 border border-gray-800 rounded text-green-400" title={`开始时间: ${formatDateTime(t.startTime)}`}>
{new Date(t.startTime).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' })}
</span>
)}
{t.endTime && (
<span className="px-2 py-1 bg-gray-900/40 border border-gray-800 rounded text-blue-400" title={`结束时间: ${formatDateTime(t.endTime)}`}>
{new Date(t.endTime).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' })}
</span>
)}
{t.startTime && t.endTime && (
<span className="px-2 py-1 bg-gray-900/40 border border-gray-800 rounded text-yellow-400">
{formatTime(task.executionTime)}
</span>
)}
</div>
);
})()}
<div className="space-y-2 text-sm">
<div><span className="text-gray-400">:</span> <span className="text-white">{task.displayName}</span></div>
<div><span className="text-gray-400">ID:</span> <span className="text-white font-mono text-sm">{task.name}</span></div>
<div><span className="text-gray-400">:</span> <span className={getTaskStatusColor(task.status)}>{getTaskStatusText(task.status)}</span></div>
<div><span className="text-gray-400">:</span> <span className="text-white">{task.type}</span></div>
<div><span className="text-gray-400">:</span> <span className="text-white">{task.level === 0 ? '主任务' : '子任务'}</span></div>
{/* 子任务完成状况 */}
@ -1603,28 +1667,7 @@ ${task.status === 'IN_PROGRESS' ? `进度: ${task.progress}%` : ''}
) : null;
})()}
{/* 时间信息显示 */}
{(() => {
const taskTimes = getTaskTimes(task);
return (
<>
<div className="border-t border-gray-700 pt-3 mt-3">
<div className="text-gray-300 font-medium text-sm mb-2"> </div>
<div><span className="text-gray-400">:</span> <span className="text-green-400 text-sm">{formatDateTime(taskTimes.startTime)}</span></div>
<div><span className="text-gray-400">:</span> <span className="text-blue-400 text-sm">{formatDateTime(taskTimes.endTime)}</span></div>
{taskTimes.createdAt && taskTimes.createdAt !== taskTimes.startTime && (
<div><span className="text-gray-400">:</span> <span className="text-gray-300 text-sm">{formatDateTime(taskTimes.createdAt)}</span></div>
)}
{taskTimes.updatedAt && taskTimes.updatedAt !== taskTimes.endTime && (
<div><span className="text-gray-400">:</span> <span className="text-gray-300 text-sm">{formatDateTime(taskTimes.updatedAt)}</span></div>
)}
{taskTimes.startTime && taskTimes.endTime && (
<div><span className="text-gray-400">:</span> <span className="text-yellow-400 text-sm">{formatTime(task.executionTime)}</span></div>
)}
</div>
</>
);
})()}
{/* 时间信息已并入顶部一行,此处不再重复 */}
{task.status === 'IN_PROGRESS' && (
<>
<div><span className="text-gray-400">:</span> <span className="text-white">{task.progress}%</span></div>
@ -1658,21 +1701,21 @@ ${task.status === 'IN_PROGRESS' ? `进度: ${task.progress}%` : ''}
if (!errorInfo.hasError) return null;
return (
<div className="mt-2 p-2 bg-red-600/10 border border-red-600/20 rounded">
<div className="flex items-center gap-1 mb-1">
<div className="mt-2 p-2 bg-red-600/10 border border-red-600/20 rounded flex items-center gap-2 flex-wrap">
<div className="flex items-center gap-1">
<XCircle className="w-3 h-3 text-red-400" />
<span className="text-red-400 font-medium"></span>
<span className="text-red-400 font-medium text-[11px]"></span>
</div>
<div className="text-red-300 text-[10px] break-words">{errorInfo.errorMessage}</div>
<span className="text-red-300 text-[10px] truncate max-w-[40%]" title={errorInfo.errorMessage}>{errorInfo.errorMessage}</span>
{errorInfo.errorCode && (
<div className="text-red-400 text-[10px] font-mono mt-1">: {errorInfo.errorCode}</div>
<span className="text-red-400 text-[10px] font-mono">: {errorInfo.errorCode}</span>
)}
{onRetryTask && (
<button
onClick={() => handleRetryTask(task.id)}
disabled={retryingTasks.has(task.id) || task.status === 'RETRYING'}
className={cn(
"mt-2 flex items-center gap-1 px-2 py-1 text-white text-[10px] rounded transition-colors",
"ml-auto flex items-center gap-1 px-2 py-1 text-white text-[10px] rounded transition-colors",
(retryingTasks.has(task.id) || task.status === 'RETRYING')
? "bg-yellow-600 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700"
@ -1691,21 +1734,15 @@ ${task.status === 'IN_PROGRESS' ? `进度: ${task.progress}%` : ''}
</div>
</div>
<div>
<h4 className="text-base font-medium text-white mb-3"></h4>
<div className="space-y-2 text-sm">
<div><span className="text-gray-400">:</span> <span className="text-white">{formatTime(task.executionTime)}</span></div>
<div><span className="text-gray-400">AI处理:</span> <span className="text-white">{formatTime(task.phases.aiProcessing || 0)}</span></div>
{/* <div><span className="text-gray-400">数据大小:</span> <span className="text-white">{formatSize(task.dataSize)}</span></div> */}
{task.level === 0 && task.taskResult?.total_count && (
<div><span className="text-gray-400">:</span> <span className="text-white">{task.taskResult.total_count}</span></div>
)}
{task.level === 0 && task.taskResult?.completed_count !== undefined && (
<div><span className="text-gray-400">:</span> <span className="text-white">{task.taskResult.completed_count}/{task.taskResult.total_count}</span></div>
)}
{task.level === 1 && task.taskResult?.urls && (
<div><span className="text-gray-400">:</span> <span className="text-white">{task.taskResult.urls.length} </span></div>
)}
</div>
<h4 className="text-base font-medium text-white mb-3"></h4>
{/* <div className="space-y-2 text-sm">
<div className="bg-gray-900/50 border border-gray-800 rounded p-3 min-h-40 max-h-64 overflow-y-auto">
<pre className="text-xs text-gray-300 font-mono whitespace-pre-wrap break-words">
{getTaskLogContent(task)}
</pre>
</div>
<div className="text-xs text-gray-500"></div>
</div> */}
</div>
</div>
);