diff --git a/frontend/src/components/WebVNCViewer.tsx b/frontend/src/components/WebVNCViewer.tsx index 2ac948f..cc9f684 100644 --- a/frontend/src/components/WebVNCViewer.tsx +++ b/frontend/src/components/WebVNCViewer.tsx @@ -1,8 +1,39 @@ import { useEffect, useRef, useState } from 'react' import { Monitor, RefreshCw, Send, X } from 'lucide-react' -import RFB from '@novnc/novnc' +import RFBModule from '@novnc/novnc/lib/rfb' import { createVNCTicket, getWebVNCUrl } from '../services/api' +type RFBConstructor = new ( + target: HTMLElement, + url: string, + options?: { credentials?: Record; shared?: boolean; repeaterID?: string; wsProtocols?: string[] } +) => RFBInstance + +interface RFBInstance extends EventTarget { + scaleViewport: boolean + resizeSession: boolean + focusOnClick: boolean + viewOnly: boolean + qualityLevel: number + compressionLevel: number + background: string + disconnect(): void + sendCtrlAltDel(): void +} + +const RFB = resolveRFBConstructor(RFBModule) + +function resolveRFBConstructor(moduleValue: unknown): RFBConstructor { + if (typeof moduleValue === 'function') { + return moduleValue as RFBConstructor + } + const maybeDefault = (moduleValue as { default?: unknown })?.default + if (typeof maybeDefault === 'function') { + return maybeDefault as RFBConstructor + } + throw new Error('noVNC RFB constructor is unavailable') +} + interface WebVNCViewerProps { containerName: string onClose: () => void @@ -10,7 +41,7 @@ interface WebVNCViewerProps { export default function WebVNCViewer({ containerName, onClose }: WebVNCViewerProps) { const screenRef = useRef(null) - const rfbRef = useRef(null) + const rfbRef = useRef(null) const [status, setStatus] = useState<'connecting' | 'connected' | 'disconnected' | 'error'>('connecting') const [errorMsg, setErrorMsg] = useState('') @@ -80,6 +111,7 @@ export default function WebVNCViewer({ containerName, onClose }: WebVNCViewerPro setErrorMsg(error.response?.data?.message || 'WebVNC ticket 创建失败,请重新登录后再试') return } + if (!ticket) { setStatus('error') setErrorMsg('WebVNC ticket 为空,请重新登录后再试') @@ -97,9 +129,7 @@ export default function WebVNCViewer({ containerName, onClose }: WebVNCViewerPro rfb.qualityLevel = 6 rfb.compressionLevel = 2 rfb.background = '#050505' - rfb.addEventListener('connect', () => { - setStatus('connected') - }) + rfb.addEventListener('connect', () => setStatus('connected')) rfb.addEventListener('disconnect', (event) => { const detail = (event as CustomEvent<{ clean?: boolean }>).detail setStatus((current) => current === 'error' ? current : 'disconnected') @@ -133,35 +163,31 @@ export default function WebVNCViewer({ containerName, onClose }: WebVNCViewerPro }, [containerName]) return ( -
-
+
+
- + WebVNC - {containerName} - {status === 'connected' && 已连接} - {status === 'connecting' && 连接中...} - {status === 'disconnected' && 已断开} - {status === 'error' && 连接失败} + {status === 'connected' && 已连接} + {status === 'connecting' && 连接中...} + {status === 'disconnected' && 已断开} + {status === 'error' && 连接失败}
- - -
-
+
{(status === 'connecting' || status === 'error' || (status === 'disconnected' && errorMsg)) && (
diff --git a/frontend/src/novnc.d.ts b/frontend/src/novnc.d.ts index 89e0793..f2058c0 100644 --- a/frontend/src/novnc.d.ts +++ b/frontend/src/novnc.d.ts @@ -1,4 +1,4 @@ -declare module '@novnc/novnc' { +declare module '@novnc/novnc/lib/rfb' { export default class RFB extends EventTarget { constructor(target: HTMLElement, url: string, options?: { credentials?: Record; shared?: boolean; repeaterID?: string; wsProtocols?: string[] }) scaleViewport: boolean