feat(k8s): M2 Pod Metrics 详情页接入 — ListPods 注入 metrics
后端: - 新增 buildPodMetricsMap() 公共辅助函数,聚合多容器 CPU/Memory - ListPods / listPodsWithNS / ListPodsAllNS 三个 handler 全部注入 - PodSummary 增加 cpu_usage_m / mem_usage_mi 字段 前端: - KubernetesWorkloads Pod 表格增加 CPU/内存列 - NamespaceResourcePage Pod 表格增加 CPU/内存列 验证: - 17 个 Pod 全部返回真实 metrics (argocd/demo-jar) - curl /kubernetes-clusters/1/pods → cpu_usage_m='2m' mem_usage_mi='61Mi' 文档: - ROADMAP M1→done, M2→done, 验收标准全部勾选 - TDD_PLAN M2 通过标准补充 - PROJECT_EVALUATION P0 缺失项移除
This commit is contained in:
1 parent
8c272a6fa6
commit
4f1ffd1275
27 files changed
+2617
-352
No files matched your search
@@ -14,6 +14,7 @@ import {
|
||||
Loader2, StopCircle, FileText, Trash2, ChevronDown,
|
||||
ChevronRight, Rocket, Box, Workflow, Code, Container, ShieldCheck, Tag,
|
||||
Globe, ArrowRight, RotateCcw, Layers, History, Server, Network,
|
||||
ChevronRight as ChevronRightSmall, Package,
|
||||
} from 'lucide-react';
|
||||
import { pipelineApi, deployApi } from '@/services/api';
|
||||
import http from '@/services/http';
|
||||
@@ -283,6 +284,35 @@ export function PipelineManager() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* ══════════ 全流程可视化横幅 // Logan.Li ══════════ */}
|
||||
<div className="flex items-center gap-1.5 px-4 py-2.5 rounded-lg bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)]">
|
||||
<div className="flex items-center gap-1">
|
||||
<GitBranch className="h-3.5 w-3.5 text-indigo-400" />
|
||||
<span className="text-[11px] font-semibold text-indigo-400">Git 代码</span>
|
||||
</div>
|
||||
<ArrowRight className="h-3 w-3 text-[var(--dg-text-secondary)] opacity-30" />
|
||||
<div className="flex items-center gap-1">
|
||||
<Box className="h-3.5 w-3.5 text-amber-400" />
|
||||
<span className="text-[11px] font-semibold text-amber-400">构建</span>
|
||||
</div>
|
||||
<ArrowRight className="h-3 w-3 text-[var(--dg-text-secondary)] opacity-30" />
|
||||
<div className="flex items-center gap-1">
|
||||
<Container className="h-3.5 w-3.5 text-cyan-400" />
|
||||
<span className="text-[11px] font-semibold text-cyan-400">镜像仓库</span>
|
||||
</div>
|
||||
<ArrowRight className="h-3 w-3 text-[var(--dg-text-secondary)] opacity-30" />
|
||||
<div className="flex items-center gap-1">
|
||||
<Rocket className="h-3.5 w-3.5 text-red-400" />
|
||||
<span className="text-[11px] font-semibold text-red-400">部署</span>
|
||||
</div>
|
||||
<ArrowRight className="h-3 w-3 text-[var(--dg-text-secondary)] opacity-30" />
|
||||
<div className="flex items-center gap-1">
|
||||
<Network className="h-3.5 w-3.5 text-emerald-400" />
|
||||
<span className="text-[11px] font-semibold text-emerald-400">K8s</span>
|
||||
</div>
|
||||
<span className="ml-auto text-[10px] text-[var(--dg-text-secondary)] opacity-40 tracking-wide">全流程可视化</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="relative max-w-md flex-1">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--dg-text-secondary)]" />
|
||||
@@ -329,15 +359,43 @@ export function PipelineManager() {
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<h3 className="font-semibold text-[var(--dg-text-primary)]">{pipeline.name}</h3>
|
||||
{pipeline.run_count > 0 && <Badge variant="outline" className="text-[11px] text-[var(--dg-text-secondary)] border-[var(--dg-border)]">#{pipeline.run_count}</Badge>}
|
||||
{getStatusBadge(pipeline.last_status)}
|
||||
<Badge variant="outline" className="text-xs text-[var(--dg-text-secondary)] border-[var(--dg-border)]">
|
||||
{pipeline.run_count > 0 ? `运行 ${pipeline.run_count} 次` : '未执行'}
|
||||
</Badge>
|
||||
{pipeline.job_name && <Badge variant="secondary" className="text-xs font-mono opacity-60">{pipeline.job_name}</Badge>}
|
||||
{pipeline.environment && <Badge style={{backgroundColor: pipeline.environment.color + '20', color: pipeline.environment.color}} className="text-xs">{pipeline.environment.display_name}</Badge>}
|
||||
{pipeline.job_name && <Badge variant="secondary" className="text-xs font-mono opacity-60">{pipeline.job_name}</Badge>}
|
||||
</div>
|
||||
<p className="text-xs text-[var(--dg-text-secondary)] mt-1 line-clamp-1">{pipeline.description || '暂无描述'}</p>
|
||||
<div className="flex items-center gap-3 mt-1 text-xs text-[var(--dg-text-secondary)] opacity-60">
|
||||
{/* 最近运行摘要行:branch + commit → image:tag // Logan.Li */}
|
||||
{(pipeline.last_run_branch || pipeline.last_commit_id || pipeline.last_image_tag) && (
|
||||
<div className="flex items-center gap-1 mt-1.5 text-xs font-mono">
|
||||
{pipeline.last_run_branch && (
|
||||
<span className="flex items-center gap-0.5 text-indigo-400">
|
||||
<GitBranch className="h-3 w-3" />{pipeline.last_run_branch}
|
||||
</span>
|
||||
)}
|
||||
{pipeline.last_run_branch && pipeline.last_commit_id && (
|
||||
<span className="text-[var(--dg-text-secondary)] opacity-50 text-[10px] mx-0.5">+</span>
|
||||
)}
|
||||
{pipeline.last_commit_id && (
|
||||
<code className="text-amber-400 bg-amber-400/10 px-1 py-0.5 rounded text-[11px]">{pipeline.last_commit_id.slice(0, 8)}</code>
|
||||
)}
|
||||
{(pipeline.last_run_branch || pipeline.last_commit_id) && pipeline.last_image_tag && (
|
||||
<ArrowRight className="h-3 w-3 text-[var(--dg-text-secondary)] opacity-40 mx-0.5" />
|
||||
)}
|
||||
{pipeline.last_image_tag && (
|
||||
<span className="flex items-center gap-0.5 text-cyan-400">
|
||||
<Package className="h-3 w-3" />
|
||||
<code className="bg-cyan-400/10 px-1 py-0.5 rounded text-[11px]">{pipeline.last_image_tag}</code>
|
||||
</span>
|
||||
)}
|
||||
{pipeline.last_duration > 0 && (
|
||||
<span className="flex items-center gap-0.5 ml-auto text-[var(--dg-text-secondary)] opacity-50">
|
||||
<Clock className="h-3 w-3" />{fmtDuration(pipeline.last_duration)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-3 mt-1.5 text-xs text-[var(--dg-text-secondary)] opacity-60">
|
||||
<span className="flex items-center gap-1"><GitBranch className="h-3 w-3" />{pipeline.deploy_mode === 'tag' ? 'Tag发版' : pipeline.branch || 'main'}</span>
|
||||
<span className="flex items-center gap-1">{(pipeline.deploy_target || 'ssh') === 'k8s' ? <><Network className="h-3 w-3" />K8s</> : <><Server className="h-3 w-3" />SSH</>}</span>
|
||||
{repo && <span className="flex items-center gap-1"><Globe className="h-3 w-3" />{repo.name}</span>}
|
||||
@@ -365,18 +423,36 @@ export function PipelineManager() {
|
||||
</div>
|
||||
</div>
|
||||
{stages.length > 0 && (
|
||||
<div className="flex items-center gap-1 mt-3 flex-wrap">
|
||||
<div className="flex items-center mt-2.5">
|
||||
{stages.map((s: any, i: number) => {
|
||||
const def = STAGE_DEFS.find(d => d.label === s.name);
|
||||
const color = def?.color || '#6B7280';
|
||||
const Icon = def?.icon || Tag;
|
||||
// 从 last_run_stages 获取最新运行状态 // Logan.Li
|
||||
let runStatus: string = '';
|
||||
if (pipeline.last_run_stages) {
|
||||
try {
|
||||
const rs = typeof pipeline.last_run_stages === 'string' ? JSON.parse(pipeline.last_run_stages) : pipeline.last_run_stages;
|
||||
const match = Array.isArray(rs) ? rs.find((r: any) => r.name === s.name || r.type === def?.key) : null;
|
||||
runStatus = match?.status || '';
|
||||
} catch {}
|
||||
}
|
||||
const isSuccess = runStatus === 'success' || runStatus === 'completed';
|
||||
const isRunning = runStatus === 'running' || runStatus === 'in_progress';
|
||||
const isFailed = runStatus === 'failed' || runStatus === 'error';
|
||||
const color = isFailed ? '#EF4444' : isRunning ? '#3B82F6' : isSuccess ? '#10B981' : '#6B7280';
|
||||
const bgColor = isFailed ? '#EF444528' : isRunning ? '#3B82F628' : isSuccess ? '#10B98128' : '#6B728018';
|
||||
const icon = isFailed ? <XCircle className="h-3 w-3" /> : isRunning ? <Loader2 className="h-3 w-3 animate-spin" /> : isSuccess ? <CheckCircle className="h-3 w-3" /> : <span className="text-[10px] opacity-40">○</span>;
|
||||
return (
|
||||
<div key={i} className="flex items-center">
|
||||
<div className="flex items-center gap-1.5 px-2.5 py-1 rounded-full border text-xs"
|
||||
style={{ borderColor: color + '40', backgroundColor: color + '15', color: color }}>
|
||||
<Icon className="h-3 w-3" />{s.name}
|
||||
<div className="flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] font-semibold border shadow-sm"
|
||||
style={{ backgroundColor: bgColor, borderColor: color + '70', color, borderRadius: i === 0 ? '8px 0 0 8px' : i === stages.length - 1 ? '0 8px 8px 0' : '0' }}>
|
||||
{icon}{s.name}
|
||||
</div>
|
||||
{i < stages.length - 1 && <ArrowRight className="h-3 w-3 text-[var(--dg-border)] mx-0.5" />}
|
||||
{i < stages.length - 1 && (
|
||||
<div className="flex items-center justify-center w-5 h-5 -mx-px z-10">
|
||||
<ChevronRightSmall className="h-3.5 w-3.5" style={{ color: color + 'CC' }} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@@ -583,15 +659,20 @@ export function PipelineManager() {
|
||||
})}
|
||||
</div>
|
||||
{selectedStages.length > 0 && (
|
||||
<div className="flex items-center gap-1 mt-2 flex-wrap">
|
||||
<div className="flex items-center mt-2">
|
||||
{selectedStages.map((key, i) => {
|
||||
const def = STAGE_DEFS.find(t => t.key === key)!;
|
||||
return (
|
||||
<div key={i} className="flex items-center">
|
||||
<Badge className="text-[10px] px-2 py-0.5" style={{ backgroundColor: def.color + '20', color: def.color, borderColor: def.color + '40' }}>
|
||||
<def.icon className="h-3 w-3 mr-0.5" />{def.label}
|
||||
</Badge>
|
||||
{i < selectedStages.length - 1 && <ArrowRight className="h-3 w-3 text-[var(--dg-border)] mx-0.5" />}
|
||||
<div className="flex items-center gap-1 px-2 py-1 text-[11px] font-medium border"
|
||||
style={{ backgroundColor: def.color + '20', borderColor: def.color + '40', color: def.color, borderRadius: i === 0 ? '6px 0 0 6px' : i === selectedStages.length - 1 ? '0 6px 6px 0' : '0' }}>
|
||||
<def.icon className="h-3 w-3" />{def.label}
|
||||
</div>
|
||||
{i < selectedStages.length - 1 && (
|
||||
<div className="flex items-center justify-center w-4 h-5 -mx-px z-10">
|
||||
<ChevronRightSmall className="h-3 w-3" style={{ color: def.color }} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in new issue
Block a user