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:
1 parent
37c657987b
commit
521bfd356f
24 files changed
+1454
-1221
No files matched your search
@@ -0,0 +1,117 @@
|
||||
// CreateHPADialog.tsx — 创建 HPA 弹窗 // Logan.Li
|
||||
import { useState } from 'react';
|
||||
import { Plus, Loader2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { kubernetesApi } from '@/api';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
|
||||
export interface CreateHPADialogProps {
|
||||
clusterCode: string;
|
||||
namespace: string;
|
||||
deploymentName: string;
|
||||
onCreated: () => void;
|
||||
}
|
||||
|
||||
export function CreateHPADialog({ clusterCode, namespace, deploymentName, onCreated }: CreateHPADialogProps) {
|
||||
const { toast } = useToast();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
minReplicas: 1,
|
||||
maxReplicas: 10,
|
||||
cpuPercent: 80,
|
||||
memPercent: 0,
|
||||
});
|
||||
|
||||
const handleCreate = async () => {
|
||||
if (!form.name.trim()) {
|
||||
toast({ title: '名称不能为空', variant: 'destructive' });
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
await kubernetesApi.createHPA(clusterCode, {
|
||||
name: form.name,
|
||||
namespace,
|
||||
targetKind: 'Deployment',
|
||||
targetName: deploymentName,
|
||||
minReplicas: form.minReplicas,
|
||||
maxReplicas: form.maxReplicas,
|
||||
cpuPercent: form.cpuPercent,
|
||||
memPercent: form.memPercent,
|
||||
});
|
||||
toast({ title: 'HPA 创建成功' });
|
||||
setOpen(false);
|
||||
onCreated();
|
||||
setForm({ name: '', minReplicas: 1, maxReplicas: 10, cpuPercent: 80, memPercent: 0 });
|
||||
} catch (e: any) {
|
||||
toast({ title: '创建失败', description: e?.message || String(e), variant: 'destructive' });
|
||||
} finally { setLoading(false); }
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button variant="outline" size="sm" className="border-slate-700 text-slate-300" onClick={() => setOpen(true)}>
|
||||
<Plus className="h-3.5 w-3.5 mr-1" />创建 HPA
|
||||
</Button>
|
||||
{open && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60" onClick={() => setOpen(false)}>
|
||||
<div className="bg-slate-900 border border-slate-700 rounded-xl p-6 w-[480px] max-h-[80vh] overflow-auto" onClick={(e) => e.stopPropagation()}>
|
||||
<h2 className="text-lg font-bold text-slate-100 mb-4">创建 HPA</h2>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<Label className="text-slate-400 text-sm">名称</Label>
|
||||
<Input value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })}
|
||||
placeholder={`hpa-${deploymentName}`}
|
||||
className="bg-slate-800 border-slate-700 text-slate-200 mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-slate-400 text-sm">目标</Label>
|
||||
<p className="text-slate-300 text-sm mt-1 bg-slate-800 rounded px-3 py-2">
|
||||
Deployment: {deploymentName} · 命名空间: {namespace}
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label className="text-slate-400 text-sm">最小副本数</Label>
|
||||
<Input type="number" value={form.minReplicas} min={1}
|
||||
onChange={(e) => setForm({ ...form, minReplicas: Number(e.target.value) })}
|
||||
className="bg-slate-800 border-slate-700 text-slate-200 mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-slate-400 text-sm">最大副本数</Label>
|
||||
<Input type="number" value={form.maxReplicas} min={1}
|
||||
onChange={(e) => setForm({ ...form, maxReplicas: Number(e.target.value) })}
|
||||
className="bg-slate-800 border-slate-700 text-slate-200 mt-1" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label className="text-slate-400 text-sm">CPU 目标 (%)</Label>
|
||||
<Input type="number" value={form.cpuPercent} min={0} max={100}
|
||||
onChange={(e) => setForm({ ...form, cpuPercent: Number(e.target.value) })}
|
||||
className="bg-slate-800 border-slate-700 text-slate-200 mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-slate-400 text-sm">内存目标 (%) <span className="text-slate-600">可选</span></Label>
|
||||
<Input type="number" value={form.memPercent} min={0} max={100}
|
||||
onChange={(e) => setForm({ ...form, memPercent: Number(e.target.value) })}
|
||||
className="bg-slate-800 border-slate-700 text-slate-200 mt-1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 mt-6">
|
||||
<Button variant="outline" className="border-slate-700 text-slate-400" onClick={() => setOpen(false)}>取消</Button>
|
||||
<Button className="bg-blue-600 hover:bg-blue-700" disabled={loading} onClick={handleCreate}>
|
||||
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : '创建'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user