Files
aiops/frontend/src/components/kubernetes/deployment-view/helpers.tsx
T

145 lines
5.7 KiB
TypeScript

// deployment-view/helpers.tsx — 时间线 / 信息面板辅助组件 // Logan.Li
import { Fragment } from 'react';
import {
formatK8sTime, formatDuration, elapsedSeconds, STATUS_COLORS,
} from '../k8s-yaml-utils';
import type { PodItem } from './types';
import type { JsonData } from '@/types/common';
export interface PodStage {
label: string;
ts?: string;
color: string;
}
// Pod 条件时间线提取 // Logan.Li
export function getPodTimeline(pod: PodItem) {
const cond: JsonData[] = (pod as JsonData).conditions || (pod as JsonData).pod_conditions || [];
const find = (t: string) => cond.find((c: JsonData) => (c.type || c.Type) === t);
const ts = (c: JsonData) => c?.lastTransitionTime || c?.LastTransitionTime || c?.last_transition_time;
return {
scheduled: ts(find('PodScheduled') || {}),
initialized: ts(find('Initialized') || {}),
containersReady: ts(find('ContainersReady') || {}),
ready: ts(find('Ready') || {}),
startTime: pod.start_time || (pod as JsonData).startTime,
};
}
// 启动阶段列表(列表/详情共用) // Logan.Li
export function getPodStages(pod: PodItem): PodStage[] {
const tl = getPodTimeline(pod);
return [
{ label: '已调度', ts: tl.scheduled, color: STATUS_COLORS.blue },
{ label: '已初始化', ts: tl.initialized, color: STATUS_COLORS.orange },
{ label: '容器已就绪', ts: tl.containersReady, color: STATUS_COLORS.green },
{ label: '容器组已就绪', ts: tl.ready, color: STATUS_COLORS.green },
];
}
// 信息面板单项 // Logan.Li
export function InfoItem({ label, value }: { label: string; value: React.ReactNode }) {
return (
<div>
<p className="text-xs text-[var(--dg-text-muted)] mb-1">{label}</p>
<div className="text-sm text-[var(--dg-text-primary)]">{value}</div>
</div>
);
}
// 列表行紧凑就绪指示(无时间戳,避免视觉疲劳) // Logan.Li
export function PodStatusCompact({ pod }: { pod: PodItem }) {
const stages = getPodStages(pod);
const done = stages.filter(s => s.ts).length;
const allReady = done === stages.length;
const pending = stages.find(s => !s.ts);
return (
<div className="flex items-center gap-2 min-w-[88px]">
<div className="flex items-center gap-0.5" aria-label="启动进度">
{stages.map((s, i) => (
<Fragment key={s.label}>
<span
className="inline-block h-1.5 w-1.5 rounded-full shrink-0"
style={{ background: s.ts ? s.color : 'var(--dg-border)' }}
title={`${s.label}${s.ts ? ` · ${formatK8sTime(s.ts)}` : ''}`}
/>
{i < stages.length - 1 && (
<span
className="inline-block h-px w-2 shrink-0"
style={{ background: s.ts && stages[i + 1]?.ts ? s.color : 'var(--dg-border)' }}
/>
)}
</Fragment>
))}
</div>
<span
className="text-[11px] whitespace-nowrap"
style={{ color: allReady ? STATUS_COLORS.green : 'var(--dg-text-muted)' }}
>
{allReady ? '就绪' : pending?.label || `${done}/${stages.length}`}
</span>
</div>
);
}
// 详情区完整启动时间线(仅展开或概览面板使用) // Logan.Li
export function PodConditionsRow({ pod }: { pod: PodItem }) {
const stages = getPodStages(pod);
const totalBoot = elapsedSeconds(stages[0]?.ts, stages[stages.length - 1]?.ts);
return (
<div className="rounded-lg bg-[var(--dg-bg-tertiary)]/60 px-4 py-3">
<div className="flex items-center justify-between mb-3">
<span className="text-xs text-[var(--dg-text-muted)]">启动时间线</span>
{totalBoot != null && (
<span className="text-[11px] text-[var(--dg-text-muted)]">
总耗时 <span className="font-mono text-[var(--dg-text-secondary)]">{formatDuration(totalBoot)}</span>
</span>
)}
</div>
<div className="flex items-start gap-0 overflow-x-auto">
{stages.map((s, i) => {
const prev = stages[i - 1];
const dur = elapsedSeconds(prev?.ts, s.ts);
const active = !!s.ts;
return (
<Fragment key={s.label}>
<div className="flex flex-col min-w-[108px]">
<div className="flex items-center gap-2">
<span
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-[10px] font-semibold"
style={{
background: active ? `${s.color}22` : 'var(--dg-bg-secondary)',
color: active ? s.color : 'var(--dg-text-muted)',
border: `1px solid ${active ? `${s.color}55` : 'var(--dg-border)'}`,
}}
>
{i + 1}
</span>
<span className="text-xs font-medium" style={{ color: active ? s.color : 'var(--dg-text-muted)' }}>
{s.label}
</span>
</div>
<span className="mt-1.5 pl-7 text-[11px] font-mono text-[var(--dg-text-muted)]">
{s.ts ? formatK8sTime(s.ts) : '—'}
</span>
</div>
{i < stages.length - 1 && (
<div className="flex flex-col items-center justify-center px-1 pt-2.5 min-w-[48px]">
<div
className="h-px w-full"
style={{ background: active && stages[i + 1]?.ts ? s.color : 'var(--dg-border)' }}
/>
<span className="mt-1 text-[10px] text-[var(--dg-text-muted)]">
{dur != null ? formatDuration(dur) : ''}
</span>
</div>
)}
</Fragment>
);
})}
</div>
</div>
);
}