diff --git a/frontend/src/pages/Dashboard/index.tsx b/frontend/src/pages/Dashboard/index.tsx index a25bb8fc..a529e156 100644 --- a/frontend/src/pages/Dashboard/index.tsx +++ b/frontend/src/pages/Dashboard/index.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Skeleton } from "@/components/ui/skeleton"; import { cn } from "@/lib/utils"; import { Package, @@ -11,7 +12,11 @@ import { GitBranch, Users, Server, - CheckCircle2 + CheckCircle2, + XCircle, + Clock, + TrendingUp, + History } from "lucide-react"; import { useToast } from '@/components/ui/use-toast'; import { getDeployEnvironments, startDeployment } from './service'; @@ -26,6 +31,63 @@ const LoadingState = () => ( ); +// 格式化持续时间 +const formatDuration = (ms?: number) => { + if (!ms) return '-'; + const seconds = Math.floor(ms / 1000); + const minutes = Math.floor(seconds / 60); + if (minutes > 0) { + return `${minutes}分${seconds % 60}秒`; + } + return `${seconds}秒`; +}; + +// 格式化时间 +const formatTime = (timeStr?: string) => { + if (!timeStr) return '-'; + try { + const date = new Date(timeStr); + return date.toLocaleString('zh-CN', { + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit' + }); + } catch { + return timeStr; + } +}; + +// 获取状态图标和颜色 +const getStatusIcon = (status?: string) => { + switch (status) { + case 'SUCCESS': + return { icon: CheckCircle2, color: 'text-green-600' }; + case 'FAILED': + return { icon: XCircle, color: 'text-red-600' }; + case 'RUNNING': + return { icon: Loader2, color: 'text-blue-600' }; + default: + return { icon: Clock, color: 'text-gray-400' }; + } +}; + +// 获取状态文本 +const getStatusText = (status?: string) => { + switch (status) { + case 'SUCCESS': + return '成功'; + case 'FAILED': + return '失败'; + case 'RUNNING': + return '运行中'; + case 'CANCELLED': + return '已取消'; + default: + return '未知'; + } +}; + const Dashboard: React.FC = () => { const { toast } = useToast(); const [loading, setLoading] = useState(true); @@ -257,45 +319,214 @@ const Dashboard: React.FC = () => {
-

{app.applicationName}

- - {app.applicationCode} - + {app.applicationName ? ( +

{app.applicationName}

+ ) : ( + + )} + {app.applicationCode ? ( + + {app.applicationCode} + + ) : ( + + )}
+ {/* 分支 */}
- {app.branch} + {app.branch ? ( + {app.branch} + ) : ( + + )}
+ {/* 工作流 */}
- {app.workflowDefinitionName || '未配置'} + {app.workflowDefinitionName ? ( + {app.workflowDefinitionName} + ) : ( + + )}
- {app.deploySystemName && ( + {/* Jenkins */} + {app.deploySystemName ? (
{app.deploySystemName}
+ ) : ( +
+ + +
+ )} +
+ + {/* 部署统计信息 */} +
+
+ {/* 总次数 */} +
+ {app.deployStatistics ? ( + <> +
+ + {app.deployStatistics.totalCount ?? 0} +
+
总次数
+ + ) : ( + <> + + + + )} +
+ + {/* 成功次数 */} +
+ {app.deployStatistics ? ( + <> +
+ + {app.deployStatistics.successCount ?? 0} +
+
成功
+ + ) : ( + <> + + + + )} +
+ + {/* 失败次数 */} +
+ {app.deployStatistics ? ( + <> +
+ + {app.deployStatistics.failedCount ?? 0} +
+
失败
+ + ) : ( + <> + + + + )} +
+
+ + {/* 最近部署信息 */} + {app.deployStatistics ? ( +
+ + {app.deployStatistics.lastDeployTime ? ( + <> + 最近: {formatTime(app.deployStatistics.lastDeployTime)} + {app.deployStatistics.lastDeployBy ? ( + by {app.deployStatistics.lastDeployBy} + ) : ( + + )} + {app.deployStatistics.latestStatus ? (() => { + const { icon: StatusIcon, color } = getStatusIcon(app.deployStatistics.latestStatus); + return ( + + + {getStatusText(app.deployStatistics.latestStatus)} + + ); + })() : ( + + )} + + ) : ( + + )} +
+ ) : ( +
+ + +
+ )} + + {/* 最近部署记录 */} + {app.recentDeployRecords && app.recentDeployRecords.length > 0 ? ( +
+
+ + 最近记录 +
+ {app.recentDeployRecords.slice(0, 2).map((record) => { + const { icon: StatusIcon, color } = getStatusIcon(record.status); + return ( +
+
+ + {record.startTime ? ( + <> + {formatTime(record.startTime)} + {record.deployRemark && ( + - {record.deployRemark} + )} + + ) : ( + + )} +
+ {record.duration ? ( + + {formatDuration(record.duration)} + + ) : ( + + )} +
+ ); + })} +
+ ) : ( +
+
+ + 最近记录 +
+ {/* 显示2条骨架记录 */} + {[1, 2].map((i) => ( +
+ + +
+ ))} +
)}