This commit is contained in:
dengqichen 2025-12-15 14:53:48 +08:00
parent 4ca37a506d
commit 7e9587c6e7
6 changed files with 145 additions and 31 deletions

View File

@ -108,8 +108,8 @@ export const DeploymentRow: React.FC<DeploymentRowProps> = ({
// 展开时启动10秒轮询
useAutoRefresh(loadPods, 10000, isExpanded);
const ready = deployment.readyReplicas ?? 0;
const desired = deployment.replicas ?? 0;
const ready = deployment.readyPodCount ?? 0;
const desired = deployment.actualPodCount ?? 0;
// 重启Deployment
const handleRestart = async () => {
@ -186,7 +186,7 @@ export const DeploymentRow: React.FC<DeploymentRowProps> = ({
}
};
const hasReplicas = (deployment.replicas ?? 0) > 0;
const hasReplicas = (deployment.actualPodCount ?? 0) > 0;
return (
<>
@ -211,15 +211,62 @@ export const DeploymentRow: React.FC<DeploymentRowProps> = ({
) : (
<div className="h-6 w-6" />
)}
<Layers className="h-4 w-4 text-blue-600" />
<span className="font-medium">{deployment.deploymentName}</span>
<Layers className="h-4 w-4 text-blue-600 flex-shrink-0" />
<span className="font-medium whitespace-nowrap">{deployment.deploymentName}</span>
</div>
</td>
<td className="px-6 py-4">
<HealthBar deployment={deployment} />
</td>
<td className="px-6 py-4">
<span className="text-sm font-mono text-gray-700">{ready}/{desired}</span>
<span className="text-sm font-mono text-gray-700 whitespace-nowrap">{ready}/{desired}</span>
</td>
<td className="px-6 py-4">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-1 whitespace-nowrap">
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">
:{deployment.runningPodCount ?? 0}
</span>
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800">
:{deployment.pendingPodCount ?? 0}
</span>
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800">
:{deployment.failedPodCount ?? 0}
</span>
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800">
:{deployment.succeededPodCount ?? 0}
</span>
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800">
:{deployment.unknownPodCount ?? 0}
</span>
</div>
</TooltipTrigger>
<TooltipContent>
<div className="text-xs space-y-1">
<div>Pod总数: {deployment.actualPodCount ?? 0}</div>
<div className="text-green-600">Running: {deployment.runningPodCount ?? 0}</div>
<div className="text-yellow-600">Pending: {deployment.pendingPodCount ?? 0}</div>
<div className="text-red-600">Failed: {deployment.failedPodCount ?? 0}</div>
<div className="text-blue-600">Succeeded: {deployment.succeededPodCount ?? 0}</div>
<div className="text-gray-600">Unknown: {deployment.unknownPodCount ?? 0}</div>
<div className="border-t pt-1 mt-1">Ready: {deployment.readyPodCount ?? 0}</div>
<div>Not Ready: {deployment.notReadyPodCount ?? 0}</div>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</td>
<td className="px-6 py-4">
<div className="text-xs text-gray-600 space-y-1 whitespace-nowrap">
<div>
<span className="font-medium">CPU:</span> {deployment.totalCpuRequest || '-'} / {deployment.totalCpuLimit || '-'}
</div>
<div>
<span className="font-medium">:</span> {deployment.totalMemoryRequest || '-'} / {deployment.totalMemoryLimit || '-'}
</div>
</div>
</td>
<td className="px-6 py-4">
<span className="text-sm text-gray-600 font-mono truncate block max-w-xs" title={deployment.image}>
@ -227,11 +274,13 @@ export const DeploymentRow: React.FC<DeploymentRowProps> = ({
</span>
</td>
<td className="px-6 py-4">
<span className="text-sm text-gray-600">0</span>
<span className={`text-sm font-mono ${(deployment.totalRestartCount ?? 0) > 10 ? 'text-red-600 font-bold' : 'text-gray-600'}`}>
{deployment.totalRestartCount ?? 0}
</span>
</td>
<td className="px-6 py-4">
<span className="text-sm text-gray-600">
{deployment.k8sUpdateTime ? dayjs(deployment.k8sUpdateTime).fromNow() : '-'}
<span className="text-sm text-gray-600 whitespace-nowrap">
{deployment.k8sCreateTime ? dayjs(deployment.k8sCreateTime).format('YYYY-MM-DD HH:mm:ss') : '-'}
</span>
</td>
<td className="px-6 py-4 sticky right-0 bg-background shadow-[-2px_0_4px_rgba(0,0,0,0.05)]" onClick={(e) => e.stopPropagation()}>
@ -321,7 +370,7 @@ export const DeploymentRow: React.FC<DeploymentRowProps> = ({
</>
) : pods.length === 0 ? (
<tr>
<td colSpan={7} className="px-6 py-4 text-center text-gray-500">
<td colSpan={9} className="px-6 py-4 text-center text-gray-500">
Deployment下暂无Pod
</td>
</tr>

View File

@ -31,13 +31,15 @@ export const DeploymentTable: React.FC<DeploymentTableProps> = ({
<table className="w-full caption-bottom text-sm" style={{ minWidth: '1200px' }}>
<TableHeader>
<TableRow>
<TableHead className="w-64"></TableHead>
<TableHead className="w-32"></TableHead>
<TableHead className="w-24"></TableHead>
<TableHead className="w-80"></TableHead>
<TableHead className="w-24"></TableHead>
<TableHead className="w-32"></TableHead>
<TableHead className="w-20 sticky right-0 bg-background shadow-[-2px_0_4px_rgba(0,0,0,0.05)]"></TableHead>
<TableHead style={{ width: '280px', minWidth: '280px' }}></TableHead>
<TableHead style={{ width: '150px', minWidth: '150px' }}></TableHead>
<TableHead style={{ width: '80px', minWidth: '80px' }}></TableHead>
<TableHead style={{ width: '280px', minWidth: '280px' }}>Pod状态</TableHead>
<TableHead style={{ width: '200px', minWidth: '200px' }}></TableHead>
<TableHead style={{ width: '350px', minWidth: '350px' }}></TableHead>
<TableHead style={{ width: '80px', minWidth: '80px' }}></TableHead>
<TableHead style={{ width: '180px', minWidth: '180px' }}></TableHead>
<TableHead className="sticky right-0 bg-background shadow-[-2px_0_4px_rgba(0,0,0,0.05)]" style={{ width: '150px', minWidth: '150px' }}></TableHead>
</TableRow>
</TableHeader>
<TableBody>

View File

@ -1,4 +1,10 @@
import React from 'react';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
import type { K8sDeploymentResponse } from '../types';
import { getDeploymentHealth, HealthStatus } from '../types';
@ -7,8 +13,8 @@ interface HealthBarProps {
}
export const HealthBar: React.FC<HealthBarProps> = ({ deployment }) => {
const ready = deployment.readyReplicas ?? 0;
const desired = deployment.replicas ?? 0;
const ready = deployment.readyPodCount ?? 0;
const desired = deployment.actualPodCount ?? 0;
const percentage = desired > 0 ? (ready / desired) * 100 : 100;
const health = getDeploymentHealth(deployment);
@ -26,13 +32,37 @@ export const HealthBar: React.FC<HealthBarProps> = ({ deployment }) => {
};
return (
<div className="w-full">
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
<div
className={`h-full ${getBarColor()} transition-all duration-300`}
style={{ width: `${percentage}%` }}
/>
</div>
</div>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="w-full cursor-help">
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
<div
className={`h-full ${getBarColor()} transition-all duration-300`}
style={{ width: `${percentage}%` }}
/>
</div>
</div>
</TooltipTrigger>
<TooltipContent>
<div className="text-xs space-y-1">
<div className="font-semibold">Pod状态详情</div>
<div>/: {ready}/{desired}</div>
{deployment.actualPodCount !== undefined && (
<>
<div className="border-t pt-1 mt-1">Pod总数: {deployment.actualPodCount}</div>
<div className="text-green-600">Running: {deployment.runningPodCount ?? 0}</div>
<div className="text-yellow-600">Pending: {deployment.pendingPodCount ?? 0}</div>
<div className="text-red-600">Failed: {deployment.failedPodCount ?? 0}</div>
<div className="text-blue-600">Succeeded: {deployment.succeededPodCount ?? 0}</div>
<div className="text-gray-600">Unknown: {deployment.unknownPodCount ?? 0}</div>
<div className="border-t pt-1 mt-1">Ready: {deployment.readyPodCount ?? 0}</div>
<div>Not Ready: {deployment.notReadyPodCount ?? 0}</div>
</>
)}
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
};

View File

@ -59,8 +59,8 @@ export const PodRow: React.FC<PodRowProps> = ({ pod, deploymentId, onViewLogs })
return (
<>
<tr className="border-t border-gray-100 bg-gray-50/50 hover:bg-gray-100/50">
{/* Pod信息列6列) */}
<td className="px-6 py-4 bg-gray-50/50" colSpan={6}>
{/* Pod信息列8列) */}
<td className="px-6 py-4 bg-gray-50/50" colSpan={8}>
<div className="pl-8 space-y-3">
{/* 第一行Pod名称 + 状态 */}
<div className="flex items-center gap-4">

View File

@ -21,6 +21,9 @@ export const StatsCards: React.FC<StatsCardsProps> = ({ deployments }) => {
if (health === HealthStatus.HEALTHY) healthy++;
else if (health === HealthStatus.WARNING) warning++;
else if (health === HealthStatus.CRITICAL) critical++;
// 累加总重启次数
totalRestarts += deployment.totalRestartCount ?? 0;
});
return { total, healthy, warning, critical, totalRestarts };

View File

@ -104,6 +104,36 @@ export interface K8sDeploymentResponse extends BaseResponse {
k8sUpdateTime?: string;
/** YAML配置 */
yamlConfig?: string;
// Pod统计字段
/** 总重启次数 */
totalRestartCount?: number;
/** 实际Pod总数 */
actualPodCount?: number;
/** Running状态Pod数 */
runningPodCount?: number;
/** Pending状态Pod数 */
pendingPodCount?: number;
/** Failed状态Pod数 */
failedPodCount?: number;
/** Succeeded状态Pod数 */
succeededPodCount?: number;
/** Unknown状态Pod数 */
unknownPodCount?: number;
/** Ready状态Pod数 */
readyPodCount?: number;
/** Not Ready状态Pod数 */
notReadyPodCount?: number;
// Pod资源统计
/** CPU请求总和 */
totalCpuRequest?: string;
/** 内存请求总和 */
totalMemoryRequest?: string;
/** CPU限制总和 */
totalCpuLimit?: string;
/** 内存限制总和 */
totalMemoryLimit?: string;
}
/**
@ -392,8 +422,8 @@ export enum HealthStatus {
* Deployment健康状态
*/
export const getDeploymentHealth = (deployment: K8sDeploymentResponse): HealthStatus => {
const ready = deployment.readyReplicas ?? 0;
const desired = deployment.replicas ?? 0;
const ready = deployment.readyPodCount ?? 0;
const desired = deployment.actualPodCount ?? 0;
if (desired === 0) return HealthStatus.HEALTHY;
if (ready === 0) return HealthStatus.CRITICAL;