赞
踩
IndexController.java
import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import com.johnny.websocket.WebSocket; @Controller public class IndexController { @Autowired WebSocket webSocket; @GetMapping("/ws") public String webSocket(HttpServletRequest request, HttpServletResponse response) { request.setAttribute("rootPath", request.getContextPath()); return "webSocket"; } @GetMapping("/index1") public String index1() { return "index1"; } @GetMapping("/index2") public String index2() { return "index2"; } @ResponseBody @GetMapping("/getUserMenu") public String index3() { return null; } @ResponseBody @GetMapping("/sendToOne") public void sendToOne(@RequestParam("from") String from, @RequestParam("to") String to, @RequestParam("text") String text, @RequestParam("type") String type) throws IOException { JSONObject json = new JSONObject(); json.put("from", from); json.put("to", to); json.put("text", text); json.put("type", type); webSocket.sendMessageTo(to, json.toString()); } @ResponseBody @GetMapping("/sendToAll") public void sendToAll(@RequestParam("from") String from, @RequestParam("to") String to, @RequestParam("text") String text, @RequestParam("type") String type) throws IOException { JSONObject json = new JSONObject(); json.put("from", from); json.put("to", to); json.put("text", text); json.put("type", type); webSocket.sendMessageAll(json.toString()); } }
WebSocketConfig.java
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration public class WebSocketConfig { // 这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket ,如果你使用外置的tomcat就不需要该配置文件 @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
WebSocket.java
import java.io.IOException; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; /** * WebSocketServer接收请求https://blog.csdn.net/qq_42402854/article/details/130948270 * * @Contacts 171471869@163.com * @Author Johnny_阿阳 * @Date 2023-04-10 10:42:19 */ @Component @ServerEndpoint("/webSocket/{userName}") public class WebSocket { public static final Logger log = LogManager.getLogger(WebSocket.class); // 心跳机制 private Timer heartBeatTimer; // 是否已关闭 private boolean isClosed; // 用户名称 private String userName; // 会话 private Session session; // 在线人数 private static AtomicInteger onlineSessionClientCount = new AtomicInteger(0); // 存放所有在线的客户端 private static Map<String, Session> onlineSessionClientMap = new ConcurrentHashMap<>(); // 以用户的姓名为key,WebSocket为对象保存起来 private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>(); public WebSocket() { this.isClosed = false; } /** * 连接建立成功调用的方法。由前端<code>new WebSocket</code>触发 * * @param userName 每次页面建立连接时传入到服务端的id,比如用户id等。可以自定义。 * @param session 与某个客户端的连接会话,需要通过它来给客户端发送消息 */ @OnOpen public void onOpen(@PathParam("userName") String userName, Session session) { this.userName = userName; this.session = session; this.session.setMaxIdleTimeout(5 * 60 * 1000); this.startHeartBeat(); String sessionId = session.getId(); clients.put(userName, this);// 把自己的信息加入到map当中去 onlineSessionClientMap.put(userName, session);// 将页面的userName和session绑定或者session.getId()与session onlineSessionClientCount.incrementAndGet();// 在线数加1 // 给所有人发送上线通知type=0,from=userName Message message1 = new Message(userName, "", "给所有人发送上线通知", "0"); String msg1 = JSON.toJSONString(message1, true); sendTextToAll(msg1); // 给自己发送在线人员type=0,from==to,online=[...] Set<String> online = onlineSessionClientMap.keySet(); Message message2 = new Message(userName, userName, "给自己发送在线人员", "0", null, online); String msg2 = JSON.toJSONString(message2, true); sendTextToOne(userName, msg2); // 给自己发送未读消息.. // Message message3 = new Message(null, userName, msg, "1", "-1", null); // String msg3 = JSON.toJSONString(message3, true); // sendTextToOne(userName, msg3); log.debug("连接建立成功,当前在线数为:{} ==> 开始监听新连接:sessionId = {}, userName = {}", onlineSessionClientCount, sessionId, userName); } /** * 连接关闭调用的方法。由前端<code>socket.close()</code>触发 * * @param userName * @param session */ @OnClose public void onClose(@PathParam("userName") String userName, Session session) { this.isClosed = true; this.stoptHeartBeat(); clients.remove(userName); onlineSessionClientMap.remove(userName);// 从 Map中移除 onlineSessionClientCount.decrementAndGet();// 在线数减1 // 给所有人发送下线通知type=-2,from=userName Message message = new Message(userName, null, "给所有人发送下线通知", "-2"); String msg = JSON.toJSONString(message, true); sendTextToAll(msg); log.debug("连接关闭成功,当前在线数为:{} ==> 关闭该连接信息:sessionId = {}, userName = {}", onlineSessionClientCount, session.getId(), userName); } /** * 发生错误调用的方法 * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { // 给自己发送失败消息.. this.stoptHeartBeat(); String sessionId = session.getId(); Message message = new Message(null, null, "给自己发送失败消息,sessionId=" + sessionId, "-1", null, null); String err = JSON.toJSONString(message, true); sendTextToOne(userName, err);// 通知用户 error.printStackTrace(); } /** * 收到客户端消息后调用的方法。由前端<code>socket.send</code>触发 * 当服务端执行toSession.getAsyncRemote().sendText(xxx)后,前端的socket.onmessage得到监听。 * * @param message * @param session */ @OnMessage public void onMessage(String message, Session session) { // html界面传递来得数据格式,可以自定义message{"from":"user1","to":"user2","text":"hello","type":"0","online":"[user1,user2...]"} JSONObject jsonObject = JSON.parseObject(message); String text = jsonObject.getString("text"); String from = jsonObject.getString("from"); String to = jsonObject.getString("to"); log.debug("服务端收到客户端消息 ==> from = {}, to = {}, message = {}", from, to, message); if (to == null || to == "" || "".equalsIgnoreCase(to)) { sendTextToAll(text); } else { sendTextToOne(to, text); } } /** * 群发消息<后端> * * @param message 消息 */ public void sendTextToAll(String message) { onlineSessionClientMap.forEach((onlineSid, toSession) -> {// 遍历在线map集合 if (!userName.equalsIgnoreCase(onlineSid)) { synchronized (toSession) { log.debug("用户向服务器发送广播 ==> message = {}", message); toSession.getAsyncRemote().sendText(message); } } }); } /** * 指定用户发送消息<后端> * * @param to * @param message */ public void sendTextToOne(String to, String message) { Session toSession = onlineSessionClientMap.get(to); synchronized (toSession) { log.debug("用户向{}发送消息 ==> message = {}", to, message); toSession.getAsyncRemote().sendText(message);// 异步发送 } // try { // toSession.getBasicRemote().sendText(message);// 同步发送 // } catch (IOException e) { // e.printStackTrace(); // } } /** * 指定用户发送消息 * * @param message * @param to * @throws IOException */ public void sendMessageTo(String to, String message) { for (WebSocket item : clients.values()) { if (item.userName.equals(to)) { log.debug("用户向{}发送消息 ==> message = {}", to, message); item.session.getAsyncRemote().sendText(message);// 异步发送 break; } } } /** * 群发消息 * * @param message * @throws IOException */ public void sendMessageAll(String message) { for (WebSocket item : clients.values()) { if (!item.userName.equalsIgnoreCase(userName)) { log.debug("用户向服务器发送广播 ==> message = {}", message); item.session.getAsyncRemote().sendText(message);// 异步发送 } } } /** * 启动心跳定时器 */ private void startHeartBeat() { this.heartBeatTimer = new Timer(); this.heartBeatTimer.schedule(new TimerTask() { @Override public void run() { if (!isClosed) { session.getAsyncRemote().sendText(""); // send(); } } }, 60000, 60000); } /** * 停止心跳定时器 */ private void stoptHeartBeat() { if (this.heartBeatTimer != null) { this.heartBeatTimer.cancel(); this.heartBeatTimer = null; } } }
Message.java
import java.util.Set; /** * WebSocketMsg发送的消息详情 * * @Contacts 171471869@163.com * @Author Johnny_阿阳 * @Date 2023-04-10 10:42:19 */ public class Message { public String from;// 发送者name public String to;// 接收者name public String text;// 发送的文本 public String type;// 消息类型(-2[下线],-1[失败],0[上线],1[正常消息],2[图片]) public String status;// 消息状态(失败[-1],未读[0],已读[1]]) public Set<String> online;// 在线用户 public Message() { super(); } /** * * @param from * @param to * @param text * @param type 消息类型(-2[下线],-1[失败],0[上线],1[正常消息]) */ public Message(String from, String to, String text, String type) { super(); this.from = from; this.to = to; this.text = text; this.type = type; } /** * * @param from * @param to * @param text * @param type 消息类型(-2[下线],-1[失败],0[上线],1[正常消息]) * @param status 消息状态([-1]失败,[0]未读,[1]已读]) * @param online 在线用户 */ public Message(String from, String to, String text, String type, String status, Set<String> online) { super(); this.from = from; this.to = to; this.text = text; this.type = type; this.status = status; this.online = online; } public String getFrom() { return from; } public String getTo() { return to; } public String getText() { return text; } public String getType() { return type; } public String getStatus() { return status; } public Set<String> getOnline() { return online; } public void setFrom(String from) { this.from = from; } public void setTo(String to) { this.to = to; } public void setText(String text) { this.text = text; } public void setType(String type) { this.type = type; } public void setStatus(String status) { this.status = status; } public void setOnline(Set<String> online) { this.online = online; } @Override public String toString() { return "Message [from=" + from + ", to=" + to + ", text=" + text + ", type=" + type + ", status=" + status + ", online=" + online + "]"; } }
webSocket.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8" /> <title>Insert title here</title> <link rel="stylesheet" th:href="@{/static/FrameworkModule/pear/css/pear.css}"> <link rel="stylesheet" th:href="@{/static/admin/css/load.css}"/> <link rel="stylesheet" th:href="@{/static/admin/css/admin.css}"/> <link rel="stylesheet" th:href="@{/static/users/css/index.css}"/> <link rel="stylesheet" th:href="@{/static/users/css/sphere.css}"> <link rel="stylesheet" th:href="@{/static/users/css/chat.css}"> <link rel="stylesheet" th:href="@{/static/FrameworkModule/emoji/dist/css/jquery.mCustomScrollbar.min.css}"> <link rel="stylesheet" th:href="@{/static/FrameworkModule/emoji/dist/css/jquery.emoji.css}"> </head> <body> <div id="id-layer-wrapper" > <div class="layout"> <!-- 左侧 --> <div class="layout-left"> <ul> <!-- 头像 --> <li > <div class="header-img"> <img src="./static/chat/dist/media/img/avatar1.jpg" class="my-header-img rounded-circle" alt="image"> </div> </li> <!-- 用户 --> <li class="left-menu active" title="用户"> <div class="layui-icon layui-icon-user"></div> </li> <!-- 声音 --> <li class="left-menu" title="声音"> <div class="layui-icon layui-icon-notice"></div> </li> <!-- 菜单 --> <li class="left-menu" title="菜单"> <div class="layui-icon layui-icon-app"></div> </li> <!-- 退出 --> <li class="left-menu" title="退出"> <div class="layui-icon layui-icon-logout"></div> </li> </ul> </div> <!-- 中间 --> <div class="layout-middle"> <!-- 在线列表 --> <div id="online" class="online-group"> <ul class="list-group" id="online_list" ></ul> </div> </div> <!-- 右侧 --> <div class="layout-right"> <!-- 右侧头部 --> <div class="chat-header"> <div id="chat-header" title="群聊" class="chat-header-user"> <div class="header-img"> <img src="./static/chat/dist/media/img/chat.png" class="rounded-circle" alt="image"> </div> <div> <h5>群聊</h5> </div> </div> </div> <!-- 右侧中间 --> <div class="chat-body" id="messages"> <div class="messages active" title="群聊" ></div> </div> <!-- 右侧底部 --> <div class="chat-footer"> <div> <textarea id="send-Chat-Msg" class="form-control" placeholder="发送消息..."></textarea> <div class="form-buttons"> <button title="表情" id="send-Chat-face" onclick="chatOperation(this)" class=" layui-icon layui-icon-face-smile-fine" ></button> <button title="语言" id="send-Chat-mike" onclick="chatOperation(this)" class=" layui-icon layui-icon-mike" ></button> <button title="发送" id="send-Chat-release" onclick="chatOperation(this)" class=" layui-icon layui-icon-release" ></button> <button title="相机" id="send-Chat-camera" onclick="chatOperation(this)" class=" layui-icon layui-icon-camera" ></button> <button title="文件" id="send-Chat-file" onclick="chatOperation(this)" class=" layui-icon layui-icon-file" ><input id="upload-file" type="file" name="file"/></button> <button title="定位" id="send-Chat-location" onclick="chatOperation(this)" class=" layui-icon layui-icon-location" ></button> </div> </div> </div> </div> </div> </div> <script th:src="@{/static/FrameworkModule/jquery.min.js}"></script> <script th:src="@{/static/FrameworkModule/layui2_8/layui.js}"></script> <script th:src="@{/static/FrameworkModule/pear/pear.js}"></script> <script th:src="@{/static/users/js/myutil.js}"></script> <script th:src="@{/static/FrameworkModule/emoji/dist/js/highlight.pack.js}"></script> <script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.mousewheel-3.0.6.min.js}"></script> <script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.mCustomScrollbar.min.js}"></script> <script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.emoji.min.js}"></script> <script type="text/javascript"> var ws = null; var music = null; var sendFrom = null; let rootPath="[[${rootPath}]]"; var singleMsg = './static/users/music/singleMsg.mp3'; var groupMsg = './static/users/music/groupMsg.mp3'; var online = './static/users/music/online.mp3'; layui.use(['jquery','layer','element','admin','form','popup','util'], function () { let $ = layui.jquery; let util = layui.util; let form = layui.form; let layer = layui.layer; let admin = layui.admin; let element = layui.element; let popup = layui.popup; var num1 = Math.floor(Math.random()*9999+1); var nickName = "user"+num1; if ("WebSocket" in window) {//localhost:8080/springWebSocket自己改 ws = new WebSocket("ws:localhost:8080/springWebSocket/webSocket/"+nickName); } else if ("MozWebSocket" in window) { ws = new MozWebSocket("ws:localhost:8080/springWebSocket/webSocket/"+nickName); } else { //ws = new SockJS(wsUrl + nickName); layer.msg('Not support websocket', {icon: 2, time: 2000}); } //新建连接 ws.onopen = function (event) { var num = Math.floor(Math.random()*9+1); var img = './static/chat/dist/media/img/avatar' + num + '.jpg'; $('.my-header-img').attr('src',img); var html = '<li title="群聊" οnclick="sendTo(this)" class="list-group-item" data-navigation-target="online-msg">'; html += '<div><figure class="avatar"><img src="./static/chat/dist/media/img/chat.png" class="rounded-circle" alt="image"></figure></div>'; html += '<div class="users-list-body"><div><h5 >群聊</h5><p title="群聊">空闲...</p></div></div></li>'; document.getElementById('online_list').innerHTML = html; }; //监听消息 ws.onmessage = function (event) { var data = JSON.parse(event.data); if (data.type == 1 || data.type == 2) { //图片消息 doReceiveMsg(data); //layer.msg('正常消息', {icon: 1, time: 1000}); } else if (data.type == 0 && data.from == data.to && data.to.length > 0) { //自己获取在线人员 onlineUser(data.online); //layer.msg('自己获取在线人员', {icon: 1, time: 10000}); } else if (data.type == 0 && data.from != data.to && data.to.length == 0) { //通知所有人xx上线了 addOnlineUser(data.from); //layer.msg('通知所有人xx上线了', {icon: 1, time: 10000}); } else if (data.type == -1) { //错误消息 layer.msg('错误消息', {icon: 1, time: 1000}); } else if (data.type == -2) { //下线消息 delOnlineUser(data.from); //layer.msg('下线消息', {icon: 1, time: 1000}); } }; //发送失败 ws.onerror = function () { layer.msg('发送失败', {icon: 2, time: 10000}); }; //断开连接 ws.onclose = function (e) { var data = '断开连接: [错误码:'+e.code+'; 断开原因:'+e.reason+'; 正常关闭:'+e.wasClean+']'; //console.log(data); layer.msg(data, {icon: 2, time: 1000}); }; //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function(){ close(); } //关闭浏览器 window.close = function () { ws.close(); }; //聊天框按钮 window.chatOperation = function (obj) { var title = $(obj).attr("title"); if ('发送'==title) { doSendText(); var mmmm = $("#messages .messages .message-item .message-content"); matchEmoji(mmmm); }else if ('文件'==title) { var myfile = $("#upload-file"); myfile[0].addEventListener('change',function (e) { var fileList = document.getElementById("upload-file").files; //console.log(fileList); if ('image/jpeg'!=fileList[0].type) { layer.msg('紧支持发送图片', {icon: 2, time: 1000}); }else { doSendImages(); } },{once:true}); }else if ('表情'==title) { loadEmoji(); } } <!-- 在线户名 --> window.onlineUser = function (data){ for (var i = 0; i < data.length; i++) { var num = Math.floor(Math.random()*9+1); var img = './static/chat/dist/media/img/avatar' + num + '.jpg'; var name = (data[i]==nickName) ? '记忆助手(我)' : data[i]; img = (data[i]==nickName) ? ($('.my-header-img').attr('src')) : img; var html = '<li title="' + name + '" οnclick="sendTo(this)" class="list-group-item" data-navigation-target="online-msg">'; html += '<div><figure class="avatar"><img src="'+img+'" class="rounded-circle" alt="image"></figure></div>'; html += '<div class="users-list-body"><div><h5 >'+name+'</h5><p title="' + data[i] + '">空闲...</p></div></div></li>'; $('#online_list').append(html); var html = '<div class="messages" title="' + name + '" ></div>'; $('#messages').append(html); } } <!-- 增加在线户名 --> window.addOnlineUser = function (name){ var num = Math.floor(Math.random()*9+1); var img = './static/chat/dist/media/img/avatar' + num + '.jpg'; var html = '<li title="' + name + '" οnclick="sendTo(this)" class="list-group-item" data-navigation-target="online-msg">'; html += '<div><figure class="avatar"><img src="'+img+'" class="rounded-circle" alt="image"></figure></div>'; html += '<div class="users-list-body"><div><h5 >'+name+'</h5><p title="' + name + '">空闲...</p></div></div></li>'; $('#online_list').append(html); var html = '<div class="messages" title="' + name + '" ></div>'; $('#messages').append(html); } <!-- 移除下线户名 --> window.delOnlineUser = function (name){ var li = $('li[title="' + name + '"]'); var div = $('.messages[title="' + name + '"]'); li.addClass('logout'); $('.logout .users-list-body div p').html('用户已离开'); setTimeout(() => { li.remove(); div.remove(); var src = $('li[title="群聊"] .avatar img').attr('src'); $('#chat-header').find('img').attr("src",src); $('#chat-header').attr("title","群聊"); $('#chat-header').find('h5').text("群聊"); $('#messages .messages[title="群聊"]').addClass('active'); }, 60*1000); } <!-- 选择发送人 --> window.sendTo = function (obj){ var title = $(obj).attr("title"); var src = $(obj).find('img').attr("src"); $('#chat-header').attr("title",title); $('#chat-header').find('img').attr("src",src); $('#chat-header').find('h5').text(title); $('#messages .messages').removeClass('active'); $('#messages .messages[title="'+title+'"]').addClass('active'); $(obj).removeClass('new-msg'); } <!-- 发送文字消息 --> window.doSendText = function () { var sentTime = new Date().format('yyyy-MM-dd hh:mm:ss'); var title = $('#chat-header').attr("title"); var msg = $('#send-Chat-Msg').val(); var img = $('.my-header-img').attr('src'); var html = '<div class="message-item outgoing-message"><div class="message-avatar">'; html += '<figure class="avatar"><img src="'+img+'" class="rounded-circle" alt="image"></figure>'; html += '<div><h5>'+ nickName +'</h5><div class="time">'+sentTime+'</div></div>'; html += '</div><div class="message-content">'+msg+'</div></div>'; if ("群聊" == title && msg.length > 0) {//群聊 $('.messages[title="'+title+'"] ').append(html); var data = {"from":nickName,"to":"群聊","text":msg,"type":"1"}; var url = rootPath+"/sendToAll"; ajaxGetData(url, data); } else if (msg.length > 0){ $('.messages[title="'+title+'"] ').append(html); var data = {"from":nickName,"to":title,"text":msg,"type":"1"}; var url = rootPath+"/sendToOne"; ajaxGetData(url, data); } $('#send-Chat-Msg').val(""); } <!-- 发送图片消息 --> window.doSendImages = function() { var fileList = document.getElementById("upload-file").files; var sentTime = new Date().format('yyyy-MM-dd hh:mm:ss'); var title = $('#chat-header').attr("title"); var img = $('.my-header-img').attr('src'); if(fileList.length > 0){ var fileReader = new FileReader(); fileReader.readAsDataURL(fileList[0]); fileReader.onload = function (e) { var msg = e.target.result; var sendImg = '<img class="chat-img" src="' + msg + '"'; var html = '<div class="message-item outgoing-message"><div class="message-avatar">'; html += '<figure class="avatar"><img src="'+img+'" class="rounded-circle" alt="image"></figure>'; html += '<div><h5>'+ nickName +'</h5><div class="time">'+sentTime+'</div></div>'; html += '</div><div class="message-content">'+sendImg+'</div></div>'; if ("群聊" == title) {//群聊 var data = {"from":nickName,"to":"群聊","text":msg,"type":"2"}; var url = rootPath+"/sendToAll"; ajaxGetData(url, data); $('.messages[title="'+title+'"] ').append(html); } else { var data = {"from":nickName,"to":title,"text":msg,"type":"2"}; var url = rootPath+"/sendToOne"; ajaxGetData(url, data); $('.messages[title="'+title+'"] ').append(html); } } } } <!-- 接收文字/图片消息 --> window.doReceiveMsg = function (data){ var from = data.from; var to = data.to; var text = data.text; var type = data.type; var title = $('#chat-header').attr("title"); var receiveTime = new Date().format('yyyy-MM-dd hh:mm:ss');; var img = $('li[title="' + from + '"]').find('img').attr('src'); var receiveImg = '<img class="chat-img" src="' + text + '"'; text = type==2 ? receiveImg : text;// 图片?/文字? var html = '<div class="message-item"><div class="message-avatar">'; html += '<figure class="avatar"><img src="'+img+'" class="rounded-circle" alt="image"></figure>'; html += '<div><h5>'+from+'</h5><div class="time">'+receiveTime+'</div></div>'; html += '</div><div class="message-content">'+text+'</div></div>'; music = new Audio(); if (nickName != from && to !="群聊") { music.src = singleMsg; $('p[title="' + from + '"]').html(text); $('li[title="' + from + '"]').addClass('new-msg'); $('.messages[title="' + from + '"] ').append(html); }else if (nickName != from && to =="群聊") { music.src = groupMsg; $('p[title="群聊"]').html(text); $('li[title="群聊"]').addClass('new-msg'); $('.messages[title="群聊"]').append(html); } var mmmm = $("#messages .messages .message-item .message-content"); matchEmoji(mmmm); var playPromise = music.play(); if (playPromise) { playPromise.then(() => { // 音频的播放需要耗时 setTimeout(() => { // 后续操作 }, music.duration * 1000); // music.duration 为音频的时长单位为秒 }).catch((e) => { // 音频加载失败 }); } } //菜单.unbind('click') $(document).on("click", '.left-menu ', function () { $(".left-menu").removeClass("active"); var active = $(this).addClass("active");; var title = $(this).attr("title"); }); }) </script> </body> </html>
js、css懒得删,全部搞进来了
<script th:src="@{/static/FrameworkModule/jquery.min.js}"></script>
<script th:src="@{/static/FrameworkModule/layui2_8/layui.js}"></script>
<script th:src="@{/static/FrameworkModule/pear/pear.js}"></script>
<script th:src="@{/static/users/js/myutil.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/highlight.pack.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.mousewheel-3.0.6.min.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.mCustomScrollbar.min.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.emoji.min.js}"></script>
<link rel="stylesheet" th:href="@{/static/FrameworkModule/pear/css/pear.css}">
<link rel="stylesheet" th:href="@{/static/admin/css/load.css}"/>
<link rel="stylesheet" th:href="@{/static/admin/css/admin.css}"/>
<link rel="stylesheet" th:href="@{/static/users/css/index.css}"/>
<link rel="stylesheet" th:href="@{/static/users/css/sphere.css}">
<link rel="stylesheet" th:href="@{/static/users/css/chat.css}">
<link rel="stylesheet" th:href="@{/static/FrameworkModule/emoji/dist/css/jquery.mCustomScrollbar.min.css}">
<link rel="stylesheet" th:href="@{/static/FrameworkModule/emoji/dist/css/jquery.emoji.css}">
效果展示:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。