import { useState, useEffect, useCallback } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Checkbox } from '@/components/ui/checkbox'; import { Input } from '@/components/ui/input'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from '@/components/ui/dialog'; import { Cloud, Key, CheckCircle, ChevronRight, ChevronLeft, Server, RefreshCw, Check, MapPin, Search, AlertCircle, } from 'lucide-react'; import { cloudApi } from '@/services/api'; interface CloudServerImportProps { open: boolean; onClose: () => void; } const cloudProviders = [ { id: 'aws', name: 'AWS', color: 'bg-orange-500', icon: 'AWS' }, { id: 'aliyun', name: '阿里云', color: 'bg-orange-600', icon: 'AL' }, { id: 'tencent', name: '腾讯云', color: 'bg-blue-500', icon: 'TX' }, { id: 'huawei', name: '华为云', color: 'bg-red-500', icon: 'HW' }, { id: 'azure', name: 'Azure', color: 'bg-blue-600', icon: 'AZ' }, ]; export function CloudServerImport({ open, onClose }: CloudServerImportProps) { const [cloudAccounts, setCloudAccounts] = useState([]); const [step, setStep] = useState(1); const [selectedProvider, setSelectedProvider] = useState(''); const [selectedAccount, setSelectedAccount] = useState(null); // 区域相关状态 const [regions, setRegions] = useState([]); const [selectedRegions, setSelectedRegions] = useState([]); const [regionSearch, setRegionSearch] = useState(''); const [isSyncingRegions, setIsSyncingRegions] = useState(false); // 主机相关状态 const [cloudServers, setCloudServers] = useState([]); const [selectedServers, setSelectedServers] = useState([]); const [isDiscovering, setIsDiscovering] = useState(false); const [discoverErrors, setDiscoverErrors] = useState([]); const [isImporting, setIsImporting] = useState(false); const [importResult, setImportResult] = useState<{ imported: number; skipped: number } | null>(null); const [defaultSshPort, setDefaultSshPort] = useState('22'); const [credentials, setCredentials] = useState([]); const [selectedCredentialId, setSelectedCredentialId] = useState(''); const loadAccounts = async () => { try { const res: Record = await cloudApi.getAccounts(); setCloudAccounts(res?.list || []); } catch (e) { console.error('加载云账号失败:', e); } }; useEffect(() => { if (open) { loadAccounts(); import('@/services/api').then(({ credentialApi }) => { credentialApi.list().then(( res: any) => setCredentials(res?.list || [])) .catch(() => setCredentials([])); }); } }, [open]); const filteredAccounts = cloudAccounts.filter( acc => acc.provider === selectedProvider && acc.status === 'active' ); // ==================== 区域同步逻辑 ==================== const loadRegions = useCallback(async (accountId: string | number) => { try { const res: Record = await cloudApi.getAccountRegions(accountId); return res?.list || []; } catch { return []; } }, []); const syncRegions = useCallback(async (accountId: string | number) => { setIsSyncingRegions(true); try { await cloudApi.syncRegions(accountId); const list = await loadRegions(accountId); setRegions(list); } catch (e: any) { console.error('同步区域失败:', e); throw e; } finally { setIsSyncingRegions(false); } }, [loadRegions]); // 进入 Step 3 时自动加载/同步区域 const enterRegionStep = useCallback(async () => { if (!selectedAccount) return; // 先尝试加载本地已有的区域 const local = await loadRegions(selectedAccount.id); setRegions(local); // 如果本地没有区域数据,自动触发同步 if (local.length === 0) { try { await syncRegions(selectedAccount.id); } catch { // 同步失败不阻塞用户,可以手动重试 } } }, [selectedAccount, loadRegions, syncRegions]); // ==================== 主机发现逻辑 ==================== const discoverInstances = useCallback(async () => { if (!selectedAccount || selectedRegions.length === 0) return; setIsDiscovering(true); setDiscoverErrors([]); setCloudServers([]); setSelectedServers([]); try { const res: Record = await cloudApi.discoverInstances(selectedAccount.id, selectedRegions); setCloudServers(res?.list || []); setDiscoverErrors(res?.errors || []); } catch (e: any) { console.error('发现主机失败:', e); setDiscoverErrors([e?.message || '发现主机失败']); } finally { setIsDiscovering(false); } }, [selectedAccount, selectedRegions]); // ==================== 导入逻辑 ==================== const handleImport = async () => { if (!selectedAccount || selectedServers.length === 0) return; setIsImporting(true); try { const instancesToImport = cloudServers.filter(s => selectedServers.includes(s.instance_id)); const res: Record = await cloudApi.importInstances(selectedAccount.id, instancesToImport, { default_port: Number(defaultSshPort) || 22, credential_id: selectedCredentialId ? Number(selectedCredentialId) : undefined, }); setImportResult((res as any) || { imported: 0, skipped: 0 }); } catch (e: any) { console.error('导入失败:', e); } finally { setIsImporting(false); } }; // ==================== 步骤导航 ==================== const handleNext = () => { if (step === 2 && selectedAccount && step < 4) { setStep(3); enterRegionStep(); } else if (step < 4) { setStep(step + 1); } }; const handleBack = () => { if (step > 1) setStep(step - 1); }; const handleSelectAllRegions = () => { if (selectedRegions.length === regions.length && regions.length > 0) { setSelectedRegions([]); } else { setSelectedRegions(regions.map(r => r.code)); } }; const handleServerToggle = (instanceId: string) => { setSelectedServers(prev => prev.includes(instanceId) ? prev.filter(id => id !== instanceId) : [...prev, instanceId] ); }; const handleSelectAllServers = () => { if (selectedServers.length === cloudServers.length && cloudServers.length > 0) { setSelectedServers([]); } else { setSelectedServers(cloudServers.map(s => s.instance_id)); } }; const handleClose = () => { onClose(); setStep(1); setSelectedProvider(''); setSelectedAccount(null); setSelectedRegions([]); setRegions([]); setCloudServers([]); setSelectedServers([]); setRegionSearch(''); setDiscoverErrors([]); setImportResult(null); setDefaultSshPort('22'); setSelectedCredentialId(''); }; const filteredRegions = regions.filter(r => !regionSearch || r.name?.toLowerCase().includes(regionSearch.toLowerCase()) || r.code?.toLowerCase().includes(regionSearch.toLowerCase()) ); const steps = [ { id: 1, title: '公有云', icon: Cloud }, { id: 2, title: '访问凭据', icon: Key }, { id: 3, title: '选择区域', icon: MapPin }, { id: 4, title: '导入确认', icon: CheckCircle }, ]; return ( 导入云主机 从云平台批量导入主机资产 {/* Step Indicator */}
{steps.map((s, index) => (
s.id ? 'bg-emerald-600/20 text-emerald-400' : 'bg-[var(--dg-bg-secondary)] text-[var(--dg-text-muted)]' }`}> {s.title} {step > s.id && }
{index < steps.length - 1 && ( )}
))}
{/* Step 1: Select Cloud Provider */} {step === 1 && (

请选择要导入云主机的云平台

{cloudProviders.map((provider) => ( setSelectedProvider(provider.id)} >
{provider.icon}
{provider.name}
))}
)} {/* Step 2: Select Account */} {step === 2 && (

请选择访问凭据(AccessKey)

{filteredAccounts.length > 0 ? (
{filteredAccounts.map((account) => ( setSelectedAccount(account)} >

{account.name}

{(account.access_key || account.accessKey || '******').slice(0, 20)}...

{account.region_count ?? account.regionCount ?? 0} 区域

{account.server_count ?? account.serverCount ?? 0} 服务器

))}
) : (

该云平台暂无有效的访问凭据

)}
)} {/* Step 3: Select Regions (NEW) */} {step === 3 && (

请选择要同步的区域 {regions.length > 0 && ( (共 {regions.length} 个区域,已选 {selectedRegions.length} 个) )}

{isSyncingRegions ? (

正在从云平台同步区域列表...

) : regions.length === 0 ? (

暂无区域数据

) : ( <> {/* 搜索框 + 全选 */}
setRegionSearch(e.target.value)} className="pl-9 bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]" />
{/* 区域列表 */}
{filteredRegions.map((region) => ( { setSelectedRegions(prev => prev.includes(region.code) ? prev.filter(r => r !== region.code) : [...prev, region.code] ); }} > ))}
0} onCheckedChange={handleSelectAllRegions} /> 区域编码 区域名称 Endpoint
{region.code} {region.name} {region.endpoint || '-'}
)}
)} {/* Step 4: Discover & Import Instances */} {step === 4 && (
{importResult ? ( /* 导入结果 */

导入完成

成功导入 {importResult.imported} 台 {importResult.skipped > 0 && ( 跳过 {importResult.skipped} 台(已存在) )}
) : ( <>

已选 {selectedRegions.length} 个区域 {cloudServers.length > 0 && ( · 发现 {cloudServers.length} 台主机 )}

{cloudServers.length > 0 && ( <> 已选 {selectedServers.length} 台 )}
{/* 区域标签 */}
{selectedRegions.map(r => { const region = regions.find(reg => reg.code === r); return ( {region?.name || r} ); })}
{/* 发现错误提示 */} {discoverErrors.length > 0 && (

部分区域发现失败:

{discoverErrors.map((err, i) => (

{err}

))}
)} {/* 主机列表 */} {isDiscovering ? (

正在发现云主机,请稍候...

正在逐区域查询,可能需要数秒

) : cloudServers.length === 0 && !isDiscovering ? (

点击"发现主机"按钮,开始扫描所选区域

) : (
{cloudServers.map((server) => ( handleServerToggle(server.instance_id)} > ))}
0} onCheckedChange={handleSelectAllServers} /> 主机名 IP地址
{server.instance_name || server.hostname || '-'} {server.private_ip || server.public_ip || '-'}
)} {/* SSH 端口 + 凭证关联 */} {cloudServers.length > 0 && selectedServers.length > 0 && (
setDefaultSshPort(e.target.value)} className="w-32 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
)} )}
)} {/* Footer */}
{step > 1 && !importResult && ( )}
{importResult ? ( ) : ( <> {step < 4 ? ( ) : ( )} )}
); }