import { memo, useCallback, useEffect, useRef, useState } from 'react' import { Server, Plus } from 'lucide-react' import { Button } from '@/components/ui/button' import { clusterApi, deploymentApi, collectorApi } from '@/api' import { PageContainer } from '@/components/PageContainer' import { LoadingSpinner } from '@/components/LoadingSpinner' import { EmptyState } from '@/components/EmptyState' import { ErrorState } from '@/components/ErrorState' import { PaginationBar } from '@/components/PaginationBar' import { ConfirmDialog } from '@/components/ConfirmDialog' import { toast } from 'sonner' import { useListQuery } from '@/hooks/useListQuery' import type { Cluster } from '@/types' import { ClusterCard } from './components/ClusterCard' import { ClusterFormDialog } from './components/ClusterFormDialog' import { ClusterDetailDialog } from './components/ClusterDetailDialog' /** * 集群管理页 — 只做编排:数据走 useListQuery(取消+版本号), * 卡片/表单/详情/删除确认均为域内子组件。成功操作静默(列表刷新即反馈)。 */ const ClustersPage = memo(function ClustersPage() { const query = useListQuery((params, signal) => clusterApi.list(params, { signal }) ) const [formOpen, setFormOpen] = useState(false) const [detailCluster, setDetailCluster] = useState(null) const [deleteTarget, setDeleteTarget] = useState(null) const [deleting, setDeleting] = useState(false) const [validating, setValidating] = useState(null) // 验证后延时刷新列表的定时器(卸载清理) const timeoutsRef = useRef[]>([]) useEffect(() => { const timers = timeoutsRef.current return () => { timers.forEach((id) => clearTimeout(id)) } }, []) // 删除保护:引用检查(部署/采集器仍指向该集群时警告) const [refCount, setRefCount] = useState<{ dep: number; col: number }>({ dep: 0, col: 0 }) useEffect(() => { if (!deleteTarget) return Promise.all([ deploymentApi.list({ page: 1, pageSize: 100 }), collectorApi.list({ page: 1, pageSize: 100 }), ]).then(([d, c]) => { setRefCount({ dep: d.list.filter((x) => x.clusterID === deleteTarget.id).length, col: c.list.filter((x) => x.clusterID === deleteTarget.id).length, }) }).catch(() => setRefCount({ dep: 0, col: 0 })) }, [deleteTarget]) const handleDelete = useCallback(async () => { const id = deleteTarget?.id if (!id) return setDeleting(true) try { await clusterApi.remove(id) setDeleteTarget(null) query.refresh() } catch (err) { toast.error(err instanceof Error ? err.message : '删除集群失败') } finally { setDeleting(false) } }, [deleteTarget, query]) const handleValidate = useCallback( async (id: number) => { setValidating(id) try { await clusterApi.validate(id) timeoutsRef.current.push(setTimeout(() => query.refresh(), 3000)) } catch (err) { toast.error(err instanceof Error ? err.message : '验证失败') } finally { timeoutsRef.current.push(setTimeout(() => setValidating(null), 3000)) } }, [query] ) return ( setFormOpen(true)} > 添加集群 } > {query.loading ? ( ) : query.error ? ( ) : query.list.length === 0 ? ( ) : ( <>
{query.list.map((cluster) => ( ))}
)} setDetailCluster(null)} /> { if (!open) setDeleteTarget(null) }} name={deleteTarget?.name} loading={deleting} description={ refCount.dep + refCount.col > 0 ? `确定要删除「${deleteTarget?.name}」吗?警告:仍有 ${refCount.dep} 个部署、${refCount.col} 个采集策略引用该集群,删除后它们将无法工作。` : undefined } onConfirm={handleDelete} />
) }) export default ClustersPage