import { useEffect, useMemo, useState } from 'react' import { CheckCircle2, Clock, Command, Copy, Play, RefreshCw, Server, Terminal, XCircle } from 'lucide-react' import Modal from './Modal' import { executeBatchCommand } from '../services/connections' import type { BatchCommandResult, Connection, ConnectionReachabilityStatus, ConnectionStatusItem } from '../types' const reachabilityCopy: Record = { unknown: { dot: 'bg-slate-500', label: '未检测', text: 'text-slate-500' }, checking: { dot: 'bg-amber-400', label: '检测中', text: 'text-amber-300' }, online: { dot: 'bg-emerald-500 shadow-[0_0_4px_rgba(16,185,129,0.5)]', label: '在线', text: 'text-emerald-500/80' }, offline: { dot: 'bg-slate-500', label: '离线', text: 'text-slate-500' }, } export default function BatchCommandModal({ open, connections, connectionStatuses, connectionStatusDetails, onRefreshStatuses, statusError, statusLoading, onClose, }: { open: boolean connections: Connection[] connectionStatuses: Record connectionStatusDetails: Record onRefreshStatuses: () => Promise statusError: string | null statusLoading: boolean onClose: () => void }) { const [selectedIds, setSelectedIds] = useState(() => connections.slice(0, 2).map((item) => item.id)) const [command, setCommand] = useState('df -h\nfree -m') const [running, setRunning] = useState(false) const [results, setResults] = useState([]) const [error, setError] = useState(null) const summary = useMemo( () => ({ total: selectedIds.length, success: results.filter((item) => item.success).length, failure: results.filter((item) => !item.success).length, }), [results, selectedIds.length], ) const onlineIds = useMemo( () => connections.filter((item) => connectionStatuses[item.id] === 'online').map((item) => item.id), [connectionStatuses, connections], ) const offlineCount = useMemo( () => connections.filter((item) => connectionStatuses[item.id] === 'offline').length, [connectionStatuses, connections], ) useEffect(() => { setSelectedIds((prev) => prev.filter( (id) => connections.some((item) => item.id === id) && connectionStatuses[id] === 'online', ), ) }, [connectionStatuses, connections]) if (!open) return null async function handleRun() { const runnableIds = selectedIds.filter((id) => connectionStatuses[id] === 'online') if (!runnableIds.length || !command.trim()) return setRunning(true) setError(null) try { const response = await executeBatchCommand(runnableIds, command) setResults(response.data.results) } catch (err) { const message = (err as { response?: { data?: { message?: string; error?: string } } }).response?.data?.message || '批量执行失败' setError(message) } finally { setRunning(false) } } return (
目标主机 ({selectedIds.length})
在线 {onlineIds.length} / 离线 {offlineCount} / 总数 {connections.length}
{connections.map((connection) => { const checked = selectedIds.includes(connection.id) const reachability = connectionStatuses[connection.id] ?? 'unknown' const reachabilityMeta = reachabilityCopy[reachability] const statusDetail = connectionStatusDetails[connection.id] const disabled = reachability !== 'online' return ( ) })}