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秒轮询 // 展开时启动10秒轮询
useAutoRefresh(loadPods, 10000, isExpanded); useAutoRefresh(loadPods, 10000, isExpanded);
const ready = deployment.readyReplicas ?? 0; const ready = deployment.readyPodCount ?? 0;
const desired = deployment.replicas ?? 0; const desired = deployment.actualPodCount ?? 0;
// 重启Deployment // 重启Deployment
const handleRestart = async () => { 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 ( return (
<> <>
@ -211,15 +211,62 @@ export const DeploymentRow: React.FC<DeploymentRowProps> = ({
) : ( ) : (
<div className="h-6 w-6" /> <div className="h-6 w-6" />
)} )}
<Layers className="h-4 w-4 text-blue-600" /> <Layers className="h-4 w-4 text-blue-600 flex-shrink-0" />
<span className="font-medium">{deployment.deploymentName}</span> <span className="font-medium whitespace-nowrap">{deployment.deploymentName}</span>
</div> </div>
</td> </td>
<td className="px-6 py-4"> <td className="px-6 py-4">
<HealthBar deployment={deployment} /> <HealthBar deployment={deployment} />
</td> </td>
<td className="px-6 py-4"> <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>
<td className="px-6 py-4"> <td className="px-6 py-4">
<span className="text-sm text-gray-600 font-mono truncate block max-w-xs" title={deployment.image}> <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> </span>
</td> </td>
<td className="px-6 py-4"> <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>
<td className="px-6 py-4"> <td className="px-6 py-4">
<span className="text-sm text-gray-600"> <span className="text-sm text-gray-600 whitespace-nowrap">
{deployment.k8sUpdateTime ? dayjs(deployment.k8sUpdateTime).fromNow() : '-'} {deployment.k8sCreateTime ? dayjs(deployment.k8sCreateTime).format('YYYY-MM-DD HH:mm:ss') : '-'}
</span> </span>
</td> </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()}> <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 ? ( ) : pods.length === 0 ? (
<tr> <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 Deployment下暂无Pod
</td> </td>
</tr> </tr>

View File

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

View File

@ -1,4 +1,10 @@
import React from 'react'; import React from 'react';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
import type { K8sDeploymentResponse } from '../types'; import type { K8sDeploymentResponse } from '../types';
import { getDeploymentHealth, HealthStatus } from '../types'; import { getDeploymentHealth, HealthStatus } from '../types';
@ -7,8 +13,8 @@ interface HealthBarProps {
} }
export const HealthBar: React.FC<HealthBarProps> = ({ deployment }) => { export const HealthBar: React.FC<HealthBarProps> = ({ deployment }) => {
const ready = deployment.readyReplicas ?? 0; const ready = deployment.readyPodCount ?? 0;
const desired = deployment.replicas ?? 0; const desired = deployment.actualPodCount ?? 0;
const percentage = desired > 0 ? (ready / desired) * 100 : 100; const percentage = desired > 0 ? (ready / desired) * 100 : 100;
const health = getDeploymentHealth(deployment); const health = getDeploymentHealth(deployment);
@ -26,7 +32,10 @@ export const HealthBar: React.FC<HealthBarProps> = ({ deployment }) => {
}; };
return ( return (
<div className="w-full"> <TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="w-full cursor-help">
<div className="h-2 bg-gray-200 rounded-full overflow-hidden"> <div className="h-2 bg-gray-200 rounded-full overflow-hidden">
<div <div
className={`h-full ${getBarColor()} transition-all duration-300`} className={`h-full ${getBarColor()} transition-all duration-300`}
@ -34,5 +43,26 @@ export const HealthBar: React.FC<HealthBarProps> = ({ deployment }) => {
/> />
</div> </div>
</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 ( return (
<> <>
<tr className="border-t border-gray-100 bg-gray-50/50 hover:bg-gray-100/50"> <tr className="border-t border-gray-100 bg-gray-50/50 hover:bg-gray-100/50">
{/* Pod信息列6列) */} {/* Pod信息列8列) */}
<td className="px-6 py-4 bg-gray-50/50" colSpan={6}> <td className="px-6 py-4 bg-gray-50/50" colSpan={8}>
<div className="pl-8 space-y-3"> <div className="pl-8 space-y-3">
{/* 第一行Pod名称 + 状态 */} {/* 第一行Pod名称 + 状态 */}
<div className="flex items-center gap-4"> <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++; if (health === HealthStatus.HEALTHY) healthy++;
else if (health === HealthStatus.WARNING) warning++; else if (health === HealthStatus.WARNING) warning++;
else if (health === HealthStatus.CRITICAL) critical++; else if (health === HealthStatus.CRITICAL) critical++;
// 累加总重启次数
totalRestarts += deployment.totalRestartCount ?? 0;
}); });
return { total, healthy, warning, critical, totalRestarts }; return { total, healthy, warning, critical, totalRestarts };

View File

@ -104,6 +104,36 @@ export interface K8sDeploymentResponse extends BaseResponse {
k8sUpdateTime?: string; k8sUpdateTime?: string;
/** YAML配置 */ /** YAML配置 */
yamlConfig?: string; 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健康状态 * Deployment健康状态
*/ */
export const getDeploymentHealth = (deployment: K8sDeploymentResponse): HealthStatus => { export const getDeploymentHealth = (deployment: K8sDeploymentResponse): HealthStatus => {
const ready = deployment.readyReplicas ?? 0; const ready = deployment.readyPodCount ?? 0;
const desired = deployment.replicas ?? 0; const desired = deployment.actualPodCount ?? 0;
if (desired === 0) return HealthStatus.HEALTHY; if (desired === 0) return HealthStatus.HEALTHY;
if (ready === 0) return HealthStatus.CRITICAL; if (ready === 0) return HealthStatus.CRITICAL;