Files
aiops/frontend/src/components/kubernetes/deployment-detail/CreateHPADialog.tsx
T

120 lines
5.4 KiB
TypeScript

// 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';
import { getErrorMessage } from '@/lib/utils';
import i18n from '@/i18n';
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: i18n.t('k8s.misc.name'), 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: i18n.t('k8s.misc.createSuccess1') });
setOpen(false);
onCreated();
setForm({ name: '', minReplicas: 1, maxReplicas: 10, cpuPercent: 80, memPercent: 0 });
} catch (e: unknown) {
toast({ title: i18n.t('k8s.misc.failed1'), description: getErrorMessage(e), variant: 'destructive' });
} finally { setLoading(false); }
};
return (
<>
<Button variant="outline" size="sm" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]" 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 p-4" onClick={() => setOpen(false)}>
<div className="bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded-xl w-[480px] max-w-[92vw] max-h-[85vh] flex flex-col" onClick={(e) => e.stopPropagation()}>
<h2 className="text-lg font-bold text-[var(--dg-text-primary)] px-6 pt-6 pb-4 shrink-0">创建 HPA</h2>
<div className="flex-1 min-h-0 overflow-y-auto px-6 space-y-3">
<div>
<Label className="dg-text-secondary text-sm">名称</Label>
<Input value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })}
placeholder={`hpa-${deploymentName}`}
className="dg-input mt-1" />
</div>
<div>
<Label className="dg-text-secondary text-sm">目标</Label>
<p className="text-[var(--dg-text-secondary)] text-sm mt-1 bg-[var(--dg-bg-tertiary)] rounded px-3 py-2 truncate" title={`Deployment: ${deploymentName} · 命名空间: ${namespace}`}>
Deployment: {deploymentName} · 命名空间: {namespace}
</p>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<Label className="dg-text-secondary text-sm">最小副本数</Label>
<Input type="number" value={form.minReplicas} min={1}
onChange={(e) => setForm({ ...form, minReplicas: Number(e.target.value) })}
className="dg-input mt-1" />
</div>
<div>
<Label className="dg-text-secondary text-sm">最大副本数</Label>
<Input type="number" value={form.maxReplicas} min={1}
onChange={(e) => setForm({ ...form, maxReplicas: Number(e.target.value) })}
className="dg-input mt-1" />
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<Label className="dg-text-secondary text-sm">CPU 目标 (%)</Label>
<Input type="number" value={form.cpuPercent} min={0} max={100}
onChange={(e) => setForm({ ...form, cpuPercent: Number(e.target.value) })}
className="dg-input mt-1" />
</div>
<div>
<Label className="dg-text-secondary text-sm">{i18n.t('k8s.misc.memory1')} <span className="dg-text-muted">可选</span></Label>
<Input type="number" value={form.memPercent} min={0} max={100}
onChange={(e) => setForm({ ...form, memPercent: Number(e.target.value) })}
className="dg-input mt-1" />
</div>
</div>
</div>
<div className="flex justify-end gap-2 px-6 py-4 border-t border-[var(--dg-border)] shrink-0">
<Button variant="outline" className="border-[var(--dg-border)] dg-text-secondary" 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" /> : i18n.t('common.create')}
</Button>
</div>
</div>
</div>
)}
</>
);
}