/* eslint-disable @typescript-eslint/no-use-before-define */ import type { MenuProps } from 'antd'; import { Button, Dropdown } from 'antd'; import React, { useEffect, useRef, useState } from 'react'; // import { RFB } from '@novnc/novnc/core/rfb'; import RFB from '@/public/novnc/core/rfb'; import './index.less'; interface VncRemoteDesktopProps { vncUrl: string; password?: string; onConnected?: () => void; onDisconnected?: () => void; onError?: (error: string) => void; loadingText?: string; className?: string; isFullscreen?: boolean; viewOnly?: boolean; autoScale?: boolean; maxRetries?: number; retryInterval?: number; } /** * 封装的noVNC远程桌面控制组件 * 使用项目中已安装的@novnc/novnc包 */ const VncRemoteDesktop: React.FC = ({ vncUrl, password, onConnected, onDisconnected, onError, loadingText = '正在连接远程桌面...', className = '', viewOnly = false, autoScale = true, maxRetries = 3, retryInterval = 2000, }) => { const canvasRef = useRef(null); const rfbRef = useRef(null); const retryCountRef = useRef(0); const retryTimeoutRef = useRef(null); const [connectionStatus, setConnectionStatus] = useState< 'disconnected' | 'connecting' | 'connected' >('disconnected'); const [errorMessage, setErrorMessage] = useState(null); const [visible, setVisible] = useState(false); // 监听浏览器窗口关闭事件 useEffect(() => { const handleBeforeUnload = (event: BeforeUnloadEvent) => { event.preventDefault(); disconnect(); return ''; }; window.addEventListener('beforeunload', handleBeforeUnload); return () => { window.removeEventListener('beforeunload', handleBeforeUnload); }; }, []); // 清除重试定时器 const clearRetryTimeout = () => { if (retryTimeoutRef.current) { clearTimeout(retryTimeoutRef.current); retryTimeoutRef.current = null; } }; // 连接到VNC服务器 const connect = (resetRetry = false) => { if (!vncUrl || !canvasRef.current) { return; } // 清除之前的重试定时器 clearRetryTimeout(); // 重置重试计数(如果需要) if (resetRetry) { retryCountRef.current = 0; } // 断开已有连接 if (rfbRef.current) { disconnect(); } setConnectionStatus('connecting'); setErrorMessage(null); try { // 验证URL格式 if (!vncUrl.startsWith('ws://') && !vncUrl.startsWith('wss://')) { throw new Error('无效的VNC URL格式,请使用ws://或wss://开头'); } console.log('WebSocket URL=========', vncUrl); console.log('尝试连接到VNC服务器:', vncUrl); // 创建RFB实例 const rfb = new RFB(canvasRef.current, vncUrl, { credentials: password ? { password } : undefined, shared: true, wsProtocols: ['binary'], // focusOnClick: !viewOnly, dragViewport: true, scaleViewport: true, resizeSession: true, // viewOnly: viewOnly, // background: '#000000', }); // 保存RFB实例引用,用于后续操作和事件处理 rfbRef.current = rfb; console.log('rfbRef.current=====保存RFB实例引用', rfbRef.current); // 监听连接事件 rfb.addEventListener('connect', () => { console.log('VNC连接成功'); retryCountRef.current = 0; // 重置重试计数 setConnectionStatus('connected'); onConnected?.(); }); // 监听断开连接事件 rfb.addEventListener('disconnect', () => { console.log('VNC连接断开'); setConnectionStatus('disconnected'); onDisconnected?.(); console.log('rfbRef.current=====监听断开连接事件', rfbRef.current); }); // 监听安全失败事件 rfb.addEventListener('securityfailure', (e: any) => { console.error('VNC安全连接失败:', e.detail); setErrorMessage('安全连接失败: ' + e.detail); setConnectionStatus('disconnected'); onError?.('安全连接失败: ' + e.detail); console.log('rfbRef.current=====监听安全失败事件', rfbRef.current); rfbRef.current = null; }); // 监听凭证请求事件 rfb.addEventListener('credentialsrequired', () => { console.log('需要凭证'); if (password && rfbRef.current) { rfbRef.current.sendCredentials({ password }); } }); // 监听错误事件 rfb.addEventListener('error', (e: any) => { const errorDetail = e.detail || {}; const errorMsg = errorDetail.message || errorDetail || '未知错误'; console.error('VNC错误:', errorMsg); // 提供更具体的错误信息 let userFriendlyError = ''; if (errorMsg.toString().includes('WebSocket')) { userFriendlyError = `WebSocket连接失败: ${errorMsg}\n可能的原因: 服务器不可达、网络问题或防火墙阻止`; } else if (errorMsg.toString().includes('401')) { userFriendlyError = '认证失败: 用户名或密码错误'; } else if (errorMsg.toString().includes('403')) { userFriendlyError = '权限不足: 您没有访问该远程桌面的权限'; } else if (errorMsg.toString().includes('timeout')) { userFriendlyError = '连接超时: 服务器响应超时,请检查网络连接'; } else { userFriendlyError = `连接错误: ${errorMsg}`; } setErrorMessage(userFriendlyError); setConnectionStatus('disconnected'); onError?.(userFriendlyError); // 自动重试连接(如果未达到最大重试次数) if (retryCountRef.current < maxRetries) { retryCountRef.current++; console.log(`尝试重新连接 (${retryCountRef.current}/${maxRetries})`); retryTimeoutRef.current = setTimeout(() => { connect(); }, retryInterval); } }); console.log('RFB连接配置完成'); } catch (error) { console.error('创建VNC连接失败:', error); const errorMsg = error instanceof Error ? error.message : '创建连接失败'; setErrorMessage(errorMsg); setConnectionStatus('disconnected'); onError?.(errorMsg); } }; // 断开连接 const disconnect = () => { // 清除重试定时器 clearRetryTimeout(); console.log('rfbRef.current=====断开连接', rfbRef); console.log('rfbRef.current=====断开连接', rfbRef.current); if (rfbRef.current) { try { // 确保完全断开连接并释放所有资源 rfbRef.current.disconnect(); } catch (error) { console.error('Error during disconnect/cleanup:', error); } } // 重置重试计数 retryCountRef.current = 0; }; // 连接 const reconnect = () => { setVisible(true); connect(true); // 重置重试计数后连接 }; // 当vncUrl或password变化时重新连接 useEffect(() => { if (vncUrl && visible) { connect(true); // 重置重试计数后连接 } // 组件卸载时断开连接并清理资源 return () => { disconnect(); clearRetryTimeout(); }; }, [visible, vncUrl, password, maxRetries, retryInterval]); // 当autoScale或viewOnly属性变化时更新RFB实例 useEffect(() => { if (rfbRef.current) { rfbRef.current.viewOnly = viewOnly; rfbRef.current.focusOnClick = !viewOnly; rfbRef.current.dragViewport = !viewOnly; rfbRef.current.scaleViewport = autoScale; rfbRef.current.resizeSession = autoScale; } }, [viewOnly, autoScale]); const menuItems: MenuProps = { items: [ { key: '1', label:
挂载优化工具
, // label: ( // // ), }, { key: '2', // 注意 key 应该唯一 label:
挂载应用软件盘
, // label: ( // // ), }, { key: '3', label:
挂载应用软件盘
, // label: ( // // ), }, ], }; return (
{/* 控制栏 */}
{connectionStatus === 'connecting' && '连接中...'} {connectionStatus === 'connected' && '已连接'} {connectionStatus === 'disconnected' && '已断开'}
{viewOnly && 仅查看模式} {!(connectionStatus === 'connected') ? ( ) : ( <> )}
{/* 远程桌面内容 */}
{/* 加载状态 */} {connectionStatus === 'connecting' && (
{loadingText}
)} {/* 错误状态 */} {errorMessage && connectionStatus === 'disconnected' && (
{errorMessage}
)} {/* VNC画布,使用canvas远程桌面控制页面无法展示,不要使用 */}
{/* 未连接时的占位符 */} {connectionStatus === 'disconnected' && !errorMessage && (
🖥️
等待连接...
)}
); }; export default VncRemoteDesktop;