refactor(frontend): P3 — K8s 组件拆分、AlertCenter Query、permission utils

- 拆分 DeploymentView / DeploymentDetail 至子目录(<700 行红线)
- AlertCenter 接入 useAlerts / useAgentAlertRules / useAlertMutations
- 新增 utils/permission.ts,usePermission 复用纯函数
- useAlerts 增强分页统计与 Agent 规则 hooks
This commit is contained in:
Logan.Li committed 2026-06-26 10:51:15 +08:00
1 parent 37c657987b
commit 521bfd356f
24 files changed
+1454 -1221

No files matched your search

@@ -0,0 +1,164 @@
// DeploymentOverview.tsx — Deployment 详情头部、状态与操作区 // Logan.Li
import {
ArrowLeft, Minus, Plus, RotateCw, Upload, RefreshCw, Loader2,
} from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge';
import { CreateHPADialog } from './CreateHPADialog';
export interface DeploymentOverviewProps {
deploymentName: string;
namespace: string;
deployment: any;
hpas: any[];
clusterCode: string;
ready: number;
total: number;
available: number;
isHealthy: boolean;
actionLoading: string;
newImage: string;
showImageUpdate: boolean;
onBack: () => void;
onRefresh: () => void;
onScale: (delta: number) => void;
onRestart: () => void;
onNewImageChange: (value: string) => void;
onToggleImageUpdate: () => void;
onUpdateImage: () => void;
}
export function DeploymentOverview({
deploymentName, namespace, deployment, hpas, clusterCode,
ready, total, available, isHealthy, actionLoading,
newImage, showImageUpdate, onBack, onRefresh, onScale, onRestart,
onNewImageChange, onToggleImageUpdate, onUpdateImage,
}: DeploymentOverviewProps) {
return (
<>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Button variant="ghost" size="sm" className="text-slate-400" onClick={onBack}>
<ArrowLeft className="h-4 w-4 mr-1" />返回
</Button>
<div>
<div className="flex items-center gap-2">
<h1 className="text-xl font-bold text-slate-100">{deploymentName}</h1>
<Badge variant="outline" className="border-slate-700 text-slate-400">{namespace}</Badge>
</div>
<p className="text-sm text-slate-500 mt-0.5">镜像: {deployment.image || '-'}</p>
</div>
</div>
<Button variant="outline" size="sm" className="border-slate-700 text-slate-400" onClick={onRefresh}>
<RefreshCw className="h-4 w-4 mr-1" />刷新
</Button>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
<Card className="bg-slate-900 border-slate-800">
<CardContent className="p-3">
<p className="text-xs text-slate-500">副本状态</p>
<p className={`text-lg font-bold ${isHealthy ? 'text-emerald-400' : 'text-amber-400'}`}>
{ready}/{total}
</p>
</CardContent>
</Card>
<Card className="bg-slate-900 border-slate-800">
<CardContent className="p-3">
<p className="text-xs text-slate-500">可用副本</p>
<p className="text-lg font-bold text-blue-400">{available}</p>
</CardContent>
</Card>
<Card className="bg-slate-900 border-slate-800">
<CardContent className="p-3">
<p className="text-xs text-slate-500">镜像</p>
<p className="text-sm font-mono text-slate-300 truncate max-w-[200px]" title={deployment.image}>
{deployment.image?.split('/').pop() || '-'}
</p>
</CardContent>
</Card>
<Card className="bg-slate-900 border-slate-800">
<CardContent className="p-3">
<p className="text-xs text-slate-500">HPA</p>
{hpas.length > 0 ? (
<p className="text-lg font-bold text-purple-400">已启用</p>
) : (
<div className="flex items-center justify-between">
<p className="text-lg font-bold text-slate-500">未配置</p>
<CreateHPADialog
clusterCode={clusterCode}
namespace={namespace}
deploymentName={deploymentName}
onCreated={onRefresh}
/>
</div>
)}
</CardContent>
</Card>
</div>
<Card className="bg-slate-900 border-slate-800">
<CardHeader className="pb-3">
<CardTitle className="text-base text-slate-200">操作</CardTitle>
</CardHeader>
<CardContent>
<div className="flex flex-wrap items-center gap-3">
<div className="flex items-center gap-1 bg-slate-800 rounded-lg p-1">
<Button
variant="ghost" size="icon" className="h-8 w-8 text-slate-300 hover:text-slate-100"
disabled={actionLoading === 'scale' || total <= 0}
onClick={() => onScale(-1)}
>
<Minus className="h-4 w-4" />
</Button>
<span className="text-sm font-mono text-slate-200 w-8 text-center">
{actionLoading === 'scale' ? <Loader2 className="h-4 w-4 animate-spin inline" /> : total}
</span>
<Button
variant="ghost" size="icon" className="h-8 w-8 text-slate-300 hover:text-slate-100"
disabled={actionLoading === 'scale'}
onClick={() => onScale(1)}
>
<Plus className="h-4 w-4" />
</Button>
</div>
<Button
variant="outline" size="sm" className="border-slate-700 text-slate-300 hover:text-slate-100"
disabled={actionLoading === 'restart'}
onClick={onRestart}
>
{actionLoading === 'restart' ? <Loader2 className="h-4 w-4 mr-1 animate-spin" /> : <RotateCw className="h-4 w-4 mr-1" />}
滚动重启
</Button>
<Button
variant="outline" size="sm" className="border-slate-700 text-slate-300 hover:text-slate-100"
onClick={onToggleImageUpdate}
>
<Upload className="h-4 w-4 mr-1" />
更新镜像
</Button>
</div>
{showImageUpdate && (
<div className="mt-3 flex items-center gap-2 p-3 bg-slate-800/50 rounded-lg border border-slate-700">
<Input
value={newImage}
onChange={(e) => onNewImageChange(e.target.value)}
placeholder="输入新镜像地址,如 harbor.example.com/app:v2.0"
className="flex-1 h-9 bg-slate-900 border-slate-700 text-slate-200 text-sm font-mono"
/>
<Button size="sm" className="bg-blue-600 hover:bg-blue-700"
disabled={actionLoading === 'image' || !newImage.trim()}
onClick={onUpdateImage}
>
{actionLoading === 'image' ? <Loader2 className="h-4 w-4 animate-spin" /> : '更新'}
</Button>
<Button variant="ghost" size="sm" className="text-slate-400" onClick={onToggleImageUpdate}>取消</Button>
</div>
)}
</CardContent>
</Card>
</>
);
}