// 与客户端交互方法列表
|
import wsPlugins from './plugins/index';
|
// const ReWebSocket = require('reconnecting-websocket');
|
// websocket重连
|
import ReWebSocket from '../node_modules/reconnecting-websocket/dist/reconnecting-websocket-cjs';
|
// const Bowser = require('bowser');
|
// 浏览器信息获取
|
import Bowser from "../node_modules/bowser/es5";
|
const browser = Bowser.getParser(window.navigator.userAgent);
|
|
interface callbacks {
|
// 连接客户端状态
|
connectResult: any,
|
// 登录客户端状态
|
loginResult: any
|
}
|
|
|
export default class Ws {
|
[x: string]: any;
|
// 连接地址
|
static url: string = '';
|
// 传入的配置项(方法)
|
static opts: Object = {};
|
// 密码加密公钥
|
static publicKey = '';
|
// 是否连接客户端
|
private isConnectSuccessQt: Boolean = false;
|
// 是否登录客户端
|
private isLoginSuccess: Boolean = false;
|
// 唯一实例
|
private static _instance: any = null;
|
// 当前连接的webSocket
|
webSocket: any;
|
// 最大重连数
|
reConnectCount: number = 3;
|
// 连接失败次数
|
connectFailCount: number = 0;
|
// 重连完成标识
|
connectEnd: Boolean = false;
|
// 登录完成标识
|
loginEnd: Boolean = false;
|
// 最大重登数
|
reLoginCount: number = 1;
|
// 连接失败次数
|
loginFailCount: number = 0;
|
// 当前websocket登录配置
|
config: Object = {};
|
// 登陆IP
|
loginIp: string;
|
// 登陆端口
|
loginPort: string;
|
// 登录用户名
|
userName: string;
|
// 登录密码,密码与token二选一
|
userPwd: string;
|
// 登录token,密码与token二选一
|
token: string;
|
// 用户标识符
|
userCode: Number = 0;
|
// 回调函数
|
private callback: callbacks;
|
// 创建的控件ID列表
|
ids: Array<String> = [];
|
// 创建的控件列表
|
ctrls: Array<String> = [];
|
// 心跳
|
heartBeatTimer: any = null;
|
// 监听的方法表
|
listerns: any;
|
constructor({
|
url= 'ws://localhost:1234',
|
publicKey= 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbEpPpxpLJft4W9YZj8bRh2bYYZshBEsKOlxgyn11rlEyTasjBSZRV9aj33tvQ2T55izH0fWl+dL/dLZChawFrlGDcH8JuWge2xYMgII9mggcYa0UiQ7pLXJ9ivXZ/cOY3HzrRQdR7dGTSNn3Z0Ctbns6mLgvlA2r3qMNs/8wHBwIDAQAB',
|
reConnectCount = 3,
|
reLoginCount = 1,
|
loginIp= location.hostname,
|
loginPort= location.port,
|
userName = '',
|
userPwd = '',
|
token = '',
|
callback = {
|
connectResult: null,
|
loginResult: null
|
}
|
} : {
|
url: string,
|
publicKey: string,
|
reConnectCount: number,
|
reLoginCount: number,
|
loginIp: string,
|
loginPort: string,
|
userName: string,
|
userPwd: string,
|
token: string
|
callback: callbacks,
|
}) {
|
// if (/https/.test(location.protocol)) {
|
// url = `wss:${url}`;
|
// } else {
|
// url = `ws:${url}`;
|
// }
|
// url = `ws://localhost:1234`;
|
|
this.url = url;
|
this.userCode = new Date().valueOf();
|
this.webSocket = null;
|
this.reConnectCount = reConnectCount;
|
this.reLoginCount = reLoginCount;
|
this.publicKey = publicKey;
|
this.loginIp = loginIp;
|
this.loginPort = loginPort;
|
this.userName = userName;
|
this.userPwd = userPwd;
|
this.token = token;
|
this.callback = callback;
|
|
this.connectFailCount = this.connectFailCount;
|
|
// 基础操作所需功能或属性
|
this.ids = [];
|
this.ctrls = [];
|
|
// 是否连接客户端
|
this.isConnectSuccessQt = false;
|
// 是否登陆客户端
|
this.isLoginSuccess = false;
|
this.heartBeatTimer = null;
|
|
// 初始化配置参数
|
this.initConfig();
|
|
// 连接客户端
|
this.connectQt();
|
|
// 获取与客户端交互方法列表
|
const plugins = [];
|
Object.keys(wsPlugins).forEach(item => {
|
if(hasKey(wsPlugins, item)){
|
plugins.push(wsPlugins[item]);
|
}
|
});
|
// 与客户端通讯功能注册
|
usePlugin(plugins);
|
|
// 用户注册监听事件表
|
this.listerns = new Map();
|
|
// 传入登录配置,则登录
|
if(this.userName) {
|
this.detectConnectQt().then((res: Boolean) => {
|
if(res) {
|
// 登陆客户端
|
this.loginClient();
|
// 心跳保活
|
this._heartbeat();
|
}
|
});
|
}
|
}
|
/**
|
* @description 获得实例对象
|
*/
|
public static getInstance(options: any):Ws {
|
if(!this._instance) {
|
this._instance = new Ws(options);
|
}
|
return this._instance;
|
}
|
/**
|
* @description 用户注册监听事件
|
* @params {String} eventType 事件名称
|
* @params {any} callback 回调函数
|
*/
|
on(eventType: String, callback: any) {
|
this.listerns.set(eventType, callback);
|
}
|
/**
|
* @description 用户取消监听事件
|
* @params {String} eventType 事件名称
|
*/
|
off(eventType: string) {
|
delete this.listerns[eventType];
|
}
|
/**
|
* @description 发送消息给客户端
|
* @params {String} method 事件名称
|
* @params {Object} data 传输消息的数据内容
|
*/
|
emit(method: any, data: { method: any; }) {
|
const { webSocket } = this;
|
data.method = method;
|
// 不需要判断登录和连接的方法过滤
|
let filterList = ['heartbeat', 'login', 'logout', 'browserInfo'];
|
if(filterList.includes(method)) {
|
webSocket.send(JSON.stringify(data));
|
return;
|
}
|
return new Promise((resolve, reject) => {
|
this.detectConnectQt().then(con => {
|
if(con) {
|
this.detectLoginClient().then(login => {
|
if(login) {
|
webSocket.send(JSON.stringify(data));
|
resolve(true);
|
} else {
|
// 登录失败
|
reject(2);
|
}
|
})
|
} else {
|
// 连接失败
|
reject(1);
|
}
|
})
|
})
|
}
|
/*
|
* 初始化配置
|
*/
|
initConfig() {
|
this.config = {
|
userName: this.userName,
|
userCode: this.userCode,
|
loginPort: this.loginPort,
|
loginIp: this.loginIp,
|
userPwd: this.userPwd,
|
token: this.token,
|
};
|
// 浏览器信息
|
const browserInfo = {
|
name: '',
|
version: '',
|
platform: ''
|
};
|
browserInfo.name = browser.getBrowserName().toLowerCase();
|
browserInfo.version = browser.getBrowser().version.toLowerCase();
|
browserInfo.platform =
|
browser._ua.indexOf('Win64') >= 0 || browser._ua.indexOf('Wow64') >= 0
|
? 'win64'
|
: 'win32';
|
this.config['browser'] = browserInfo;
|
}
|
/**
|
* @description 连接客户端
|
*/
|
connectQt() {
|
this.connectEnd = false;
|
// 连接客户端
|
this.webSocket = new ReWebSocket(this.url, '', {
|
maxRetries: this.reConnectCount
|
});
|
this.addEvents();
|
}
|
/**
|
* @description 检测连接客户端状态
|
*/
|
detectConnectQt() {
|
let _this = this;
|
return new Promise((resolve, reject) => {
|
if(!this.connectEnd) { // 连接中
|
let _interval = setInterval(() => {
|
if(_this.connectEnd) {
|
clearInterval(_interval);
|
resolve(_this.isConnectSuccessQt);
|
}
|
}, 50)
|
} else {
|
resolve(_this.isConnectSuccessQt);
|
}
|
})
|
}
|
/**
|
* @description 检测登录客户端状态
|
*/
|
detectLoginClient() {
|
let _this = this;
|
return new Promise((resolve, reject) => {
|
if(!this.loginEnd) { // 连接中
|
let _interval = setInterval(() => {
|
if(_this.loginEnd) {
|
clearInterval(_interval);
|
resolve(_this.isLoginSuccess);
|
}
|
}, 50)
|
} else {
|
resolve(_this.isLoginSuccess);
|
}
|
})
|
}
|
/**
|
* @description 登录客户端
|
* @params {Object} config 登录相关配置
|
*/
|
login(config: Object) {
|
this.loginEnd = false;
|
Object.assign(this.config, config);
|
// 未连接客户端
|
if(!this.isOpen()) {
|
// 连接失败
|
return false
|
} else {
|
// 若已登录,先注销
|
if (this.isLoginSuccess) {
|
this.logout();
|
}
|
// 登陆客户端
|
this.loginClient();
|
// 心跳保活
|
this._heartbeat();
|
}
|
}
|
/**
|
* @description 连接客户端完成后登陆
|
*/
|
// loginAfterConnectEnd() {
|
// // 未连接客户端
|
// if(!this.isOpen()) {
|
// // 连接失败
|
// return false
|
// } else {
|
// // 若已登录,先注销
|
// if (this.isLoginSuccess) {
|
// this.logout();
|
// }
|
// // 登陆客户端
|
// this.loginClient();
|
// // 心跳保活
|
// this._heartbeat();
|
// }
|
// }
|
/**
|
* @description 登出客户端
|
*/
|
logout() {
|
// 退出客户端
|
this.logoutClient();
|
this.isLoginSuccess = false;
|
if (typeof this.callback.loginResult === 'function') {
|
this.callback.loginResult.call(this, this.isLoginSuccess);
|
}
|
}
|
/**
|
* @description 添加websocket/window监听事件
|
*/
|
addEvents() {
|
const webSocket = this.webSocket;
|
webSocket.addEventListener('open', () => this.onOpen());
|
webSocket.addEventListener('message', (e: any) => this.onMessage(e));
|
webSocket.addEventListener('error', () => this.onError());
|
window.addEventListener('resize', () => this.reLocatedPosition());
|
window.addEventListener('scroll', () => this.reLocatedPosition());
|
}
|
/**
|
* @description 移除websocket/window监听事件
|
*/
|
removeEvents() {
|
const webSocket = this.webSocket;
|
webSocket.removeEventListener('open', this.onOpen);
|
webSocket.removeEventListener('message', this.onMessage);
|
webSocket.removeEventListener('error', this.onError);
|
webSocket.removeEventListener('resize', this.reLocatedPosition);
|
webSocket.removeEventListener('scroll', this.reLocatedPosition);
|
}
|
/**
|
* @description 连接客户端成功事件
|
*/
|
onOpen() {
|
this.isConnectSuccessQt = true;
|
this.connectEnd = true;
|
if (typeof this.callback.connectResult === 'function') {
|
this.callback.connectResult.call(this, this.isConnectSuccessQt);
|
}
|
}
|
/**
|
* @description 接收客户端消息
|
* @params {Object} event 接收客户端的消息数据
|
*/
|
onMessage(event: { data: string; }) {
|
try {
|
const data = JSON.parse(event.data);
|
const { method } = data;
|
const callback = this.listerns.get(method);
|
if (method === 'loginState') {
|
this.loginFailCount ++;
|
if(this.loginFailCount < this.reLoginCount + 1) { // 登录未到最大次数
|
if(data.params.loginResult === 0) { // 登录成功
|
this.isLoginSuccess = data.params.loginResult === 0;
|
if (typeof this.callback.loginResult === 'function') {
|
this.callback.loginResult.call(this, this.isLoginSuccess);
|
}
|
} else { // 登录失败
|
this.loginClient();
|
}
|
} else { // 登录达到最大次数
|
this.loginEnd = true;
|
this.isLoginSuccess = data.params.loginResult === 0;
|
if (typeof this.callback.loginResult === 'function') {
|
this.callback.loginResult.call(this, this.isLoginSuccess);
|
}
|
}
|
}
|
if (callback) {
|
if (method === 'loginState') {
|
callback(this.isLoginSuccess);
|
} else if (method === 'createCtrlResult') {
|
callback(data.params.array);
|
} else {
|
callback(data);
|
}
|
}
|
} catch (e) {
|
// console.error('error', e);
|
}
|
}
|
/**
|
* @description 客户端发生错误事件
|
*/
|
onError() {
|
this.isConnectSuccessQt = false;
|
clearTimeout(this.heartbeatTimer);
|
this.connectFailCount ++;
|
if(this.connectFailCount === this.reConnectCount + 1) {
|
this.connectEnd = true;
|
if (typeof this.callback.connectResult === 'function') {
|
this.callback.connectResult.call(this, this.isConnectSuccessQt);
|
}
|
}
|
}
|
/**
|
* @description 判断是否成功连接客户端
|
*/
|
isOpen() {
|
if (!this.webSocket) return false;
|
return this.webSocket.readyState === 1;
|
}
|
/**
|
* @description 心跳事件
|
*/
|
_heartbeat() {
|
this.heartbeat();
|
clearTimeout(this.heartbeatTimer);
|
this.heartbeatTimer = setTimeout(() => {
|
this._heartbeat();
|
}, 10000);
|
}
|
/**
|
* @description 获取当前浏览器缩放和滚动条信息
|
*/
|
getScrollInfo() {
|
let ratio = detectZoom();
|
let scrollX = window.pageXOffset;
|
let scrollY = window.pageYOffset;
|
var hasscrollbary = hasScrollbarY();
|
var hasscrollbarx = hasScrollbarX();
|
var scrollbarWidth = getScrollbarWidth();
|
let scrollXH = hasscrollbarx ? scrollbarWidth : 0;
|
let scrollYW = hasscrollbary ? scrollbarWidth : 0;
|
return { ratio, scrollX, scrollY, scrollXH, scrollYW };
|
}
|
}
|
|
function promisify(func: Function) {
|
return new Promise((resolve, reject) => {
|
func('1', (data) => resolve(data))
|
})
|
}
|
|
/**
|
* @description 判断对象是否含有某属性
|
* @params {Object} obj 对象
|
* @params {String} key 属性key
|
*/
|
function hasKey<O>(obj: O, key: keyof any): key is keyof O {
|
return key in obj
|
}
|
|
/**
|
* @description 与客户端通讯功能注册
|
* @params {Array} plugins 功能列表
|
*/
|
function usePlugin(plugins: any[]) {
|
plugins.forEach((plugin) => {
|
Object.getOwnPropertyNames(plugin).forEach(prop => {
|
Ws.prototype[prop] = plugin[prop];
|
});
|
});
|
}
|
|
function detectZoom() {
|
var ratio = 0,
|
screen = window.screen,
|
ua = navigator.userAgent.toLowerCase();
|
|
if (window.devicePixelRatio !== undefined) {
|
ratio = window.devicePixelRatio;
|
} else if (~ua.indexOf('msie')) {
|
if (screen['deviceXDPI'] && screen['logicalXDPI']) {
|
ratio = screen['deviceXDPI'] / screen['logicalXDPI'];
|
}
|
} else if (window.outerWidth !== undefined && window.innerWidth !== undefined) {
|
ratio = window.outerWidth / window.innerWidth;
|
}
|
|
if (ratio) {
|
ratio = Math.round(ratio * 100);
|
}
|
return ratio;
|
}
|
|
function hasScrollbarY() {
|
return (
|
document.body.scrollHeight >
|
(window.innerHeight || document.documentElement.clientHeight)
|
);
|
}
|
|
function hasScrollbarX() {
|
return (
|
document.body.scrollWidth >
|
(window.innerWidth || document.documentElement.clientWidth)
|
);
|
}
|
|
function getScrollbarWidth() {
|
var scrollDiv = document.createElement('div');
|
scrollDiv.style.cssText =
|
'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;';
|
document.body.appendChild(scrollDiv);
|
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
document.body.removeChild(scrollDiv);
|
|
return scrollbarWidth;
|
}
|