前端vue2中直接拉起vnc客户端实现方式
作者:隐含
本文介绍Vue项目中协议检测工具类与组件的实现,通过封装检测逻辑提升复用性,并展示如何在组件中应用工具类进行协议校验与数据展示
前端vue2中直接拉起vnc客户端
协议检测工具类
(src/utils/protocolCheck.js)
//1. 协议检测工具类 (src/utils/protocolCheck.js) //javascript // 浏览器检测函数 function checkBrowser() { const isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; const ua = navigator.userAgent.toLowerCase(); return { isOpera: isOpera, isFirefox: typeof InstallTrigger !== 'undefined', isSafari: (~ua.indexOf('safari') && !~ua.indexOf('chrome')) || Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0, isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream, isChrome: !!window.chrome && !isOpera, isIE: /*@cc_on!@*/false || !!document.documentMode } } // IE版本检测 function getInternetExplorerVersion() { let rv = -1; if (navigator.appName === 'Microsoft Internet Explorer') { const ua = navigator.userAgent; const re = new RegExp('MSIE ([0-9]{1,}[\.0-9]{0,})'); if (re.exec(ua) != null) rv = parseFloat(RegExp.$1); } else if (navigator.appName === 'Netscape') { const ua = navigator.userAgent; const re = new RegExp('Trident/.*rv:([0-9]{1,}[\.0-9]{0,})'); if (re.exec(ua) != null) rv = parseFloat(RegExp.$1); } return rv; } // 事件注册辅助函数 function _registerEvent(target, eventType, cb) { if (target.addEventListener) { target.addEventListener(eventType, cb); return { remove: function() { target.removeEventListener(eventType, cb); } }; } else { target.attachEvent('on' + eventType, cb); return { remove: function() { target.detachEvent('on' + eventType, cb); } }; } } // 创建隐藏iframe function _createHiddenIframe(target, uri) { const iframe = document.createElement('iframe'); iframe.src = uri; iframe.id = 'hiddenIframe'; iframe.style.display = 'none'; target.appendChild(iframe); return iframe; } // 各种浏览器处理函数 function openUriWithHiddenFrame(uri, failCb, successCb) { const timeout = setTimeout(function() { failCb(); handler.remove(); }, 1000); let iframe = document.querySelector('#hiddenIframe'); if (!iframe) { iframe = _createHiddenIframe(document.body, 'about:blank'); } const handler = _registerEvent(window, 'blur', onBlur); function onBlur() { clearTimeout(timeout); handler.remove(); successCb(); } iframe.contentWindow.location.href = uri; } function openUriWithTimeoutHack(uri, failCb, successCb) { const timeout = setTimeout(function() { failCb(); handler.remove(); }, 1000); let target = window; while (target != target.parent) { target = target.parent; } const handler = _registerEvent(target, 'blur', onBlur); function onBlur() { clearTimeout(timeout); handler.remove(); successCb(); } window.location = uri; } function openUriUsingFirefox(uri, failCb, successCb) { let iframe = document.querySelector('#hiddenIframe'); if (!iframe) { iframe = _createHiddenIframe(document.body, 'about:blank'); } try { iframe.contentWindow.location.href = uri; successCb(); } catch (e) { if (e.name == 'NS_ERROR_UNKNOWN_PROTOCOL') { failCb(); } } } function openUriUsingIEInOlderWindows(uri, failCb, successCb) { const version = getInternetExplorerVersion(); if (version === 10) { openUriUsingIE10InWindows7(uri, failCb, successCb); } else if (version === 9 || version === 11) { openUriWithHiddenFrame(uri, failCb, successCb); } else { openUriInNewWindowHack(uri, failCb, successCb); } } function openUriUsingIE10InWindows7(uri, failCb, successCb) { const timeout = setTimeout(failCb, 1000); window.addEventListener('blur', function() { clearTimeout(timeout); successCb(); }); let iframe = document.querySelector('#hiddenIframe'); if (!iframe) { iframe = _createHiddenIframe(document.body, 'about:blank'); } try { iframe.contentWindow.location.href = uri; } catch (e) { failCb(); clearTimeout(timeout); } } function openUriInNewWindowHack(uri, failCb, successCb) { const myWindow = window.open('', '', 'width=0,height=0'); myWindow.document.write("<iframe src='" + uri + "'></iframe>"); setTimeout(function() { try { myWindow.location.href; myWindow.setTimeout('window.close()', 1000); successCb(); } catch (e) { myWindow.close(); failCb(); } }, 1000); } function openUriWithMsLaunchUri(uri, failCb, successCb) { navigator.msLaunchUri(uri, successCb, failCb); } // 主导出函数 export function protocolCheck(uri, failCb, successCb, unsupportedCb) { function failCallback() { failCb && failCb(); } function successCallback() { successCb && successCb(); } if (navigator.msLaunchUri) { openUriWithMsLaunchUri(uri, failCallback, successCallback); } else { const browser = checkBrowser(); if (browser.isFirefox) { openUriUsingFirefox(uri, failCallback, successCallback); } else if (browser.isChrome || browser.isIOS) { openUriWithTimeoutHack(uri, failCallback, successCallback); } else if (browser.isIE) { openUriUsingIEInOlderWindows(uri, failCallback, successCallback); } else if (browser.isSafari) { openUriWithHiddenFrame(uri, failCallback, successCallback); } else { unsupportedCb && unsupportedCb(); } } }
Vue 组件实现
(src/components/ProtocolChecker.vue)
<template> <div class="protocol-checker"> <el-card shadow="hover"> <template #header> <div class="card-header"> <span>VNC 连接器</span> </div> </template> <el-form label-position="top"> <el-form-item label="VNC 连接地址"> <el-input v-model="vncUrl" placeholder="例如: com.realvnc.vncviewer.connect://10.0.66.98:20" clearable> </el-input> </el-form-item> <el-form-item> <el-button type="primary" :loading="loading" @click="handleProtocolCheck" icon="el-icon-connection"> 启动连接 </el-button> </el-form-item> </el-form> </el-card> <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="30%" :close-on-click-modal="false"> <div class="dialog-content"> <el-alert :title="dialogMessage" :type="alertType" :closable="false" show-icon> </el-alert> </div> <template #footer> <el-button @click="dialogVisible = false">关闭</el-button> <el-button v-if="showDownload" type="primary" @click="openDownloadPage"> 下载 VNC Viewer </el-button> </template> </el-dialog> </div> </template> <script> import { protocolCheck } from '@/utils/protocolCheck' export default { name: 'ProtocolChecker', data() { return { loading: false, dialogVisible: false, dialogTitle: '', dialogMessage: '', alertType: 'info', showDownload: false, vncUrl: 'com.realvnc.vncviewer.connect://10.0.66.98:20' } }, methods: { handleProtocolCheck() { if (!this.vncUrl) { this.showDialog('错误', '请输入有效的VNC连接地址', 'error') return } this.loading = true protocolCheck( //直接用这个方法就可以了 this.vncUrl, () => { // 失败回调 this.showDialog( '未检测到VNC Viewer', '您的系统未安装VNC Viewer或协议不受支持', 'error', true ) this.loading = false }, () => { // 成功回调 this.showDialog( '正在连接', '正在启动VNC Viewer应用程序...', 'success' ) this.loading = false }, () => { // 浏览器不支持回调 this.showDialog( '浏览器不支持', '您的浏览器不支持此功能,请使用Chrome/Firefox/Edge浏览器', 'warning' ) this.loading = false } ) }, showDialog(title, message, type = 'info', showDownload = false) { this.dialogTitle = title this.dialogMessage = message this.alertType = type this.showDownload = showDownload this.dialogVisible = true }, openDownloadPage() { window.open('https://www.realvnc.com/en/connect/download/viewer/', '_blank') this.dialogVisible = false } } } </script> <style scoped> .protocol-checker { max-width: 600px; margin: 0 auto; padding: 20px; } .card-header { display: flex; justify-content: space-between; align-items: center; } .dialog-content { margin-bottom: 20px; } </style>
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。