import { useState, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { kubernetesApi } from '@/api'; import { toast } from 'sonner'; import type { JsonData } from '@/types/common'; // 名称即 code,只允许英文/数字/连字符 const NAME_RE = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/; const emptyForm = { name: '', version: '', description: '', config_type: 1, // 0=Token, 1=Kubeconfig api_server: '', token: '', kube_config: '', }; export function KubernetesClusterDialog({ open, onOpenChange, editingCluster, onSaved, }: { open: boolean; onOpenChange: (open: boolean) => void; editingCluster?: JsonData; onSaved: () => void; }) { const [formData, setFormData] = useState({ ...emptyForm }); const [submitting, setSubmitting] = useState(false); const [hasToken, setHasToken] = useState(false); const [hasKubeConfig, setHasKubeConfig] = useState(false); const [nameError, setNameError] = useState(''); useEffect(() => { if (!open) return; if (editingCluster && editingCluster.id) { kubernetesApi.getClusterDetail(editingCluster.id).then((res: JsonData) => { const d = res || {}; setHasToken(!!d.has_token); setHasKubeConfig(!!d.has_kube_config); setFormData({ name: d.name || '', version: d.version || '', description: d.description || '', config_type: Number(d.config_type ?? 1), api_server: d.api_server || '', token: '', kube_config: '', }); setNameError(''); }).catch(() => { setFormData({ name: editingCluster.name || '', version: editingCluster.version || '', description: editingCluster.description || '', config_type: Number(editingCluster.config_type ?? 1), api_server: editingCluster.api_server || '', token: '', kube_config: '', }); setNameError(''); }); } else { setHasToken(false); setHasKubeConfig(false); setFormData({ ...emptyForm }); setNameError(''); } }, [open, editingCluster]); const handleNameChange = (value: string) => { setFormData({ ...formData, name: value }); if (!value.trim()) { setNameError(''); } else if (!NAME_RE.test(value)) { setNameError('只允许英文、数字和连字符(-),首尾不能是连字符'); } else { setNameError(''); } }; const parseApiServer = (yaml: string): string => { const match = yaml.match(/^\s*server:\s*(\S+)\s*$/m); return match ? match[1] : ''; }; const handleKubeConfigChange = (value: string) => { const server = parseApiServer(value); setFormData((prev) => ({ ...prev, kube_config: value, api_server: server || prev.api_server, })); }; const handleSubmit = async () => { if (!formData.name.trim() || !NAME_RE.test(formData.name.trim())) { toast.error('请输入有效的集群名称(英文/数字/连字符)'); return; } try { setSubmitting(true); const payload = { name: formData.name.trim(), code: formData.name.trim(), // 名称即 code version: formData.version, description: formData.description, config_type: formData.config_type, api_server: formData.api_server, token: formData.config_type === 0 ? formData.token : '', kube_config: formData.config_type === 1 ? formData.kube_config : '', }; if (editingCluster) { await kubernetesApi.updateCluster(editingCluster.id, payload); } else { await kubernetesApi.createCluster(payload); } onOpenChange(false); onSaved(); } catch (e) { console.error('保存集群失败:', e); } finally { setSubmitting(false); } }; const canSubmit = submitting || !formData.name.trim() || !!nameError; return ( {editingCluster ? '编辑集群' : '添加集群'} {editingCluster ? '修改集群接入配置' : '配置新的 Kubernetes 集群接入信息'}
handleNameChange(e.target.value)} placeholder="如: prod-cluster" className={`bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] ${nameError ? 'border-red-500' : ''}`} /> {nameError &&

{nameError}

}

只允许英文、数字和连字符(-),首尾不能是连字符。名称将作为集群唯一标识。

setFormData({ ...formData, version: e.target.value })} placeholder="如: 1.28.0" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]" />
setFormData({ ...formData, description: e.target.value })} placeholder="集群用途描述" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]" />
setFormData({ ...formData, config_type: Number(v) })} className="flex gap-4" >
setFormData({ ...formData, api_server: e.target.value })} placeholder="https://10.0.0.1:6443" className="bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]" />
{formData.config_type === 0 ? (