import { useState, useEffect, useCallback } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell, } from '@/components/ui/table'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogTrigger, } from '@/components/ui/dialog'; import { RefreshCw, Loader2, Plus, Trash2, Boxes, Shield, AlertTriangle, CheckCircle2, } from 'lucide-react'; import { kubernetesApi } from '@/api'; import { toast } from 'sonner'; import { PageShell } from '@/components/layout/PageShell'; import { ContentPanel } from '@/components/layout/ContentPanel'; import { StatStrip } from '@/components/ui/stat-strip'; import i18n from '@/i18n'; interface NamespaceDetail { name: string; status: string; age: string; labels: Record; resource_counts: Record; has_resource_quota: boolean; has_limit_range: boolean; resource_quotas?: Array<{ metadata: { name: string }; status: { hard: Record; used: Record }; }>; } const SYSTEM_NAMESPACES = new Set([ 'kube-system', 'kube-public', 'kube-node-lease', 'default', 'devops', ]); export default function NamespaceManagementPage() { const { clusterCode } = useParams<{ clusterCode: string }>(); const navigate = useNavigate(); const [namespaces, setNamespaces] = useState([]); const [loading, setLoading] = useState(false); const [searchQuery, setSearchQuery] = useState(''); // Create dialog state const [createOpen, setCreateOpen] = useState(false); const [newName, setNewName] = useState(''); const [creating, setCreating] = useState(false); // Delete dialog state const [deleteTarget, setDeleteTarget] = useState(null); const [deleting, setDeleting] = useState(false); const loadData = useCallback(async () => { if (!clusterCode) return; setLoading(true); try { const data = await kubernetesApi.getNamespacesDetail(clusterCode); setNamespaces(Array.isArray(data) ? data : []); } catch { setNamespaces([]); } finally { setLoading(false); } }, [clusterCode]); useEffect(() => { loadData(); }, [loadData]); const handleCreate = async () => { if (!clusterCode || !newName.trim()) return; setCreating(true); try { await kubernetesApi.createNamespace(clusterCode, newName.trim()); setNewName(''); setCreateOpen(false); await loadData(); } catch (err) { toast.error(err?.message || i18n.t('monitor.dashboard.custom.toastCreateFailed')); } finally { setCreating(false); } }; const handleDelete = async () => { if (!clusterCode || !deleteTarget) return; setDeleting(true); try { await kubernetesApi.deleteNamespace(clusterCode, deleteTarget); setDeleteTarget(null); await loadData(); } catch (err) { toast.error(err?.message || i18n.t('kubernetes.msg.deleteFailed2')); } finally { setDeleting(false); } }; const filtered = namespaces.filter(ns => !searchQuery || ns.name.toLowerCase().includes(searchQuery.toLowerCase()) ); const totalDeploys = namespaces.reduce((s, n) => s + (n.resource_counts?.deployments || 0), 0); const totalPods = namespaces.reduce((s, n) => s + (n.resource_counts?.pods || 0), 0); const totalSvcs = namespaces.reduce((s, n) => s + (n.resource_counts?.services || 0), 0); const quotaCount = namespaces.filter(n => n.has_resource_quota).length; const searchToolbar = ( setSearchQuery(e.target.value)} placeholder={i18n.t('kubernetes.namespacemanage.se91247a0')} className="max-w-xs dg-input" /> ); return ( } metrics={!loading ? ( ) : undefined} actions={( <> {i18n.t('kubernetes.namespacemanage.s737f41e9')}
setNewName(e.target.value)} placeholder={i18n.t('kubernetes.namespacemanage.scba00862')} onKeyDown={(e) => e.key === 'Enter' && handleCreate()} />

名称需符合 DNS-1123 标准(小写字母/数字/连字符)

)} > {i18n.t('kubernetes.secret.secretName')} {i18n.t('system.terminalStatus')} {i18n.t('kubernetes.namespacemanage.sa01a9963')} Deploy Pods Svc {i18n.t('common.quota')} {i18n.t('kubernetes.namespace.labels')} {i18n.t('system.auditAction')} {loading ? ( 加载中... ) : filtered.length === 0 ? ( 暂无命名空间 ) : ( filtered.map((ns) => { const isSystem = SYSTEM_NAMESPACES.has(ns.name); return ( {ns.status === 'Active' ? : } {ns.status} {ns.age} {ns.resource_counts?.deployments ?? 0} {ns.resource_counts?.pods ?? 0} {ns.resource_counts?.services ?? 0} {ns.has_resource_quota ? ( 有 ) : ( - )} {ns.labels && Object.keys(ns.labels).length > 0 ? (
{Object.entries(ns.labels).slice(0, 3).map(([k, v]) => ( {k}={v} ))} {Object.keys(ns.labels).length > 3 && ( +{Object.keys(ns.labels).length - 3} )}
) : ( - )}
{isSystem ? ( {i18n.t('common.system3')} ) : ( )}
); }) )}
{/* Delete confirmation dialog */} !open && setDeleteTarget(null)}> 删除命名空间

⚠️ 即将删除命名空间 {deleteTarget} 及其中所有资源。

此操作不可逆,所有 Deployment/Pod/Service/ConfigMap 等资源将被永久删除。

); }