1.30
This commit is contained in:
parent
4ca37a506d
commit
7e9587c6e7
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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,13 +32,37 @@ export const HealthBar: React.FC<HealthBarProps> = ({ deployment }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<TooltipProvider>
|
||||||
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
|
<Tooltip>
|
||||||
<div
|
<TooltipTrigger asChild>
|
||||||
className={`h-full ${getBarColor()} transition-all duration-300`}
|
<div className="w-full cursor-help">
|
||||||
style={{ width: `${percentage}%` }}
|
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
|
||||||
/>
|
<div
|
||||||
</div>
|
className={`h-full ${getBarColor()} transition-all duration-300`}
|
||||||
</div>
|
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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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">
|
||||||
|
|||||||
@ -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 };
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user