小白第一次做大屏项目,第一次用到websocket绘制地图点位(一脸懵),第一次百度挣扎试过好多方法失败的,更有些摸不着头脑(更是一脸懵)。
因项目需要,websocket必须做到全局才能使项目两模块都能用。第一次想着偷懒就把大屏做到管理系统里,用v-show达到切换目的同时websocket也同时在运行(真是个小天才~~嘻嘻~~),但暴露出很多v-show对echarts图的弊端o(╥﹏╥)o。后来不敢偷懒了,还是老老实实用vuex吧o(╥﹏╥)o
因为要做到全局,所以得用app.vue来实现目的。
App.vue是项目的主组件,页面入口文件 ,所有页面都在App.vue下进行切换,app.vue负责构建定义及页面组件归集。
同样光靠app.vue可不行,还得配合vuex来完美实现。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式, 采用集中式存储管理应用的所有组件的状态,解决多组件数据通信。
使用Vuex的好处:
1、数据的存取一步到位,不需要层层传递
2、数据的流动非常清晰
3、存储在Vuex中的数据都是响应式的
我用的框架是iview的admin plus,store方法都封装好了,用的比较轻松一些。
以下代码仅供参考,具体还是以自己项目为准
/** boolean常量 **/
let webSocket = {
SYSTEM_HEADERS: 'system',
BUSINESS_HEADERS: 'business',
LOGIN_TYPE: 'login',
HEARTBEAT_TYPE: 'heartbeat',
CHAT_TYPE: 'chat',
GAS_INDUSTRIAL_OR_RESIDENT: 'gasIndustrialOrResident',
DEVICE_REAL_TIME_STATUS: 'deviceRealStatus',
WEB_UPDATE_TIME: 'webUpdateTime',
WS_READ_BELL: 'readBell',
POLICE_TYPE: 'alarm',
DEVICES: 'devices',
ALLMAP_TYPE: 'batchMapPoints',
ONLYMAP_TYPE: 'map',
TIPS_TYPE: 'bell',
BELL_BATCH_TYPE: 'bellList',
BELL_WORK_TYPE: 'bellWork',
TIME_TYPE: 'alarmScrolling',
GET_ALL: 'getAll',
TCP_MSG: 'tcpMsg'
}
/** 后台接口地址 **/
let urls = {
/** ---------------------------------获取webscoket ip-----------------------------**/
getWebscoketIP: BASEURL + supervisionApi + 'getWebSocketInfo',
}
export {
urls, webSocket
}
import store from '../../store'
import { webSocket } from '@/js/constant';
import tools from '../tools'
import { Message } from 'view-design'
import { urls } from '../constant'
let ws;
/** webSocket报文格式,必须使用
* 具体参照webSocket常量类,或者与后端联系1111111
* **/
function message (obj) {
/** 报文头,必填 **/
this.headers = obj.headers
/** 报文类型,必填 **/
this.type = obj.type
/** 消息发送者,必填 **/
this.source = store.state.admin.user.info.Authorization
/** 消息体 **/
if (obj.message != null && undefined != obj.message) {
this.message = obj.message
}
/** 内容接收者 **/
if (obj.accept != null && undefined != obj.accept) {
this.accept = obj.accept
}
}
let WebsocketMessageService = {
/** 获取ws实例 **/
getWebSocket: function () {
return this.ws
},
setWebSocket: function (ws) {
this.ws = ws
},
closeWebSocket: function () {
this.ws.close();
},
/** 通用消息发送方法 **/
sendMessage: function (obj) {
if (this.ws.readyState === 1) {
this.ws.send(JSON.stringify(obj))
}
},
/** 封装登录请求参数 **/
setLoginMessage: function () {
let params = new message({ headers: webSocket.SYSTEM_HEADERS, type: webSocket.LOGIN_TYPE })
return params
},
/** 封装普通请求参数
* 根据业务可自行调整具体的参数
* **/
setChatMessage: function (obj) {
let params = new message({ headers: webSocket.BUSINESS_HEADERS, type: webSocket.CHAT_TYPE, message: obj.message })
return params
},
/***
* 心跳包
* */
setHeartbeat: function () {
let params = new message({ headers: webSocket.BUSINESS_HEADERS, type: webSocket.HEARTBEAT_TYPE })
return params
},
/**
* 重新进图地图发送
*
* @param obj
* @returns {message}
*/
setWsAll: function (obj) {
let params = new message({ headers: webSocket.BUSINESS_HEADERS, type: webSocket.GET_ALL, message: obj.message })
return params
},
// 获取ip地址
getWebSocketIP () {
return new Promise((resolve, reject) => {
tools.get(urls.getWebscoketIP).then(res => {
if (res.success) {
resolve(res.obj)
} else {
Message.error(res)
}
}).catch(error => {
tools.doError(error)
})
})
}
}
export {
WebsocketMessageService
}
然后再app.vue中引入
<script>
import { WebsocketMessageService } from '@/js/system/websocketMessage';
import { webSocket, urls } from '@/js/constant';
//iview的admin plus的vuex
import { mapGetters, mapMutations, mapState } from 'vuex';
export default {
name: 'app',
data () {
return {
// websocket相关
socketObj: '', // websocket实例对象
// 心跳检测
timeout: 10000, // 超时时间
timeoutObj: null, // 计时器对象——向后端发送心跳检测
serverTimeoutObj: null, // 计时器对象——等待后端心跳检测的回复
// 心跳检测重置
socketReconnectTimer: null, // 计时器对象——重连
socketReconnectLock: false, // WebSocket重连的锁
socketLeaveFlag: false, // 离开标记(解决 退出登录再登录 时出现的 多次相同推送 问题,出现的本质是多次建立了WebSocket连接)
};
},
created () {
},
methods: {
//iview的admin plus已封装好的vuex方法
...mapMutations('admin/websocket', [
'setAllMap',
]),
handleWindowResize () {
this.handleMatchMedia();
},
handleMatchMedia () {
const matchMedia = window.matchMedia;
if (matchMedia('(max-width: 600px)').matches) {
this.setDevice('Mobile');
} else if (matchMedia('(max-width: 992px)').matches) {
this.setDevice('Tablet');
} else {
this.setDevice('Desktop');
}
},
// websocket处理后台推送消息
/** 发送消息 **/
sendMessage (params) {
WebsocketMessageService.sendMessage(params);
},
/** 连接websocket服务器 **/
initWebSocket (ip) {
console.log('webSocket 启用中。。。。 ');
let _this = this;
// 判断页面有没有存在websocket连接
if (window.WebSocket) {
let ws = new WebSocket('ws://' + ip + '/ws');
/** 服务器连接成功 **/
ws.onopen = function (e) {
console.log('webSocket连接成功');
/** 发送频道注册消息 **/
_this.sendMessage(WebsocketMessageService.setLoginMessage());
};
/** 服务器连接关闭 **/
ws.onclose = function (e) {
console.log('webSocket连接关闭');
_this.heartbeatReset();
};
/** 服务器连接出错 **/
ws.onerror = function () {
console.log('webSocket连接出错,重新链接');
this.initWebSocket(ip);
};
/** 解析信息 **/
ws.onmessage = function (e) {
// 接收服务器返回的数据
let resData = JSON.parse(e.data);
switch (resData.headers) {
/** 系统级消息 **/
case webSocket.SYSTEM_HEADERS:
switch (resData.type) {
// 通道注册成功
case webSocket.LOGIN_TYPE:
/***
* 建立心跳机制
* */
_this.heartbeatReset();
_this.heartbeatStart();
break;
case webSocket.HEARTBEAT_TYPE:
/***
* 收到心跳回复
* */
_this.heartbeatReset();
_this.heartbeatStart();
break;
// 全点位
case webSocket.ALLMAP_TYPE:
_this.setAllMap(JSON.parse(resData.message))
console.log('地图点推送');
break;
default:
}
};
} else {
}
WebsocketMessageService.setWebSocket(_this.ws);
},
// websocket重连
socketReconnect () {
if (this.socketReconnectLock) {
return;
}
this.socketReconnectLock = true;
this.socketReconnectTimer && clearTimeout(this.socketReconnectTimer);
this.socketReconnectTimer = setTimeout(() => {
console.log('WebSocket:重连中...');
this.socketReconnectLock = false;
// websocket启动
WebsocketMessageService.getWebSocketIP().then((e) => {
/** 初始化websocket **/
this.initWebSocket(e);
}).catch((error) => {
this.heartbeatReset();
/** 接口异常,取消重新链接****/
this.$Message.error(error.errorMsg)
});
}, 4000);
},
// 获取ip地址
getWebsocketIp () {
console.log('Authorization,', this.info.Authorization)
WebsocketMessageService.getWebSocketIP().then((e) => {
/** 初始化websocket **/
this.initWebSocket(e);
});
},
heartbeatReset () {
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
},
heartbeatStart () {
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(() => {
// 这里向后端发送一个心跳检测,后端收到后,会返回一个心跳回复
WebsocketMessageService.sendMessage(WebsocketMessageService.setHeartbeat());
console.log('发送心跳检测');
this.serverTimeoutObj = setTimeout(() => {
// 如果超过一定时间还没重置计时器,开始重新链接,
console.log('未收到心跳检测回复');
this.socketReconnect()
}, this.timeout);
}, this.timeout);
},
closeWebSocket () {
this.ws.close();
},
},
mounted () {
this.getWebsocketIp();
},
}
</script>
然后再vuex中添加
export default {
namespaced: true,
state: {
dataSpot: [],
},
mutations: {
setAllMap (state, type) {
state.dataSpot = type;
},
},
};
然后再地图文件中调用
import { mapState } from 'vuex';
computed: {
...mapState('admin/websocket', [
'dataSpot',
])
},
watch: {
dataSpot: {
handler: function (newer, older) {
//绘制点位
this.handlePoint()
},
deep: true
},
}
小白第一次编写,如果问题,请见谅。如果能帮到你,十分荣幸!