import { useState, useEffect } from "react"; import { useParams, Link } from "react-router-dom"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Progress } from "@/components/ui/progress"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { ArrowLeft, Activity, AlertTriangle, CheckCircle, Clock, FileText, Calendar, GitBranch, Shield, Bug, TrendingUp, Download, Code, Lightbulb, Info, Zap } from "lucide-react"; import { api } from "@/shared/config/database"; import type { AuditTask, AuditIssue } from "@/shared/types"; import { toast } from "sonner"; import ExportReportDialog from "@/components/reports/ExportReportDialog"; import { calculateTaskProgress } from "@/shared/utils/utils"; // AI解释解析函数 function parseAIExplanation(aiExplanation: string) { try { const parsed = JSON.parse(aiExplanation); // 检查是否有xai字段 if (parsed.xai) { return parsed.xai; } // 检查是否直接包含what, why, how字段 if (parsed.what || parsed.why || parsed.how) { return parsed; } // 如果都没有,返回null表示无法解析 return null; } catch (error) { // JSON解析失败,返回null return null; } } // 问题列表组件 function IssuesList({ issues }: { issues: AuditIssue[] }) { const getSeverityColor = (severity: string) => { switch (severity) { case 'critical': return 'bg-red-100 text-red-800 border-red-200'; case 'high': return 'bg-orange-100 text-orange-800 border-orange-200'; case 'medium': return 'bg-yellow-100 text-yellow-800 border-yellow-200'; case 'low': return 'bg-blue-100 text-blue-800 border-blue-200'; default: return 'bg-gray-100 text-gray-800 border-gray-200'; } }; const getTypeIcon = (type: string) => { switch (type) { case 'security': return ; case 'bug': return ; case 'performance': return ; case 'style': return ; case 'maintainability': return ; default: return ; } }; const criticalIssues = issues.filter(issue => issue.severity === 'critical'); const highIssues = issues.filter(issue => issue.severity === 'high'); const mediumIssues = issues.filter(issue => issue.severity === 'medium'); const lowIssues = issues.filter(issue => issue.severity === 'low'); const renderIssue = (issue: AuditIssue, index: number) => (
{getTypeIcon(issue.issue_type)}

{issue.title}

{issue.file_path}
{issue.line_number && (
📍 第 {issue.line_number} 行 {issue.column_number && ,第 {issue.column_number} 列}
)}
{issue.severity === 'critical' ? '严重' : issue.severity === 'high' ? '高' : issue.severity === 'medium' ? '中等' : '低'}
{issue.description && (
问题详情

{issue.description}

)} {issue.code_snippet && (
CODE_SNIPPET
{issue.line_number && ( LINE: {issue.line_number} )}
              {issue.code_snippet}
            
)}
{issue.suggestion && (
修复建议

{issue.suggestion}

)} {issue.ai_explanation && (() => { const parsedExplanation = parseAIExplanation(issue.ai_explanation); if (parsedExplanation) { return (
AI 解释
{parsedExplanation.what && (
问题: {parsedExplanation.what}
)} {parsedExplanation.why && (
原因: {parsedExplanation.why}
)} {parsedExplanation.how && (
方案: {parsedExplanation.how}
)} {parsedExplanation.learn_more && ( )}
); } else { // 如果无法解析JSON,回退到原始显示方式 return (
AI 解释

{issue.ai_explanation}

); } })()}
); if (issues.length === 0) { return (

代码质量优秀!

恭喜!没有发现任何问题

您的代码通过了所有质量检查,包括安全性、性能、可维护性等各个方面的评估。

); } return ( 全部 ({issues.length}) 严重 ({criticalIssues.length}) 高 ({highIssues.length}) 中等 ({mediumIssues.length}) 低 ({lowIssues.length}) {issues.map((issue, index) => renderIssue(issue, index))} {criticalIssues.length > 0 ? ( criticalIssues.map((issue, index) => renderIssue(issue, index)) ) : (

没有发现严重问题

代码在严重级别的检查中表现良好

)}
{highIssues.length > 0 ? ( highIssues.map((issue, index) => renderIssue(issue, index)) ) : (

没有发现高优先级问题

代码在高优先级检查中表现良好

)}
{mediumIssues.length > 0 ? ( mediumIssues.map((issue, index) => renderIssue(issue, index)) ) : (

没有发现中等优先级问题

代码在中等优先级检查中表现良好

)}
{lowIssues.length > 0 ? ( lowIssues.map((issue, index) => renderIssue(issue, index)) ) : (

没有发现低优先级问题

代码在低优先级检查中表现良好

)}
); } export default function TaskDetail() { const { id } = useParams<{ id: string }>(); const [task, setTask] = useState(null); const [issues, setIssues] = useState([]); const [loading, setLoading] = useState(true); const [exportDialogOpen, setExportDialogOpen] = useState(false); useEffect(() => { if (id) { loadTaskDetail(); } }, [id]); // 对于运行中或等待中的任务,静默更新进度(不触发loading状态) useEffect(() => { if (!task || !id) { return; } // 运行中或等待中的任务需要定时更新 if (task.status === 'running' || task.status === 'pending') { const intervalId = setInterval(async () => { try { // 静默获取任务数据,不触发loading状态 const [taskData, issuesData] = await Promise.all([ api.getAuditTaskById(id), api.getAuditIssues(id) ]); // 只有数据真正变化时才更新状态 if (taskData && ( taskData.status !== task.status || taskData.scanned_files !== task.scanned_files || taskData.issues_count !== task.issues_count )) { setTask(taskData); setIssues(issuesData); } } catch (error) { console.error('静默更新任务失败:', error); } }, 3000); // 每3秒静默更新一次 return () => clearInterval(intervalId); } }, [task?.status, task?.scanned_files, id]); const loadTaskDetail = async () => { if (!id) return; try { setLoading(true); const [taskData, issuesData] = await Promise.all([ api.getAuditTaskById(id), api.getAuditIssues(id) ]); setTask(taskData); setIssues(issuesData); } catch (error) { console.error('Failed to load task detail:', error); toast.error("加载任务详情失败"); } finally { setLoading(false); } }; const getStatusColor = (status: string) => { switch (status) { case 'completed': return 'bg-green-100 text-green-800'; case 'running': return 'bg-red-50 text-red-800'; case 'failed': return 'bg-red-100 text-red-900'; case 'cancelled': return 'bg-gray-100 text-gray-800'; default: return 'bg-gray-100 text-gray-800'; } }; const getStatusIcon = (status: string) => { switch (status) { case 'completed': return ; case 'running': return ; case 'failed': return ; case 'cancelled': return ; default: return ; } }; const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('zh-CN', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); }; if (loading) { return (
); } if (!task) { return (

任务不存在

请检查任务ID是否正确

); } // 使用公共函数计算进度百分比 const progressPercentage = calculateTaskProgress(task.scanned_files, task.total_files); return (
{/* 页面标题 */}

任务详情

{task.project?.name || '未知项目'} - 审计任务

{getStatusIcon(task.status)} {task.status === 'completed' ? '已完成' : task.status === 'running' ? '运行中' : task.status === 'failed' ? '失败' : task.status === 'cancelled' ? '已取消' : '等待中'} {/* 已完成的任务显示导出按钮 */} {task.status === 'completed' && ( )}
{/* 任务概览 */}

扫描进度

{progressPercentage}%

发现问题

{task.issues_count}

质量评分

{task.quality_score.toFixed(1)}

代码行数

{task.total_lines.toLocaleString()}

{/* 任务信息 */}

任务信息

任务类型

{task.task_type === 'repository' ? '仓库审计任务' : '即时分析任务'}

目标分支

{task.branch_name || '默认分支'}

创建时间

{formatDate(task.created_at)}

{task.completed_at && (

完成时间

{formatDate(task.completed_at)}

)}
{/* 排除模式 */} {task.exclude_patterns && (

排除模式

{JSON.parse(task.exclude_patterns).map((pattern: string) => ( {pattern} ))}
)} {/* 扫描配置 */} {task.scan_config && (

扫描配置

                      {JSON.stringify(JSON.parse(task.scan_config), null, 2)}
                    
)}

项目信息

{task.project ? ( <>

项目名称

{task.project.name}
{task.project.description && (

项目描述

{task.project.description}

)}

仓库类型

{task.project.repository_type?.toUpperCase() || 'OTHER'}

{task.project.programming_languages && (

编程语言

{JSON.parse(task.project.programming_languages).map((lang: string) => ( {lang} ))}
)} ) : (

项目信息不可用

)}
{/* 问题列表 */} {issues.length > 0 && (

发现的问题 ({issues.length})

)} {/* 导出报告对话框 */} {task && ( )}
); }