- 协议检测工具类 (src/utils/protocolCheck.js)
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: false || !!document.documentMode
}
}
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);
}
};
}
}
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>