Vue+websocket+stompjs 实时监控坐席状态demo
由于是前后端分离的demo, 程序的后端我不管,我只负责把前端做好,这只是个demo, 还有很多不完善的地方。
2018-01-09新增:
后端的MQ事件结构现在也改了,该demo只能看看了。
html
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <link href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <table class="table" id="event-queue"> <thead> <tr> <th>当前状态</th> <th>状态改变时间</th> <th>姓名</th> <th>工号</th> <th>分机号</th> <th>对方号码</th> <th>呼入数</th> <th>呼出数</th> </tr> </thead> <tbody> <tr v-for="item in eventQueue"> <td>{{item.agentStatus | transAgentStatus}}</td> <td>{{item.agentStatusTime}}</td> <td>{{item.userName}}</td> <td>{{item.loginName}}</td> <td>{{item.deviceId}}</td> <td></td> <td></td> <td></td> </tr> </tbody> </table> <script src="http://cdn.bootcss.com/vue/1.0.26/vue.js"></script> <script src="js/websocket-suport.min.js"></script> <script src="js/main.js"></script> </body> </html>
js
var tm = (function(){ var App = function(){}; var app = App.prototype; var config = { dest: 'http://xxx.xxx.xxx.xxx:58080/mvc/stomp', topic: '/topic/csta/namespace/testwdd2.com' // topic: '/topic/csta/device/[email protected]' }; var eventQueue = []; var vm = new Vue({ el:'#event-queue', data:{ eventQueue: eventQueue } }); Vue.filter('transAgentStatus', function(status){ switch(status){ case 'NotReady': return '未就绪'; case 'WorkNotReady': return '话后处理状态'; case 'Idle': return '就绪'; case 'OnCallIn': return '呼入通话'; case 'OnCallOut': return '呼出通话'; case 'Logout': return '登出'; case 'Ringing': return '振铃'; case 'OffHook': return '摘机'; case 'CallInternal': return '内部通话'; case 'Dailing': return '外线已经振铃'; case 'Ringback': return '回铃'; case 'Conference': return '会议'; case 'OnHold': return '保持'; case 'Other': return '其他'; } return ''; }); /** * [render description] * @Author Wdd * @DateTime 2016-12-26T16:06:16+0800 * @param {[string]} tpl [模板字符串] * @param {[object]} data [data对象] * @return {[string]} [渲染后的字符串] */ app.render = function(tpl,data){ var re = /{{([^}]+)?}}/g; while(match = re.exec(tpl)){ tpl = tpl.replace(match[0],data[match[1]] || ''); } return tpl; }; app.initWebSocket = function(dest, topic){ dest = dest || config.dest; topic = topic || config.topic; var socket = new SockJS(dest); var ws = Stomp.over(socket); ws.connect({}, function(frame) { ws.subscribe(topic, function(event) { // var eventInfo = JSON.parse(event.body); app.handerEvent(JSON.parse(event.body)); }); }, function(frame) { console.log(frame); console.error(new Date() + 'websocket失去连接'); }); }; /** * [findAgentIndex description] * @Author Wdd * @DateTime 2016-12-28T10:34:13+0800 * @param {[string]} agentId [description] * @return {[int]} [description] */ app.findAgentIndex = function(agentId){ for(var i = eventQueue.length - 1; i >= 0; i--){ if(eventQueue[i].agentId === agentId){ return i; } } return -1; }; /** * [handerEvent 处理websocket事件] * @Author Wdd * @DateTime 2016-12-28T10:33:03+0800 * @param {[object]} data [description] * @return {[type]} [description] */ app.handerEvent = function(data){ if(data.eventType === 'CallEvent'){ return; } if(!data.eventSrc){ return; } var eventItem = { agentStatus: '', eventName: data.eventName, agentId: '', loginName: '', userName: '', deviceId: data.deviceId, agentStatusTime: '' }; var agent = data.eventSrc.agent || ''; if(agent){ eventItem.agentId = agent.agentId; eventItem.loginName = agent.loginName; eventItem.userName = agent.userName; eventItem.agentStatus = agent.agentStatus; eventItem.agentStatusTime = agent.agentStatusTime; } // 针对登出事件的agentId在外层 else if(data.agentMode){ eventItem.agentStatus = data.agentMode; eventItem.agentId = data.agentId; } else if(data.agentStatus){ eventItem.agentStatus = data.agentStatus; } if(!eventItem.agentId){ return; } var itemIndex = app.findAgentIndex(eventItem.agentId); // 新的座席加入 if(itemIndex === -1){ eventQueue.push(eventItem); } // 更新已有座席的状态 else{ eventQueue[itemIndex].agentStatus = eventItem.agentStatus; eventQueue[itemIndex].agentStatusTime = eventItem.agentStatusTime; eventQueue[itemIndex].eventName = eventItem.eventName; } }; return new App(); })();
打开控制台,输入tm.initWebsocket()后,websocket连接正常。
之后坐席状态改变,可以看到有事件推送过来。
看下整个页面:
最后,这个小小的监控如果用jQuery写,也可以,不过就是太坑了,每次都要去找到Dom元素,再更新DOM,用了Vue这类的框架,页面的dom操作完全不用关心了,真是太舒服了。\(^o^)/
关于stomp的重连
程序后服务端使用RabbitMQ
这里我直接引用我的另一个项目的部分代码,这个没有使用SockJS, 直接使用浏览器原生的WebSocket。
重连的原理很简单,就是检测到断开时,去调用我的reconnectWs方法,这里我也做了重连的次数限制。
initWebSocket: function(callback, errorCallback) { callback = callback || function(){}; if(ws && ws.connected){ return; } Config.isManCloseWs = false; var url = Config.wsProtocol + Config.SDK + Config.eventPort + Config.eventBasePath + "/websocket"; if(typeof WebSocket != 'function'){ alert('您的浏览器版本太太太老了,请升级你的浏览器到IE11,或使用任何支持原生WebSocket的浏览器'); return; } try{ var socket = new WebSocket(url); } catch(e){ console.log(e); return; } var wsHeartbeatId = ''; ws = Stomp.over(socket); if(!Config.useWsLog){ ws.debug = null; } ws.connect({}, function(frame) { Config.currentReconnectTimes = 0; var dest = Config.newWsTopic + env.loginId.replace(/\./g,'_'); var lastEventSerial = ''; ws.subscribe(dest, function(event) { var eventInfo = {}; try{ eventInfo = JSON.parse(event.body); delete eventInfo.params; delete eventInfo._type; delete eventInfo.topics; } catch(e){ console.log(e); return; } if(lastEventSerial === eventInfo.serial){ util.error('Error: event repeat sent !'); return; } else{ lastEventSerial = eventInfo.serial; } if(Config.useEventLog){ util.debugout.log(' ' + JSON.stringify(eventInfo)); } eventHandler.deliverEvent(eventInfo); }); callback(); }, function(frame) { // websocket upexpected disconnected // maybe network disconnection, or browser in offline // this condition will emit wsDisconnected event if(Config.isManCloseWs){return;} errorCallback(); util.log(frame); util.error(new Date() + 'websocket disconnect'); // clearInterval(wsHeartbeatId); if(Config.currentReconnectTimes < Config.maxReconnectTimes){ Config.currentReconnectTimes++; util.reconnectWs(); } else{ var errorMsg = { eventName: 'wsDisconnected', msg: 'websocket disconnect' }; wellClient.ui.main({ eventName:'wsDisconnected' }); util.debugout.log('>>> websocket disconnect'); wellClient.triggerInnerOn(errorMsg); } }); }, reconnectWs: function(){ setTimeout(function(){ util.log('>>> try to reconnect'); util.debugout.log('>>> try to reconnect'); util.initWebSocket(function(){},function(){}); }, Config.timeout * 1000); },
参考
相关推荐
sucheng 2020-04-29
choupiaoyi 2020-01-23
redis 2016-11-17
codable 2018-12-21
luojinbai 2017-03-28
feinifi 2018-06-26
芋道源码 2015-11-25
hushhw 2015-11-23
mjperhuo 2011-04-13
zhangmingming 2013-07-11
qiuzhuoxian 2012-09-01