refactor: 全局重命名 k8s → kubernetes (前后端+路由+表名+文件)
后端: - pkg/k8sclient → pkg/kubernetesclient - models/k8s.go → models/kubernetes.go - handlers/k8s.go → handlers/kubernetes.go - K8sCluster/K8sNode/.. → KubernetesCluster/KubernetesNode/.. - K8sHandler → KubernetesHandler - 路由: /k8s-clusters → /kubernetes-clusters - 路由: /k8s/:code/namespace/:ns/... → /kubernetes/:code/namespace/:ns/... (Kuboard风格) - 表前缀: container_k8s_ → container_kubernetes_ 前端: - components/k8s/ → components/kubernetes/ - K8sClusters → KubernetesClusters - K8sWorkloads → KubernetesWorkloads - K8sNodes → KubernetesNodes - k8sApi → kubernetesApi - 路由前缀: /k8s/ → /kubernetes/ 测试验证: ✅ /kubernetes/k8s-cluster-liren/namespace/kube-system/deployments → 3 items ✅ /kubernetes/k8s-cluster-liren/namespace/kube-system/pods → 7 items ✅ /kubernetes/k8s-cluster-liren/namespace/kube-system/services → 4 items ✅ /kubernetes/k8s-cluster-liren/namespace/kube-system/configmaps → 6 items ✅ /kubernetes/k8s-cluster-liren/namespace/kube-system/secrets → 1 item ✅ /kubernetes/k8s-cluster-liren/namespaces → 4 items
This commit is contained in:
1 parent
f7b667b878
commit
cac1414917
27 files changed
+779
-284
No files matched your search
@@ -0,0 +1,489 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Cloud, XCircle } from 'lucide-react';
|
||||
import { kubernetesApi } from '@/services/api';
|
||||
import {
|
||||
AreaChart,
|
||||
Area,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
ResponsiveContainer,
|
||||
} from 'recharts';
|
||||
|
||||
// 模拟资源使用趋势数据
|
||||
const resourceData = [
|
||||
{ time: '00:00', cpu: 45, memory: 52 },
|
||||
{ time: '04:00', cpu: 38, memory: 48 },
|
||||
{ time: '08:00', cpu: 62, memory: 68 },
|
||||
{ time: '12:00', cpu: 78, memory: 82 },
|
||||
{ time: '16:00', cpu: 72, memory: 76 },
|
||||
{ time: '20:00', cpu: 58, memory: 64 },
|
||||
{ time: '24:00', cpu: 48, memory: 56 },
|
||||
];
|
||||
|
||||
export function KubernetesClusterDetail({ cluster, onClose }: { cluster: any; onClose: () => void }) {
|
||||
const [nodes, setNodes] = useState<any[]>([]);
|
||||
const [pods, setPods] = useState<any[]>([]);
|
||||
const [deployments, setDeployments] = useState<any[]>([]);
|
||||
const [services, setServices] = useState<any[]>([]);
|
||||
const [ingresses, setIngresses] = useState<any[]>([]);
|
||||
|
||||
const loadClusterDetail = async (clusterId: string) => {
|
||||
try {
|
||||
// 后端 ListNodes/ListPods/etc 返回 data 直接为数组(非 {list,total})
|
||||
const [nodesRes, podsRes, depRes, svcRes, ingRes] = await Promise.all([
|
||||
kubernetesApi.getNodes(clusterId).catch(() => []),
|
||||
kubernetesApi.getPods(clusterId).catch(() => []),
|
||||
kubernetesApi.getDeployments(clusterId).catch(() => []),
|
||||
kubernetesApi.getServices(clusterId).catch(() => []),
|
||||
kubernetesApi.getIngresses(clusterId).catch(() => []),
|
||||
]);
|
||||
const toArray = (v: any) => (Array.isArray(v) ? v : (v?.list || []));
|
||||
setNodes(toArray(nodesRes));
|
||||
setPods(toArray(podsRes));
|
||||
setDeployments(toArray(depRes));
|
||||
setServices(toArray(svcRes));
|
||||
setIngresses(toArray(ingRes));
|
||||
} catch (e) {
|
||||
console.error('加载集群详情失败:', e);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (cluster?.id) {
|
||||
loadClusterDetail(cluster.id);
|
||||
}
|
||||
}, [cluster?.id]);
|
||||
|
||||
return (
|
||||
<Card className="bg-slate-900 border-slate-800">
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<Cloud className="h-6 w-6 text-blue-400" />
|
||||
<div>
|
||||
<CardTitle className="text-slate-100">{cluster.name}</CardTitle>
|
||||
<CardDescription className="text-slate-500">
|
||||
{cluster.version} · {cluster.node_count || 0} 节点 · 创建于{' '}
|
||||
{cluster.created_at ? new Date(cluster.created_at).toLocaleDateString() : '-'}
|
||||
</CardDescription>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-slate-400 hover:text-slate-100"
|
||||
onClick={onClose}
|
||||
>
|
||||
<XCircle className="h-4 w-4 mr-2" />
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Tabs defaultValue="overview" className="w-full">
|
||||
<TabsList className="bg-slate-800 border border-slate-700 flex-wrap h-auto">
|
||||
<TabsTrigger value="overview" className="data-[state=active]:bg-slate-700">
|
||||
概览
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="nodes" className="data-[state=active]:bg-slate-700">
|
||||
节点 ({nodes.length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="pods" className="data-[state=active]:bg-slate-700">
|
||||
Pod ({pods.length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="deployments" className="data-[state=active]:bg-slate-700">
|
||||
Deployment ({deployments.length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="services" className="data-[state=active]:bg-slate-700">
|
||||
Service ({services.length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="ingresses" className="data-[state=active]:bg-slate-700">
|
||||
Ingress ({ingresses.length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="events" className="data-[state=active]:bg-slate-700">
|
||||
事件
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="overview" className="mt-4 space-y-4">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<Card className="bg-slate-800/50 border-slate-700">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm text-slate-400">资源使用趋势</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="h-[200px]">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<AreaChart data={resourceData}>
|
||||
<defs>
|
||||
<linearGradient id="colorCpu" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#3b82f6" stopOpacity={0.3} />
|
||||
<stop offset="95%" stopColor="#3b82f6" stopOpacity={0} />
|
||||
</linearGradient>
|
||||
<linearGradient id="colorMemory" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#10b981" stopOpacity={0.3} />
|
||||
<stop offset="95%" stopColor="#10b981" stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#1e293b" />
|
||||
<XAxis dataKey="time" stroke="#475569" fontSize={12} />
|
||||
<YAxis stroke="#475569" fontSize={12} />
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: '#0f172a',
|
||||
border: '1px solid #1e293b',
|
||||
borderRadius: '6px',
|
||||
color: '#f8fafc',
|
||||
}}
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="cpu"
|
||||
stroke="#3b82f6"
|
||||
fillOpacity={1}
|
||||
fill="url(#colorCpu)"
|
||||
name="CPU"
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="memory"
|
||||
stroke="#10b981"
|
||||
fillOpacity={1}
|
||||
fill="url(#colorMemory)"
|
||||
name="内存"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-slate-800/50 border-slate-700">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm text-slate-400">资源分布</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-4 rounded-lg bg-slate-800">
|
||||
<div className="text-3xl font-bold text-blue-400">{cluster.pod_count || 0}</div>
|
||||
<div className="text-sm text-slate-500 mt-1">Pod</div>
|
||||
</div>
|
||||
<div className="text-center p-4 rounded-lg bg-slate-800">
|
||||
<div className="text-3xl font-bold text-emerald-400">{cluster.service_count || 0}</div>
|
||||
<div className="text-sm text-slate-500 mt-1">Service</div>
|
||||
</div>
|
||||
<div className="text-center p-4 rounded-lg bg-slate-800">
|
||||
<div className="text-3xl font-bold text-purple-400">{cluster.deployment_count || 0}</div>
|
||||
<div className="text-sm text-slate-500 mt-1">Deployment</div>
|
||||
</div>
|
||||
<div className="text-center p-4 rounded-lg bg-slate-800">
|
||||
<div className="text-3xl font-bold text-amber-400">{cluster.node_count || 0}</div>
|
||||
<div className="text-sm text-slate-500 mt-1">节点</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="nodes" className="mt-4">
|
||||
<div className="border rounded-lg border-slate-700 overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead className="bg-slate-800">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">名称</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">角色</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">状态</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">CPU</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">内存</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">Pod</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">操作系统</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800">
|
||||
{nodes.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={7} className="px-4 py-8 text-center text-slate-500">暂无节点数据</td>
|
||||
</tr>
|
||||
) : (
|
||||
nodes.map((node) => (
|
||||
<tr key={node.id || node.name} className="hover:bg-slate-800/50">
|
||||
<td className="px-4 py-3 text-sm text-slate-200">{node.name}</td>
|
||||
<td className="px-4 py-3">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={
|
||||
node.role === 'master'
|
||||
? 'border-purple-500/30 text-purple-400 bg-purple-500/10'
|
||||
: 'border-blue-500/30 text-blue-400 bg-blue-500/10'
|
||||
}
|
||||
>
|
||||
{node.role === 'master' ? 'Master' : 'Worker'}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={
|
||||
(node.status || '').toLowerCase() === 'ready'
|
||||
? 'border-emerald-500/30 text-emerald-400 bg-emerald-500/10'
|
||||
: 'border-red-500/30 text-red-400 bg-red-500/10'
|
||||
}
|
||||
>
|
||||
{(node.status || '').toLowerCase() === 'ready' ? 'Ready' : 'NotReady'}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-300">
|
||||
{Number(node.cpu_usage || 0).toFixed(1)}% / {node.cpu}核
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-300">
|
||||
{Number(node.memory_usage || 0).toFixed(1)}% / {node.memory}GB
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-300">{node.pods ?? node.pod_count ?? 0}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-400">{node.os}</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="pods" className="mt-4">
|
||||
<div className="border rounded-lg border-slate-700 overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead className="bg-slate-800">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">名称</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">命名空间</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">状态</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">重启次数</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">CPU</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">内存</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">节点</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800">
|
||||
{pods.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={7} className="px-4 py-8 text-center text-slate-500">暂无Pod数据</td>
|
||||
</tr>
|
||||
) : (
|
||||
pods.map((pod) => (
|
||||
<tr key={pod.id || pod.name} className="hover:bg-slate-800/50">
|
||||
<td className="px-4 py-3 text-sm text-slate-200">{pod.name}</td>
|
||||
<td className="px-4 py-3">
|
||||
<Badge variant="outline" className="border-slate-700 text-slate-400">
|
||||
{pod.namespace}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={
|
||||
(pod.status || '').toLowerCase() === 'running'
|
||||
? 'border-emerald-500/30 text-emerald-400 bg-emerald-500/10'
|
||||
: (pod.status || '').toLowerCase() === 'pending'
|
||||
? 'border-amber-500/30 text-amber-400 bg-amber-500/10'
|
||||
: 'border-red-500/30 text-red-400 bg-red-500/10'
|
||||
}
|
||||
>
|
||||
{pod.status}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-300">{pod.restart_count ?? 0}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-300">{Number(pod.cpu_usage || 0).toFixed(1)}m</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-300">{Number(pod.memory_usage || 0).toFixed(0)}Mi</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-400">{pod.node_name}</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="deployments" className="mt-4">
|
||||
<div className="border rounded-lg border-slate-700 overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead className="bg-slate-800">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">名称</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">命名空间</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">副本数</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">可用副本</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">镜像</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">创建时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800">
|
||||
{deployments.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={6} className="px-4 py-8 text-center text-slate-500">暂无Deployment数据</td>
|
||||
</tr>
|
||||
) : (
|
||||
deployments.map((deployment) => (
|
||||
<tr key={deployment.id || deployment.name} className="hover:bg-slate-800/50">
|
||||
<td className="px-4 py-3 text-sm text-slate-200">{deployment.name}</td>
|
||||
<td className="px-4 py-3">
|
||||
<Badge variant="outline" className="border-slate-700 text-slate-400">
|
||||
{deployment.namespace}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-300">{deployment.replicas}</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className={`text-sm ${(deployment.available_replicas ?? deployment.availableReplicas) === deployment.replicas ? 'text-emerald-400' : 'text-amber-400'}`}>
|
||||
{deployment.available_replicas ?? deployment.availableReplicas} / {deployment.replicas}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-400 truncate max-w-[200px]">{deployment.image}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-400">{deployment.created_at || deployment.createdAt}</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="services" className="mt-4">
|
||||
<div className="border rounded-lg border-slate-700 overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead className="bg-slate-800">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">名称</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">命名空间</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">类型</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">ClusterIP</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">端口</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">Selector</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800">
|
||||
{services.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={6} className="px-4 py-8 text-center text-slate-500">暂无Service数据</td>
|
||||
</tr>
|
||||
) : (
|
||||
services.map((service) => (
|
||||
<tr key={service.id || service.name} className="hover:bg-slate-800/50">
|
||||
<td className="px-4 py-3 text-sm text-slate-200">{service.name}</td>
|
||||
<td className="px-4 py-3">
|
||||
<Badge variant="outline" className="border-slate-700 text-slate-400">
|
||||
{service.namespace}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={
|
||||
service.type === 'ClusterIP'
|
||||
? 'border-blue-500/30 text-blue-400 bg-blue-500/10'
|
||||
: service.type === 'NodePort'
|
||||
? 'border-purple-500/30 text-purple-400 bg-purple-500/10'
|
||||
: 'border-emerald-500/30 text-emerald-400 bg-emerald-500/10'
|
||||
}
|
||||
>
|
||||
{service.type}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-300">{service.cluster_ip ?? service.clusterIP}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-300">{service.ports}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-400">{service.selector}</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="ingresses" className="mt-4">
|
||||
<div className="border rounded-lg border-slate-700 overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead className="bg-slate-800">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">名称</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">命名空间</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">Hosts</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">后端服务</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-slate-400">TLS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800">
|
||||
{ingresses.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={5} className="px-4 py-8 text-center text-slate-500">暂无Ingress数据</td>
|
||||
</tr>
|
||||
) : (
|
||||
ingresses.map((ingress) => (
|
||||
<tr key={ingress.id || ingress.name} className="hover:bg-slate-800/50">
|
||||
<td className="px-4 py-3 text-sm text-slate-200">{ingress.name}</td>
|
||||
<td className="px-4 py-3">
|
||||
<Badge variant="outline" className="border-slate-700 text-slate-400">
|
||||
{ingress.namespace}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-300">{ingress.hosts}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-300">{ingress.backend}</td>
|
||||
<td className="px-4 py-3">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={
|
||||
ingress.tls
|
||||
? 'border-emerald-500/30 text-emerald-400 bg-emerald-500/10'
|
||||
: 'border-slate-700 text-slate-400'
|
||||
}
|
||||
>
|
||||
{ingress.tls ? '已启用' : '未启用'}
|
||||
</Badge>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="events" className="mt-4">
|
||||
<div className="space-y-2">
|
||||
{[
|
||||
{ type: 'Normal', reason: 'Started', message: 'Started container nginx', time: '2分钟前' },
|
||||
{ type: 'Normal', reason: 'Created', message: 'Created pod nginx-deployment-xxx', time: '5分钟前' },
|
||||
{ type: 'Warning', reason: 'FailedScheduling', message: '0/3 nodes are available', time: '10分钟前' },
|
||||
{ type: 'Normal', reason: 'Pulling', message: 'Pulling image "nginx:latest"', time: '15分钟前' },
|
||||
{ type: 'Normal', reason: 'Pulled', message: 'Successfully pulled image', time: '12分钟前' },
|
||||
].map((event, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center gap-3 p-3 rounded-lg bg-slate-800/50"
|
||||
>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={
|
||||
event.type === 'Normal'
|
||||
? 'border-emerald-500/30 text-emerald-400 bg-emerald-500/10'
|
||||
: 'border-amber-500/30 text-amber-400 bg-amber-500/10'
|
||||
}
|
||||
>
|
||||
{event.type}
|
||||
</Badge>
|
||||
<span className="text-sm font-medium text-slate-300 min-w-[120px]">{event.reason}</span>
|
||||
<span className="text-sm text-slate-400 flex-1">{event.message}</span>
|
||||
<span className="text-xs text-slate-500">{event.time}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user