rkljw 1 giorno fa
parent
commit
86fe29536a

+ 176 - 14
src/layout/components/Chat/ChatPanel.vue

@@ -420,7 +420,7 @@
                   :rows="4"
                   resize="none"
                   maxlength="200"
-                  @keyup.enter.native="sendUserMessageToFriend"
+                  @keyup.enter.native="sendUserMessage"
                 >
                 </el-input>
               </div>
@@ -619,7 +619,7 @@
           </div>
         </div>
     </el-dialog>
-    <el-dialog :visible.sync="groupWindowStatus" :modal="false" :close-on-click-modal="false" width="1028px">
+          <el-dialog :visible.sync="groupWindowStatus" :modal="false" :close-on-click-modal="false" width="1028px" :inert="!groupWindowStatus">
       <div class="searchWindow">
         <div class="searchWindowLeft">
           <div class="searchUserWindowBox">
@@ -706,7 +706,7 @@
       </div>
     </el-dialog>
     <!--分享群聊 start------------------------------------------------------------>
-      <el-dialog :visible.sync="shareGroupWindowStatus" :modal="false" title="分享群聊" :close-on-click-modal="false" width="420px">
+      <el-dialog :visible.sync="shareGroupWindowStatus" :modal="false" title="分享群聊" :close-on-click-modal="false" width="420px" :inert="!shareGroupWindowStatus">
         <div>
           <div class="shareCardWindowBox">
             <el-tabs v-model="shareGroupActive" type="card" @tab-click="handleClickShareGroupCard">
@@ -762,7 +762,7 @@
         </div>
       </el-dialog>
       <!--聊天记录弹出框 start------------------------------------------------------------>
-      <el-dialog :visible.sync="fileWindowStatus" title="聊天记录" :model="false" :close-on-click-modal="false" width="757px">
+      <el-dialog :visible.sync="fileWindowStatus" title="聊天记录" :model="false" :close-on-click-modal="false" width="757px" :inert="!fileWindowStatus">
         <div class="fileWindow">
           <div class="fileWindowHeader">
             <el-input
@@ -958,7 +958,7 @@
       <!--聊天记录弹出框 end------------------------------------------------------------>
             <!--编辑群成员弹出框 start------------------------------------------------------------>
       <!--编辑时 左侧显示的是好友列表 右侧显示的是群成员-->
-      <el-dialog :visible.sync="groupEditWindowStatus" :model="false" :close-on-click-modal="false" width="1028px" @closed="cancelEditGroupMember">
+      <el-dialog :visible.sync="groupEditWindowStatus" :model="false" :close-on-click-modal="false" width="1028px" @closed="cancelEditGroupMember" :inert="!groupEditWindowStatus">
         <div class="searchWindow">
           <div class="searchWindowLeft">
             <div class="searchUserWindowBox">
@@ -1053,7 +1053,7 @@
       </el-dialog>
       <!--编辑群成员弹出框 end------------------------------------------------------------>
       <!--群名称弹出框 start------------------------------------------------------------>
-      <el-dialog :visible.sync="editGroupNameWindow" title="编辑群名称" :model="false" :close-on-click-modal="false" width="420px">
+      <el-dialog :visible.sync="editGroupNameWindow" title="编辑群名称" :model="false" :close-on-click-modal="false" width="420px" :inert="!editGroupNameWindow">
         <div>
           <div>
             <el-input type="textarea" resize="none" v-model="editMessageTitle" placeholder="请输入" maxlength="20"></el-input>
@@ -1066,7 +1066,7 @@
       </el-dialog>
       <!--群名称弹出框 end------------------------------------------------------------>
       <!--群公告弹出框 start------------------------------------------------------------>
-      <el-dialog :visible.sync="editGroupNoticeWindow" title="编辑群公告" :model="false" :close-on-click-modal="false" width="420px">
+      <el-dialog :visible.sync="editGroupNoticeWindow" title="编辑群公告" :model="false" :close-on-click-modal="false" width="420px" :inert="!editGroupNoticeWindow">
         <div>
           <div>
             <el-input type="textarea" resize="none" v-model="editGroupProfile" placeholder="请输入" rows="10" maxlength="500"></el-input>
@@ -1137,7 +1137,7 @@
       <!--分享名片 end------------------------------------------------------------>
         
       <!--发送好友申请弹出框 start------------------------------------------------------------>
-      <el-dialog :visible.sync="addFriendWindowStatus" title="朋友申请" :model="false" :close-on-click-modal="false" width="420px">
+      <el-dialog :visible.sync="addFriendWindowStatus" title="朋友申请" :model="false" :close-on-click-modal="false" width="420px" :inert="!addFriendWindowStatus">
         <div>
           <div>
             <el-form :model="form" ref="form" autocomplete="off" label-position="left">
@@ -1157,7 +1157,7 @@
       <!--发送好友申请弹出框 end------------------------------------------------------------>
 
       <!--加入群聊弹出框 start------------------------------------------------------------>
-      <el-dialog :visible.sync="addGroupWindow" title="加入群聊" :model="false" :close-on-click-modal="false" width="420px">
+      <el-dialog :visible.sync="addGroupWindow" title="加入群聊" :model="false" :close-on-click-modal="false" width="420px" :inert="!addGroupWindow">
         <div class="addGroupWindow">
           <div class="addGroupWindowImg">
             <img src="@/assets/chat/user/group.jpg" alt="">
@@ -1202,7 +1202,7 @@
             </div>
       </el-dialog>
       <!--请求验证弹出框 start------------------------------------------------------------>
-      <el-dialog :visible.sync="friendWindowStatus" title="通过朋友验证" :model="false" :close-on-click-modal="false" width="420px">
+      <el-dialog :visible.sync="friendWindowStatus" title="通过朋友验证" :model="false" :close-on-click-modal="false" width="420px" :inert="!friendWindowStatus">
         <div>
           <div class="friendWindowText">
             确定要添加<span>{{friendInfo.user_name}}</span>为好友吗?
@@ -1215,7 +1215,7 @@
       </el-dialog>
       <!--请求验证弹出框 end------------------------------------------------------------>
       <!--修改备注弹出框 start------------------------------------------------------------>
-      <el-dialog :visible.sync="editWindowStatus" title="修改备注" :modal="false" :close-on-click-modal="false" width="420px">
+      <el-dialog :visible.sync="editWindowStatus" title="修改备注" :modal="false" :close-on-click-modal="false" width="420px" :inert="!editWindowStatus">
         <div>
           <div>
             <el-form :model="form" ref="form" autocomplete="off" label-position="left">
@@ -1233,7 +1233,7 @@
         </div>
       </el-dialog>
       <!--修改备注弹出框 end------------------------------------------------------------>
-      <el-dialog :visible.sync="searchWindowStatus" title="聊天记录" :close-on-click-modal="false" width="420px" :modal="false">
+      <el-dialog :visible.sync="searchWindowStatus" title="聊天记录" :close-on-click-modal="false" width="420px" :modal="false" :inert="!searchWindowStatus">
         <div class="searchFriendBox">
               <div class="searchFriendItem">
                 <div class="searchFriendAvatar">
@@ -1273,6 +1273,8 @@
     name: "ChatPanel",
     data() {
       return {
+        userInteracted: false,
+        pendingAudio: null,
         newMessage: {},
         token:'',
         ones:0,
@@ -1447,10 +1449,48 @@
       }
     },
     methods: {
+      checkNotificationEnv() {
+        // 检查 Notification API 支持
+        if (!('Notification' in window)) {
+          this.$notify({
+            title: '浏览器不支持通知',
+            message: '请使用支持 Notification 的现代浏览器',
+            type: 'warning',
+            duration: 8000
+          });
+          return;
+        }
+        // 检查安全环境
+        const isSecure = location.protocol === 'https:' ||
+          location.hostname === 'localhost' ||
+          location.hostname === '127.0.0.1';
+        if (!isSecure) {
+          this.$notify({
+            title: '通知功能受限',
+            message: '请用 localhost 或 https 访问以启用浏览器通知',
+            type: 'warning',
+            duration: 8000
+          });
+          return;
+        }
+        // 主动请求权限
+        if (Notification.permission === 'default') {
+          Notification.requestPermission().catch(() => {});
+        }
+      },
+      showMessage(item){
+        console.log("showMessage走没走",item)
+        this.notificationShowMessage(item);
+        this.playVoice();
+      },
       clickShowMessage(item) {
         // this.newMessage = item;
         // 先关闭所有旧消息
         this.$message.closeAll();
+        // 检查通知权限
+      // 检查 Notification 支持和安全环境
+        this.notificationMessage(item);
+        this.playVoice();
         this.$message({
           dangerouslyUseHTMLString: true,
           message: `
@@ -1474,6 +1514,109 @@
           }
         }, 50);
       },
+      playVoice(){
+        //http://img.bjzxtw.org.cn/master/video/mp3/11302.mp3 播放这段音频
+        const audio = new Audio('http://img.bjzxtw.org.cn/master/video/mp3/xxxtx.mp3');
+        
+        if (this.userInteracted) {
+          audio.play().catch(e => console.error('Audio play failed:', e));
+        } else {
+          // Store audio to play after first interaction
+          this.pendingAudio = audio;
+        }
+      },
+      notificationShowMessage(item) {
+        const iconList = {
+          2:'http://img.bjzxtw.org.cn/master/www/icon/zixun.png',
+          3:'http://img.bjzxtw.org.cn/master/www/icon/sahngpin.png',
+          4:'http://img.bjzxtw.org.cn/master/www/icon/keti.png',//课题
+          5:'http://img.bjzxtw.org.cn/master/www/icon/tongzhi.png',//通知
+          6:'http://img.bjzxtw.org.cn/master/www/icon/tousu.png',//投诉
+          7:'http://img.bjzxtw.org.cn/master/www/icon/zhaopin.png',
+          8:'http://img.bjzxtw.org.cn/master/www/icon/zhaopin.png',
+          9:'http://img.bjzxtw.org.cn/master/www/icon/zhaopin.png',
+          10:'http://img.bjzxtw.org.cn/master/www/icon/zhaopin.png',
+          11:'http://img.bjzxtw.org.cn/master/www/icon/zhaopin.png',
+        }
+        if (!('Notification' in window)) {
+          this.$notify({
+            title: '浏览器不支持通知',
+            message: '请使用最新版 Chrome、Edge、Firefox 或 Safari 浏览器',
+            type: 'error',
+            duration: 8000
+          });
+          return;
+        }
+        // console.log("===============:notificationMessage",window)
+        if (!('Notification' in window)) return;
+          const isSecure = location.protocol === 'https:' ||
+            location.hostname === 'localhost' ||
+            location.hostname === '127.0.0.1';
+          if (!isSecure) return;
+
+          if (Notification.permission === 'granted') {
+            console.log("===============:Notification.permission === 'granted'",item)
+            const notification = new Notification(item.title, {
+              icon: iconList[item.messageType],
+              body: item.content,
+              requireInteraction: true, // 需要用户交互才能关闭
+              renotify: true, // 每次新消息都提醒
+              tag: 'message'
+            });
+            notification.onclick = () => {
+              try { window.focus(); } catch (e) {}
+              this.scorllBottom && this.scorllBottom();
+              notification.close();
+            };
+          } else if (Notification.permission === 'denied') {
+            this.$notify({
+              title: '通知权限已禁用',
+              message: '请在浏览器设置中启用通知权限以接收新消息提醒',
+              type: 'warning',
+              duration: 5000
+            });
+          }
+      },
+      notificationMessage(item) {
+        if (!('Notification' in window)) {
+          this.$notify({
+            title: '浏览器不支持通知',
+            message: '请使用最新版 Chrome、Edge、Firefox 或 Safari 浏览器',
+            type: 'error',
+            duration: 8000
+          });
+          return;
+        }
+        // console.log("===============:notificationMessage",window)
+        if (!('Notification' in window)) return;
+          const isSecure = location.protocol === 'https:' ||
+            location.hostname === 'localhost' ||
+            location.hostname === '127.0.0.1';
+          if (!isSecure) return;
+
+          if (Notification.permission === 'granted') {
+            console.log("===============:Notification.permission === 'granted'",item)
+            const notification = new Notification(item.user_name, {
+              icon: 'http://img.bjzxtw.org.cn/master/www/icon/group.png',
+              body: item.content,
+              requireInteraction: true, // 需要用户交互才能关闭
+              renotify: true, // 每次新消息都提醒
+              tag: 'message'
+            });
+            notification.onclick = () => {
+              try { window.focus(); } catch (e) {}
+              this.scorllBottom && this.scorllBottom();
+              notification.close();
+            };
+          } else if (Notification.permission === 'denied') {
+            this.$notify({
+              title: '通知权限已禁用',
+              message: '请在浏览器设置中启用通知权限以接收新消息提醒',
+              type: 'warning',
+              duration: 5000
+            });
+          }
+      },
       onCustomMessageClick(item) {
 
         // 通知父组件显示ChatPanel,并把 item 作为参数传递
@@ -2485,7 +2628,7 @@
           }else{
             console.log("当前消息为群消息,但是用户未开启此聊天窗口!")
           }
-        }else{
+        }else if(message.talk_type==1){
           //当前的单聊消息是自己发的,回显到聊天框中
           if(Number(this.receiverId)==Number(message.receiver_id)){
             //如果message的receiverId和会话的receiverId一致,说明这条信息是自己发的回显
@@ -2555,6 +2698,9 @@
   
             
           }
+        }else{
+          //其它消息提醒全走这里
+          this.showMessage(message);
         }
       },
       // 5.2 发送消息
@@ -2568,7 +2714,7 @@
           };
           // console.log("websocket发送消息:",wsService.getReadyState,WebSocket.OPEN)
           //发送消息
-          console.log("发送消息前的状态:",wsService.getReadyState() ,"==========" ,WebSocket.OPEN)
+          console.log("发送消息前的状态:",wsService.getReadyState() ,"==========" ,WebSocket.OPEN,message)
           if (wsService.getReadyState() === WebSocket.OPEN) {
             wsService.send(message);
           }
@@ -3115,6 +3261,22 @@
       }
     },
     mounted() {
+      this.checkNotificationEnv();
+    // Add user interaction listeners
+    const handleInteraction = () => {
+      this.userInteracted = true;
+      // Play pending audio if exists
+      if (this.pendingAudio) {
+        this.pendingAudio.play().catch(e => console.error('Audio play failed:', e));
+        this.pendingAudio = null;
+      }
+      // Remove listeners after first interaction
+      document.removeEventListener('click', handleInteraction);
+      document.removeEventListener('keydown', handleInteraction);
+    };
+
+    document.addEventListener('click', handleInteraction);
+    document.addEventListener('keydown', handleInteraction);
       // console.log("用户头像信息:",this.$store.state.user.avatar)
       // //开启websocket连接 start---------------------------------------->
       // //1.获取admin-token

+ 5 - 5
src/utils/baseUrl.js

@@ -16,18 +16,18 @@ const URL = {
 
 
 
-  webUrl: 'adminpre.bjzxtw.org.cn',//管理系统地址,如果用户因为某种原因以外退出,需要一个userurl用于请求接口
-  baseUrl: 'http://apipre1.bjzxtw.org.cn:29501',//pre环境域名2
-  //baseUrl: 'http://192.168.1.201:9501',//刘佳伟本地环境
+  // webUrl: 'adminpre.bjzxtw.org.cn',//管理系统地址,如果用户因为某种原因以外退出,需要一个userurl用于请求接口
+  // baseUrl: 'http://apipre1.bjzxtw.org.cn:29501',//pre环境域名2
+  baseUrl: 'http://192.168.1.201:9501',//刘佳伟本地环境
   //baseUrl:'http://192.168.1.115:9501',//冯蕊的本地环境
   //baseUrl: 'http://192.168.1.129:9501',//刘剑的本地环境
-  //WebsocketUrl: 'ws://192.168.1.201:9506',//刘佳伟 -- websocket地址
+  WebsocketUrl: 'ws://192.168.1.201:9501',//刘佳伟 -- websocket地址
   // WebsocketUrl: 'ws://192.168.1.127:9506'//刘剑 -- websocket地址
   //WebsocketUrl: 'ws://183.131.25.186:9506',//测试环境ip -- websocket地址
   //WebsocketUrl: 'ws://admindev.bjzxtw.org.cn:9506',//测试环境域名
   //WebsocketUrl: 'ws://116.131.8.26:9506'//pre环境ip -- websocket地址
   //WebsocketUrl: 'ws://adminpre.bjzxtw.org.cn:9506'//pre环境域名 -- websocket地址
-  WebsocketUrl: 'ws://apipre1.bjzxtw.org.cn:29501',//pre环境域名 -- websocket地址
+  // WebsocketUrl: 'ws://apipre1.bjzxtw.org.cn:29501',//pre环境域名 -- websocket地址
   //WebsocketUrl: 'ws://103.105.201.2:9506'//正式环境ip -- websocket地址
   //WebsocketUrl: 'wss://flzxw.bjzxtw.org.cn'//正式环境域名 wss可用 -- websocket地址
   WebCloginUrl: 'http://apipre1.bjzxtw.org.cn/api/loginapi', //单点登录地址

+ 2 - 0
src/utils/websocketService.js

@@ -13,6 +13,7 @@ class WebSocketService {
     }
     const baseUrls = baseUrl.WebsocketUrl; // 替换为你的 ws 地址
     const url = `${baseUrls}?token=${token}`;
+    console.log('#############websocket url:', url);
     this.ws = new window.WebSocket(url);
 
     this.ws.onopen = () => {
@@ -22,6 +23,7 @@ class WebSocketService {
 
     this.ws.onmessage = (event) => {
       const message = JSON.parse(event.data);
+      console.log('#############websocket message:', message);
       this.messageListeners.forEach(cb => {
         if (typeof cb === 'function') {
           cb(message);