import { useEffect, useRef, useState } from 'react' import { Monitor, RefreshCw, Send, X } from 'lucide-react' import RFB from '@novnc/novnc' import { createVNCTicket, getWebVNCUrl } from '../services/api' interface WebVNCViewerProps { containerName: string onClose: () => void } export default function WebVNCViewer({ containerName, onClose }: WebVNCViewerProps) { const screenRef = useRef(null) const rfbRef = useRef(null) const [status, setStatus] = useState<'connecting' | 'connected' | 'disconnected' | 'error'>('connecting') const [errorMsg, setErrorMsg] = useState('') const cleanup = () => { if (rfbRef.current) { rfbRef.current.disconnect() rfbRef.current = null } } const connect = async () => { const target = screenRef.current if (!target) return cleanup() target.innerHTML = '' setStatus('connecting') setErrorMsg('') let ticket = '' try { const response = await createVNCTicket(containerName) ticket = response.data.data?.ticket || '' } catch (err: unknown) { const error = err as { response?: { data?: { message?: string } } } setStatus('error') setErrorMsg(error.response?.data?.message || 'WebVNC ticket 创建失败,请重新登录后再试') return } if (!ticket) { setStatus('error') setErrorMsg('WebVNC ticket 为空,请重新登录后再试') return } try { const rfb = new RFB(target, getWebVNCUrl(containerName, ticket)) rfb.scaleViewport = true rfb.resizeSession = false rfb.focusOnClick = true rfb.qualityLevel = 6 rfb.compressionLevel = 2 rfb.background = '#050505' rfb.addEventListener('connect', () => { setStatus('connected') }) rfb.addEventListener('disconnect', (event) => { const detail = (event as CustomEvent<{ clean?: boolean }>).detail setStatus((current) => current === 'error' ? current : 'disconnected') if (detail && detail.clean === false) { setErrorMsg('WebVNC 连接已断开,请确认虚拟机正在运行且 VNC 控制台可用') } }) rfb.addEventListener('securityfailure', () => { setStatus('error') setErrorMsg('VNC 安全协商失败') }) rfb.addEventListener('credentialsrequired', () => { setStatus('error') setErrorMsg('当前 VNC 控制台要求密码,暂不支持自动输入') }) rfbRef.current = rfb } catch (err) { console.error(err) setStatus('error') setErrorMsg('WebVNC 初始化失败') } } useEffect(() => { const timer = window.setTimeout(connect, 100) return () => { window.clearTimeout(timer) cleanup() } }, [containerName]) return (
WebVNC - {containerName} {status === 'connected' && 已连接} {status === 'connecting' && 连接中...} {status === 'disconnected' && 已断开} {status === 'error' && 连接失败}
{(status === 'connecting' || status === 'error' || (status === 'disconnected' && errorMsg)) && (
{status === 'connecting' ? '正在连接 KVM VNC 控制台...' : (errorMsg || 'WebVNC 已断开')}
)}
) }