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) => (
+
+
+
+
+ ))}
+
)}