电器网站模板,营销方法有哪几种,山东网站建设和游戏开发的公司排名,android开发软件下载更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 为了后面流程发起等消息推送#xff0c;所以需要集成websocket。 1、后端增加websoket支持 首先在framework模块里的pom.xml增加websocket
dependency… 更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码 https://gitee.com/nbacheng/ruoyi-nbcio 为了后面流程发起等消息推送所以需要集成websocket。 1、后端增加websoket支持 首先在framework模块里的pom.xml增加websocket
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-websocket/artifactId/dependency 2、增加websocket配置
package com.ruoyi.framework.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** 开启WebSocket支持*/
Configuration
public class WebSocketConfig {Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
3、增加websocket服务当然这部分后面还要修改
package com.ruoyi.framework.websocket;import cn.hutool.json.JSONUtil;
import com.ruoyi.common.core.domain.BaseProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;// ServerEndpoint 声明并创建了webSocket端点, 并且指明了请求路径
// id 为客户端请求时携带的参数, 用于服务端区分客户端使用/*** ServerEndpoint 声明并创建了websocket端点, 并且指明了请求路径* uid 为客户端请求时携带的用户id, 用于区分发给哪个用户的消息* author nbacheng* date 2023-09-20
*/ServerEndpoint(/websocket/{uid})
Component
public class WebSocketServer {// 日志对象private static final Logger log LoggerFactory.getLogger(WebSocketServer.class);// 静态变量用来记录当前在线连接数。应该把它设计成线程安全的。//千万不要用private static AtomicInteger onlineCount new AtomicInteger(0);// concurrent包的线程安全Set用来存放每个客户端对应的MyWebSocket对象。private static CopyOnWriteArraySetWebSocketServer webSocketSet new CopyOnWriteArraySet();// private static ConcurrentHashMapString,WebSocketServer websocketList new ConcurrentHashMap();// 与某个客户端的连接会话需要通过它来给客户端发送数据private Session session;// 接收uidprivate String uid ;/** 客户端创建连接时触发* */OnOpenpublic void onOpen(Session session, PathParam(uid) String uid) {this.session session;webSocketSet.add(this); // 加入set中addOnlineCount(); // 在线数加1log.info(有新窗口开始监听: uid , 当前在线人数为 getOnlineCount());this.uid uid;try {sendMessage(连接成功);} catch (IOException e) {log.error(websocket IO异常);}}/*** 客户端连接关闭时触发**/OnClosepublic void onClose() {webSocketSet.remove(this); // 从set中删除subOnlineCount(); // 在线数减1log.info(有一连接关闭当前在线人数为 getOnlineCount());}/*** 接收到客户端消息时触发*/OnMessagepublic void onMessage(String message, Session session) {log.info(收到来自窗口 uid 的信息: message);// 群发消息for (WebSocketServer item : webSocketSet) {try {item.sendMessage(message);} catch (IOException e) {e.printStackTrace();}}}/*** 连接发生异常时候触发*/OnErrorpublic void onError(Session session, Throwable error) {log.error(发生错误);error.printStackTrace();}/*** 实现服务器主动推送(向浏览器发消息)*/public void sendMessage(String message) throws IOException {log.info(服务器消息推送message);this.session.getAsyncRemote().sendText(message);}/*** 发送消息到所有客户端* 指定uid则向指定客户端发消息* 不指定uid则向所有客户端发送消息* */public static void sendInfo(String message, PathParam(uid) String uid) throws IOException {log.info(推送消息到窗口 uid 推送内容: message);for (WebSocketServer item : webSocketSet) {try {// 这里可以设定只推送给这个sid的为null则全部推送if (uid null) {item.sendMessage(message);} else if (item.uid.equals(uid)) {item.sendMessage(message);}} catch (IOException e) {continue;}}}/*** * 给多个指定uid客户端发消息* * */public static void sendInfo(String message, PathParam(uids) String[] uids ) throws IOException {log.info(推送消息到窗口 uids 推送内容: message);for (String uid : uids) {sendInfo(message,uid);}}/*** 发送消息到所有客户端* 指定uid则向指定客户端发消息* 不指定uid则向所有客户端发送消息* */public static void sendInfo(BaseProtocol message, PathParam(uid) String uid) throws IOException {log.info(推送消息到窗口 uid 推送内容: message);for (WebSocketServer item : webSocketSet) {try {// 这里可以设定只推送给这个sid的为null则全部推送if (uid null) {item.sendMessage(JSONUtil.toJsonStr(message));} else if (item.uid.equals(uid)) {item.sendMessage(JSONUtil.toJsonStr(message));}} catch (IOException e) {continue;}}}public static synchronized int getOnlineCount() {return onlineCount.get();}public static synchronized void addOnlineCount() {WebSocketServer.onlineCount.incrementAndGet();}public static synchronized void subOnlineCount() {WebSocketServer.onlineCount.decrementAndGet();}public static CopyOnWriteArraySetWebSocketServer getWebSocketSet() {return webSocketSet;}}
4、在导航条里增加一个消息
el-tooltip content消息 effectdark placementbottom!--message idmessage classright-menu-item hover-effect / --header-notice idmessage classright-menu-item-message hover-effect //el-tooltip
界面就是
同时为了样式问题增加下面样式
right-menu-item-message {display: inline-block;padding: 0 8px;height: 100%;font-size: 18px;color: #5a5e66;vertical-align: text-bottom;width: 36px;.hover-effect {cursor: pointer;transition: background .3s;:hover {background: rgba(0, 0, 0, .025)}}}
5、增加HeaderNotice 组件当然现在是测试只作为websocket消息测试用后续正式还需要修改。
templatediva-popover triggerclick placementbottomRight :autoAdjustOverflowtrue :arrowPointAtCentertrueoverlayClassNameheader-notice-wrapper visibleChangehandleHoverChange:overlayStyle{ width: 400px, top: 50px }template slotcontenta-spin :spinningloaddinga-tabsa-tab-pane :tabmsg1Title key1a-lista-list-item :keyindex v-for(record, index) in announcement1div stylemargin-left: 5%;width: 50%pa clickshowAnnouncement(record){{ record.titile }}/a/pp stylecolor: rgba(0,0,0,.45);margin-bottom: 0px{{ record.createTime }} 发布/p/divdiv styletext-align: righta-tag clickshowAnnouncement(record) v-ifrecord.priority L colorblue一般消息/a-taga-tag clickshowAnnouncement(record) v-ifrecord.priority M colororange重要消息/a-taga-tag clickshowAnnouncement(record) v-ifrecord.priority H colorred紧急消息/a-tag/div/a-list-itemdiv stylemargin-top: 5px;text-align: centera-button clicktoMyAnnouncement() typedashed block查看更多/a-button/div/a-list/a-tab-panea-tab-pane :tabmsg2Title key2a-lista-list-item :keyindex v-for(record, index) in announcement2div stylemargin-left: 5%;width: 50%pa clickshowAnnouncement(record){{ record.titile }}/a/pp stylecolor: rgba(0,0,0,.45);margin-bottom: 0px{{ record.createTime }} 发布/p/divdiv styletext-align: righta-tag clickshowAnnouncement(record) v-ifrecord.priority L colorblue一般消息/a-taga-tag clickshowAnnouncement(record) v-ifrecord.priority M colororange重要消息/a-taga-tag clickshowAnnouncement(record) v-ifrecord.priority H colorred紧急消息/a-tag/div/a-list-itemdiv stylemargin-top: 5px;text-align: centera-button clicktoMyAnnouncement() typedashed block查看更多/a-button/div/a-list/a-tab-panea-tab-pane :tabmsg3Title key3a-lista-list-item :keyindex v-for(record, index) in announcement3div stylemargin-left: 5%;width: 50%pa clickshowAnnouncement(record){{ record.titile }}/a/pp stylecolor: rgba(0,0,0,.45);margin-bottom: 0px{{ record.createTime }} 发布/p/divdiv styletext-align: righta-tag clickshowAnnouncement(record) v-ifrecord.priority L colorblue一般消息/a-taga-tag clickshowAnnouncement(record) v-ifrecord.priority M colororange重要消息/a-taga-tag clickshowAnnouncement(record) v-ifrecord.priority H colorred紧急消息/a-tag/div/a-list-itemdiv stylemargin-top: 5px;text-align: centera-button clicktoMyAnnouncement() typedashed block查看更多/a-button/div/a-list/a-tab-pane/a-tabs/a-spin/templatespan clickfetchNotice classheader-noticea-badge :countmsgTotala-icon stylefont-size: 16px; padding: 4px typebell //a-badge/spanshow-announcement refShowAnnouncement okmodalFormOk/show-announcementdynamic-notice refshowDynamNotice :pathopenPath :formDataformData //a-popover/div
/templatescriptimport ShowAnnouncement from ./ShowAnnouncementimport store from /store/import DynamicNotice from ./DynamicNoticeexport default {name: HeaderNotice,components: {DynamicNotice,ShowAnnouncement,},data() {return {loadding: false,url: {listCementByUser: /sys/annountCement/listByUser,editCementSend: /sys/sysAnnouncementSend/editByAnntIdAndUserId,queryById: /sys/annountCement/queryById,},hovered: false,announcement1: [],announcement2: [],announcement3: [],msg1Count: 0,msg2Count: 0,msg3Count: 0,msg1Title: 通知(0),msg2Title: ,msg3Title: ,stopTimer: false,websock: null,lockReconnect: false,heartCheck: null,formData: {},openPath: }},computed: {msgTotal() {return parseInt(this.msg1Count) parseInt(this.msg2Count) parseInt(this.msg3Count);}},mounted() {//this.loadData();//this.timerFun();this.initWebSocket();// this.heartCheckFun();},destroyed: function() { // 离开页面生命周期函数this.websocketOnclose();},methods: {timerFun() {this.stopTimer false;let myTimer setInterval(() {// 停止定时器if (this.stopTimer true) {clearInterval(myTimer);return;}this.loadData()}, 6000)},loadData() {try {// 获取系统消息getAction(this.url.listCementByUser).then((res) {if (res.success) {this.announcement1 res.result.anntMsgList;this.msg1Count res.result.anntMsgTotal;this.msg1Title 通知( res.result.anntMsgTotal );this.announcement2 res.result.sysMsgList;this.msg2Count res.result.sysMsgTotal;this.msg2Title 系统消息( res.result.sysMsgTotal );this.announcement3 res.result.todealMsgList;this.msg3Count res.result.todealMsgTotal;this.msg3Title 待办消息( res.result.todealMsgTotal );}}).catch(error {console.log(系统消息通知异常, error); //这行打印permissionName is undefinedthis.stopTimer true;console.log(清理timer);});} catch (err) {this.stopTimer true;console.log(通知异常, err);}},fetchNotice() {if (this.loadding) {this.loadding falsereturn}this.loadding truesetTimeout(() {this.loadding false}, 200)},showAnnouncement(record) {putAction(this.url.editCementSend, {anntId: record.id}).then((res) {if (res.success) {this.loadData();}});this.hovered false;if (record.openType component) {this.openPath record.openPage;this.formData {id: record.busId};this.$refs.showDynamNotice.detail(record.openPage);} else {this.$refs.ShowAnnouncement.detail(record);}},toMyAnnouncement() {this.$router.push({path: /isps/userAnnouncement});},modalFormOk() {},handleHoverChange(visible) {this.hovered visible;},initWebSocket: function() {// WebSocket与普通的请求所用协议有所不同ws等同于httpwss等同于httpsvar uid store.getters.name;var url process.env.VUE_APP_WS_API /websocket/ uid;console.log(url,url);this.websock new WebSocket(url);this.websock.onopen this.websocketOnopen;this.websock.onerror this.websocketOnerror;this.websock.onmessage this.websocketOnmessage;this.websock.onclose this.websocketOnclose;},websocketOnopen: function() {console.log(WebSocket连接成功);//心跳检测重置//this.heartCheck.reset().start();},websocketOnerror: function(e) {console.log(WebSocket连接发生错误);this.reconnect();},websocketOnmessage: function(e) {console.log(-----接收消息-------, e);console.log(-----接收消息-------, e.data);var data eval(( e.data )); //解析对象if (data.cmd topic) {//系统通知//this.loadData();this.$notification.open({ //websocket消息通知弹出message: websocket消息通知,description: data.msgTxt,style: {width: 600px,marginLeft: ${335 - 600}px,},});} else if (data.cmd user) {//用户消息//this.loadData();this.$notification.open({message: websocket消息通知,description: data.msgTxt,style: {width: 600px,marginLeft: ${335 - 600}px,},});}//心跳检测重置//this.heartCheck.reset().start();},websocketOnclose: function(e) {console.log(connection closed ( e ));if (e) {console.log(connection closed ( e.code ));}this.reconnect();},websocketSend(text) { // 数据发送try {this.websock.send(text);} catch (err) {console.log(send failed ( err.code ));}},openNotification(data) {var text data.msgTxt;const key open${Date.now()};this.$notification.open({message: 消息提醒,placement: bottomRight,description: text,key,btn: (h) {return h(a-button, {props: {type: primary,size: small,},on: {click: () this.showDetail(key, data)}}, 查看详情)},});},reconnect() {var that this;if (that.lockReconnect) return;that.lockReconnect true;//没连接上会一直重连设置延迟避免请求过多setTimeout(function() {console.info(尝试重连...);that.initWebSocket();that.lockReconnect false;}, 5000);},heartCheckFun() {var that this;//心跳检测,每20s心跳一次that.heartCheck {timeout: 20000,timeoutObj: null,serverTimeoutObj: null,reset: function() {clearTimeout(this.timeoutObj);//clearTimeout(this.serverTimeoutObj);return this;},start: function() {var self this;this.timeoutObj setTimeout(function() {//这里发送一个心跳后端收到后返回一个心跳消息//onmessage拿到返回的心跳就说明连接正常that.websocketSend(HeartBeat);console.info(客户端发送心跳);//self.serverTimeoutObj setTimeout(function(){//如果超过一定时间还没重置说明后端主动断开了// that.websock.close();//如果onclose会执行reconnect我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次//}, self.timeout)}, this.timeout)}}},showDetail(key, data) {this.$notification.close(key);var id data.msgId;getAction(this.url.queryById, {id: id}).then((res) {if (res.success) {var record res.result;this.showAnnouncement(record);}})},}}
/scriptstyle langcss.header-notice-wrapper {top: 50px !important;}
/style
style langless scoped.header-notice {display: inline-block;transition: all 0.3s;span {vertical-align: initial;}}
/style
6、增加websocket测试页面以便测试地址根据自己需要进行填写
templatedivel-input v-modelurl typetext stylewidth: 100% / nbsp; nbsp;br /el-button clickjoin typeprimary连接/el-buttonel-button clickexit typedanger断开/el-buttonbr /el-input typetextarea v-modelmessage :rows9 /el-button typeinfo clicksend发送消息/el-buttonbr /br /el-input typetextarea v-modeltext_content :rows9 / 返回内容br /br //div
/templatescript
export default {data() {return {url: ws://127.0.0.1:9060/websocket/ry,message: ,text_content: ,ws: null,};},methods: {join() {const wsuri this.url;this.ws new WebSocket(wsuri);const self this;this.ws.onopen function (event) {self.text_content self.text_content 已经打开连接! \n;};this.ws.onmessage function (event) {self.text_content event.data \n;};this.ws.onclose function (event) {self.text_content self.text_content 已经关闭连接! \n;};},exit() {if (this.ws) {this.ws.close();this.ws null;}},send() {if (this.ws) {const messageData {msgTxt: this.message,cmd: user}let strdata JSON.stringify(messageData);console.log(strdata,JSON.stringify(messageData));this.ws.send(strdata);//this.ws.send(this.message);} else {alert(未连接到服务器);}},},
};
/script
7、实际效果图