Bläddra i källkod

消息关键词触发特效

wangqi49 1 månad sedan
förälder
incheckning
4b592b7f50

+ 49 - 20
webchat-front-admin/src/views/editArticle.vue

@@ -213,26 +213,7 @@ export default {
 
     const toolbarConfig = {}
     const editorConfig = {
-      placeholder: '请输入内容...',
-      uploadImage: {
-        server: '/admin-service/file/upload', 
-        fieldName: 'file', 
-        headers: {
-          'oauth-code': oauthCode.value,
-          'origin-url': window.location.href,
-          'upload-path': 'images/editor'
-        },
-        customInsert(res, insertFn) {
-          // 图片上传成功后,将图片插入到编辑器中
-          if (res.code === 200) {
-            insertFn(res.data.url, '', '');
-          } else {
-            message.error(res.message || '图片上传失败');
-          }
-        },
-        maxFileSize: 2 * 1024 * 1024, // 图片最大大小,单位字节
-        allowedFileTypes: ['image/jpeg', 'image/png', 'image/gif'] // 允许上传的图片类型
-      }
+      placeholder: '请输入内容...'
     };
 
 
@@ -245,6 +226,54 @@ export default {
 
     const handleCreated = (editor) => {
       editorRef.value = editor // 记录 editor 实例,重要!
+      const { MENU_CONF } = editor.getConfig()
+      MENU_CONF['uploadImage'] = {
+        server: '/admin-service/file/upload/editor',
+        fieldName: 'file',
+        allowedFileTypes: ['image/*'],
+        
+        // 自定义 headers
+        headers: {
+          // 'Content-Type': 'multipart/form-data',
+          'oauth-code': oauthCode.value,
+          'origin-url': window.location.href,
+          'upload-path': 'article/editor'
+        },
+
+        // 跨域携带 cookie(如果需要)
+        withCredentials: true,
+
+        // 上传前处理
+        onBeforeUpload(file) {
+          console.log('【上传文件】', file)
+          return file // 返回 false 则终止上传
+        },
+
+        // 自定义插入
+        customInsert(res, insertFn) {
+          console.log('【上传响应】', res)
+          if (res.code === 40001) {
+            window.location.href = res.redirect_url
+            return
+          }
+          if (res.code !== 200) {
+            message.error(res.message || '上传失败')
+            return
+          }
+          const url = res.data?.url || res.data?.imageUrl
+          if (!url) {
+            message.error('图片地址获取失败')
+            return
+          }
+          insertFn(url, '', '')
+        },
+
+        // 错误处理
+        onError(file, err, res) {
+          console.error('【上传错误】', { file, err, res })
+          message.error(`上传失败: ${err?.message || '服务器错误'}`)
+        }
+      }
     }
 
     // 公众号推文

BIN
webchat-front-client/src/static/images/cake.png


+ 53 - 28
webchat-front-client/src/views/ChatGroup.vue

@@ -29,6 +29,11 @@
                     {{ chatMessage.message }}
                 </div>
             </template>
+            <!-- 消息内容出发特效关键词 -->
+            <ChatEmojiAnimations 
+                v-if="showChatEmojiAnimations"
+                class="chatEmojiAnimationsComponent"
+                :emojis="emojis"/>
         </div>
     </div>
     <div class="chat-editer-menu-container">
@@ -75,6 +80,7 @@
         v-model:visible="groupVideoModalVisible"
         :width="1045"
         :height="800"
+        class="custom-dark-modal"
         :okButtonProps="{ style: { display: 'none' } }"
         :cancelButtonProps="{ style: { display: 'none' } }"
         @update:visible="handleVideoModalVisibleChange"
@@ -96,13 +102,15 @@
   import axios from 'axios';
   import { inject, defineComponent, ref, onMounted, onUnmounted, watch } from 'vue';
   import Video2Group from './video2group.vue';
+  import ChatEmojiAnimations from './animation/emojiAnimations.vue';
   export default defineComponent({
     props: {
         openVideoRef: Boolean,
         selectChatUserRef: Object
     },
     components: {
-        Video2Group
+        Video2Group,
+        ChatEmojiAnimations
     },
     setup(props) {
         const inputValue = ref('');
@@ -118,6 +126,13 @@
         const groupDetailVisible = ref(false)
         const groupVideoModalVisible = ref(false);
 
+        const showChatEmojiAnimations = ref(false);
+        const emojis = ref([""]);
+
+        // 维护关键词,emoji特效
+        const messageKeywords = ["生日快乐", "比心", "爱你", "礼物", "红包", "💣", "炸弹", "💩", "屎"];
+        const messageKeywordsEmojis = ["🍰", "♥️", "😘", "🎁", "🧧", "💣", "💣", "💩", "💩"];
+
         const loadChatMessages = (chatUserId) => {
             // 先清空
             axios.get(`/client-service/chat/message/list/`+chatUserId, {})
@@ -167,7 +182,10 @@
                 if (messageType === 1) {
                     // 处理对话
                     chatMessageArr.value.push(socketMessage);
+                     // 关键词出发emoji飘落特效
+                    handleMessageKeyword(socketMessage.message);
                 }
+                
             };
 
             socket.value.onerror = (error) => {
@@ -251,12 +269,30 @@
                     chatMessageArr.value.push(JSON.parse(chatMessage));
                     // ws发送消息
                     socket.value.send(chatMessage);
+                    // 关键词出发emoji飘落特效
+                    handleMessageKeyword(inputValue.value.trim());
                     // 清空输入框
                     inputValue.value = ''; 
                 }
             }
         }
 
+        // 关键词出发emoji飘落特效
+        const handleMessageKeyword = (message) => {
+            showChatEmojiAnimations.value = false;
+            emojis.value = [];
+            for(var i = 0; i < messageKeywords.length;  i ++) {
+                if (message.includes(messageKeywords[i])) {
+                    showChatEmojiAnimations.value = true;
+                    emojis.value.push(messageKeywordsEmojis[i]);
+                     // 延迟隐藏保证动画完成
+                    setTimeout(() => {
+                        showChatEmojiAnimations.value = false;
+                    }, 8000)
+                }
+            }
+        }
+
         const handleClick = (type) => {
             switch (type) {
                 case 'emoji':
@@ -286,8 +322,11 @@
       onUnmounted(() => {
        
       });
+
       
       return {
+        showChatEmojiAnimations,
+        emojis,
         openVideo,
         groupVideoModalVisible,
         inputValue,
@@ -453,35 +492,21 @@
     font-size: 10px;
     margin-top: -5px;
 }
-/* 修改遮罩背景色 */
-::v-deep(.ant-modal-mask) {
-  background-color: rgba(0, 0, 0, 0.7); /* 半透明黑色遮罩 */
-}
-
-/* 修改遮罩背景色 */
-.ant-modal-mask {
-  background-color: rgba(0, 0, 0, 0.7); /* 半透明黑色遮罩 */
+/* 强制提升优先级 */
+.custom-dark-modal.ant-modal .ant-modal-content {
+  background: #000 !important;
+  color: white !important;
 }
 
-/* 修改模态框内容背景色 */
-.ant-modal-content {
-  background-color: black !important; /* 模态框整体背景色 */
+.custom-dark-modal.ant-modal .ant-modal-header {
+  background: #000 !important;
 }
-
-.ant-modal-body {
-  background-color: black !important; /* 模态框主体内容背景色 */
-}
-
-.ant-modal-title {
-  color: white; /* 标题颜色 */
-}
-
-.ant-modal-header {
-  background-color: black !important; /* 头部背景色 */
-  border-bottom: none; /* 去掉底部边框 */
-}
-
-.ant-modal-footer {
-  background-color: black !important; /* 底部背景色 */
+.chatEmojiAnimationsComponent {
+    position: absolute;
+    left: 0px;
+    top: 0px;
+    width: 100%;
+    height: 100%;
+    z-index: 999;
 }
   </style>

+ 211 - 6
webchat-front-client/src/views/ChatUser.vue

@@ -26,6 +26,11 @@
                     {{ chatMessage.message }}
                 </div>
             </template>
+            <!-- 消息内容出发特效关键词 -->
+            <ChatEmojiAnimations 
+                v-if="showChatEmojiAnimations"
+                class="chatEmojiAnimationsComponent"
+                :emojis="emojis"/>
         </div>
     </div>
     <div class="chat-editer-menu-container">
@@ -60,13 +65,14 @@
         v-model:visible="videoModalVisible"
         :width="1045"
         :height="530"
+        class="custom-dark-modal"
         :okButtonProps="{ style: { display: 'none' } }"
         :cancelButtonProps="{ style: { display: 'none' } }"
         @update:visible="handleVideoModalVisibleChange"
         :maskClosable="false"
         >
         <template #title>
-            <div class="modal-title">
+            <div class="modal-title" style="color: white">
                 正在与 <b style="color: brown">{{ selectChatUser?.userName }} </b>音视频通话
             </div>
         </template>
@@ -77,22 +83,77 @@
             @close="handleVideoClose"/>
     </a-modal>
 
+    <a-modal
+        v-model:visible="sendRedPacketModalVisible"
+        :width="400"
+        :height="450"
+        class="red-packet-modal"
+        :okButtonProps="{ style: { display: 'none' } }"
+        :cancelButtonProps="{ style: { display: 'none' } }"
+        >
+        <template #title>
+            <div class="modal-title">
+                发红包
+            </div>
+        </template>
+          <!-- 隐藏的文件上传input -->
+            <input 
+                type="file"
+                ref="redPacketFileInput"
+                accept="image/*"
+                style="display: none;"
+                @change="handleFileUpload"
+            />
+        <a-input prefix="¥" suffix="RMB" class="red-packet-input" placeholder="红包金额 ¥0.00"/>
+        <a-input suffix="😊"  class="red-packet-input" placeholder="恭喜发财,大吉大利"/>
+        <a-dropdown style="width: 100%">
+            <template #overlay>
+                <a-menu @click="uploadRedPacketCover">
+                    <a-menu-item key="1">
+                        <PictureOutlined />
+                        上传红包红面
+                    </a-menu-item>
+                </a-menu>
+            </template>
+            <a-button class="red-packet-cover-button">
+                红包封面 <RightOutlined />
+                 <!-- 显示预览图 -->
+                <img 
+                    v-if="redPacketCover" 
+                    :src="redPacketCover" 
+                    class="cover-preview"
+                />
+            </a-button>
+        </a-dropdown>
+        <div class="red-packet-money">
+            ¥ {{redPacketMoney}}
+        </div>
+        <a-button type="primary" class="send-red-packet-button">塞钱进红包</a-button>
+
+        <div class="red-packet-tip">
+            使用WebChat钱包余额直接发红包
+        </div>
+
+    </a-modal>
+
   </template>
   
   <script>
   import axios from 'axios';
   import { inject, defineComponent, ref, onMounted, onUnmounted, watch } from 'vue';
+  import { message } from 'ant-design-vue';
   import Video2User from './video2User.vue';
+  import ChatEmojiAnimations from './animation/emojiAnimations.vue';
   export default defineComponent({
     props: {
         selectChatUserRef: Object,
         openVideoRef: Boolean
     },
     components: {
-        Video2User
+        Video2User,
+        ChatEmojiAnimations
     },
     setup(props) {
-
         const inputValue = ref('');
         const socket = ref(null);
         const messageQueue = ref([]); // 存储未发送的消息队列
@@ -104,6 +165,17 @@
         const heartbeatInterval = ref(30000); // 心跳间隔时间,单位:毫秒
         const chatMessageArr = ref([]);
         const videoModalVisible = ref(false)
+        const sendRedPacketModalVisible = ref(false)
+        const redPacketMoney = ref(0.00)
+        const redPacketCover = ref(null);
+        const redPacketFileInput = ref(null);
+        const uploadUrl = `/client-service/chat/file/upload`; // 上传API
+        const showChatEmojiAnimations = ref(false);
+        const emojis = ref([""]);
+
+        // 维护关键词,emoji特效
+        const messageKeywords = ["生日快乐", "比心", "爱你", "礼物", "红包", "💣", "炸弹", "💩", "屎"];
+        const messageKeywordsEmojis = ["🍰", "♥️", "😘", "🎁", "🧧", "💣", "💣", "💩", "💩"];
 
         const loadChatMessages = (chatUserId) => {
             // 先清空
@@ -169,8 +241,8 @@
                 }
                 const messageType = socketMessage.type;
                 if (messageType === 1) {
-                    // 处理对话
                     chatMessageArr.value.push(socketMessage);
+                    handleMessageKeyword(socketMessage.message);
                 }
             };
 
@@ -202,14 +274,36 @@
         // 链接ws
         connectWebSocket();
 
+        const sendMessageObj = (message) => {
+            sendMessage(JSON.stringify(message));
+        }
+
         const sendMessage = (message) => {
             if (isConnected.value) {
                 socket.value.send(message);
             } else {
                 messageQueue.value.push(message);
             }
+            // 关键词出发emoji飘落特效
+            handleMessageKeyword(message);
         };
 
+        // 关键词出发emoji飘落特效
+        const handleMessageKeyword = (message) => {
+            showChatEmojiAnimations.value = false;
+            emojis.value = [];
+            for(var i = 0; i < messageKeywords.length;  i ++) {
+                if (message.includes(messageKeywords[i])) {
+                    showChatEmojiAnimations.value = true;
+                    emojis.value.push(messageKeywordsEmojis[i]);
+                     // 延迟隐藏保证动画完成
+                    setTimeout(() => {
+                        showChatEmojiAnimations.value = false;
+                    }, 8000)
+                }
+            }
+        }
+
         const handleInput = (event) => {
             inputValue.value = event.target.value;
         };
@@ -229,13 +323,57 @@
                     // 当前用户自己发送的消息直接提交到页面渲染
                     chatMessageArr.value.push(JSON.parse(chatMessage));
                     // ws发送消息
-                    socket.value.send(chatMessage);
+                    sendMessage(chatMessage);
                     // 清空输入框
                     inputValue.value = ''; 
                 }
             }
         }
 
+        // 点击上传菜单项
+        const uploadRedPacketCover = ({ key }) => {
+            if (key === '1') {
+                // 触发隐藏的input点击
+                redPacketFileInput.value.click();
+            }
+        };
+
+        // 处理文件选择
+        const handleFileUpload = async (e) => {
+            const file = e.target.files[0];
+            if (!file) return;
+            // 校验文件类型
+            if (!file.type.startsWith('image/')) {
+                message.error('请选择图片文件');
+                return;
+            }
+            // 校验文件大小(示例限制2MB)
+            if (file.size > 10 * 1024 * 1024) {
+                message.error('图片大小不能超过10MB');
+                return;
+            }
+            // 方式2:上传到服务器(示例使用axios)
+            try {
+                const formData = new FormData();
+                formData.append('file', file);
+                const response = await axios.post(uploadUrl, formData, {
+                    headers: {
+                    'Content-Type': 'multipart/form-data',
+                    'origin-url': window.location.href,
+                    'upload-path': 'images/redPacket'
+                    }
+                });
+                if (response.data.code === 40001) {
+                    window.location.href = response.data.redirect_url;
+                }
+                // 预览红包封面图
+                redPacketCover.value = response.data.data.url;
+            } catch (error) {
+                message.error('上传失败');
+                console.error('Upload error:', error);
+            }
+        };
+
         const handleClick = (type) => {
             switch (type) {
                 case 'emoji':
@@ -245,6 +383,7 @@
                 case 'redPacket':
                     console.log('点击了红包按钮');
                     // 在此添加红包按钮的逻辑,例如打开红包发送界面等
+                    sendRedPacketModalVisible.value = true;
                     break;
                 case 'audioVideo':
                     // 发起音视频通话
@@ -266,6 +405,9 @@
       });
       
       return {
+        uploadUrl,
+        redPacketCover,
+        redPacketFileInput,
         openVideo,
         videoModalVisible,
         inputValue,
@@ -276,13 +418,20 @@
         reconnectInterval,
         heartbeatInterval,
         chatMessageArr,
+        sendRedPacketModalVisible,
+        redPacketMoney,
+        showChatEmojiAnimations,
+        emojis,
         handleVideoModalVisibleChange,
         handleClick,
         handleSendMessage,
         handleInput,
         connectWebSocket,
         loadChatMessages,
-        handleVideoClose
+        handleVideoClose,
+        handleFileUpload,
+        uploadRedPacketCover,
+        sendMessageObj,
       };
     }
   });
@@ -418,4 +567,60 @@
     border-style: solid;
     border-color: transparent #f5f5f5 transparent transparent;
 }
+/* 强制提升优先级 */
+.custom-dark-modal.ant-modal .ant-modal-content {
+  background: #000 !important;
+  color: white !important;
+}
+.custom-dark-modal.ant-modal .ant-modal-header {
+  background: #000 !important;
+}
+.red-packet-input, .red-packet-cover-button {
+    height: 50px;
+    margin-top: 15px;
+    background-color: white;
+}
+.red-packet-cover-button {
+    width: 100%;
+    text-align: left;
+}
+.send-red-packet-button {
+    background-color: #ff5e4b;
+    color: white;
+    width: 170px;
+    height: 45px;
+    margin-top: 30px;
+    margin-left: 100px;
+}
+.send-red-packet-button:hover {
+    background-color:rgb(252, 68, 48);
+}
+.red-packet-money {
+    position: relative;
+    width: 100%;
+    line-height: 50px;
+    font-size: 35px;
+    margin-top: 40px;
+    text-align: center;
+    font-weight: 700;
+}
+.red-packet-tip {
+    color: gray;
+    font-size: 12px;
+    margin-top: 100px;
+    text-align: center;
+}
+.cover-preview {
+    position: relative;
+    float: right;
+    height: 20px;
+}
+.chatEmojiAnimationsComponent {
+    position: absolute;
+    left: 0px;
+    top: 0px;
+    width: 100%;
+    height: 100%;
+    z-index: 999;
+}
   </style>

+ 120 - 0
webchat-front-client/src/views/animation/emojiAnimations.vue

@@ -0,0 +1,120 @@
+<template>
+  <div class="emoji-container">
+    <span 
+      v-for="emoji in displayEmojis" 
+      :key="emoji.id"
+      :class="emoji.class"
+      :style="emoji.style"
+      @animationend="removeEmoji(emoji.id)"
+    >{{ emoji.char }}</span>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    emojis: {
+      type: Array,
+      default: () => ['🎁', '🎉', '✨', '🎈', '💝']
+    },
+    density: {
+      type: Number,
+      default: 0.004 // 控制emoji特效表情密度,值越小越稀疏
+    }
+  },
+  data() {
+    return {
+      screenWidth: window.innerWidth,
+      screenHeight: window.innerHeight,
+      displayEmojis: [],
+      emojiCounter: 0
+    }
+  },
+  mounted() {
+    this.generateEmojis()
+    window.addEventListener('resize', this.handleResize)
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize)
+  },
+  methods: {
+    getRandomClass() {
+      const classes = ['emoji-big', 'emoji-medium', 'emoji-small']
+      return classes[Math.floor(Math.random() * classes.length)]
+    },
+    generateEmojis() {
+      const numEmojis = Math.floor(this.screenWidth * this.density)
+      
+      this.displayEmojis = Array.from({ length: numEmojis }, () => {
+        const char = this.emojis[Math.floor(Math.random() * this.emojis.length)]
+        const initialRotation = Math.floor(Math.random() * 360)
+        const animationDuration = (Math.random() * 2 + 8).toFixed(1) // 8-10秒
+
+        return {
+          id: ++this.emojiCounter,
+          char,
+          class: this.getRandomClass(),
+          style: {
+            '--start-rotate': `${initialRotation}deg`,
+            '--end-rotate': `${initialRotation + 180}deg`, // 修正此处
+            animation: `fall ${animationDuration}s linear forwards`,
+            left: `${Math.random() * 100}%`, // 改为百分比布局
+            opacity: (Math.random() * 0.4 + 0.3).toFixed(2),
+            'animation-delay': `${Math.random() * 5}s`
+          }
+        }
+      })
+    },
+    removeEmoji(id) {
+      this.displayEmojis = this.displayEmojis.filter(emoji => emoji.id !== id)
+    },
+    handleResize: () => {
+      this.screenWidth = window.innerWidth
+      this.screenHeight = window.innerHeight
+      this.displayEmojis = []
+      requestAnimationFrame(() => this.generateEmojis())
+    }
+  }
+}
+</script>
+
+<style scoped>
+.emoji-container {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 100vh;
+  pointer-events: none;
+  overflow: hidden;
+  z-index: 9999;
+}
+
+span {
+  position: absolute;
+  animation-timing-function: cubic-bezier(0.55, 0.085, 0.68, 0.53);
+  transform: translateY(-150%) rotate(var(--start-rotate));
+  will-change: transform, opacity;
+}
+
+.emoji-big { font-size: 2.2em; }
+.emoji-medium { font-size: 1.6em; }
+.emoji-small { font-size: 1em; }
+</style>
+
+<style>
+@keyframes fall {
+  0% {
+    transform: 
+      translateY(-150%) 
+      rotate(var(--start-rotate));
+    opacity: var(--opacity);
+  }
+  100% {
+    transform: 
+      translateY(calc(100vh + 150px)) 
+      rotate(var(--end-rotate));
+    opacity: 0;
+  }
+}
+</style>

+ 33 - 4
webchat-front-client/src/views/article.vue

@@ -5,13 +5,16 @@
     </div>
 </template>
 
-<script>
+<script scoped>
 import axios from 'axios';
+import { provide, defineComponent, ref, onMounted, onUnmounted, watch } from 'vue';
 import '@wangeditor/editor/dist/css/style.css';
 
 export default {
   data() {
     return {
+      oauthCode: null,
+      loginUser: null,
       articleId: null,
       articleData: null
     };
@@ -20,6 +23,7 @@ export default {
     // 正确获取路由参数的方式
     this.articleId = this.$route.query.id;
     this.loadArticle();
+    this.loadCurrentUserInfo();
   },
   watch: {
     // 修正监听器写法
@@ -29,6 +33,23 @@ export default {
     }
   },
   methods: {
+    async loadCurrentUserInfo () {
+      this.oauthCode = new URLSearchParams(window.location.search).get('oauthCode');
+      const response = await axios.get(`/client-service/chat/account/current/info`, {
+        // 这里可以添加请求的配置,例如 headers 或 params
+        headers: {
+          'Content-Type': 'application/json',
+          'origin-url': window.location.href,
+          'oauth-code': this.oauthCode
+        }
+      })
+      if (response.data.code === 40001) {
+          window.location.href = response.data.redirect_url;
+          return;
+      } else if (response.data.code === 200) {
+        this.loginUser = response.data.data;
+      }
+    },
     async loadArticle() {
       if (!this.articleId) return;
       try {
@@ -41,7 +62,6 @@ export default {
           window.location.href = response.data.redirect_url;
           return;
         }
-        // 修正数据赋值方式
         this.articleData = response.data.data;
       } catch (error) {
         console.error('加载文章失败:', error);
@@ -51,7 +71,10 @@ export default {
 };
 </script>
 
-<style scoped>
+<style>
+    body {
+      background-color: whitesmoke;
+    }
     /* 样式保持不变 */
     .article-container {
         position: absolute;
@@ -65,9 +88,15 @@ export default {
         background-color: white;
     }
     .article-title {
-        font-size: 22px;
+        font-size: 25px;
+        padding: 40px 40px 0px 40px;
     }
     .article-content {
         margin-top: 2px;
+        padding: 40px;
+        margin-bottom: 50px;
+    }
+    .article-content img {
+      width: 100%
     }
 </style>

+ 3 - 3
webchat-front-client/src/views/chat.vue

@@ -584,7 +584,7 @@ export default defineComponent({
     const connectWebSocket = () => {
       socket.value = new WebSocket(`/connect-service/ws/chat/PC_WEB_INDEX/`+userId.value);
       socket.value.onopen = () => {
-        console.log('WebSocket 已连接');
+        message.info("网络连接成功");
         isConnected.value = true;
         // 发送队列中的消息
         messageQueue.value.forEach(message => socket.value.send(message));
@@ -620,7 +620,7 @@ export default defineComponent({
       };
 
       socket.value.onerror = (error) => {
-        console.error('WebSocket 错误:', error);
+        message.error("网络断开,重新连接");
         isConnected.value = false;
         // 关闭当前连接
         socket.value.close();
@@ -629,7 +629,7 @@ export default defineComponent({
       };
 
       socket.value.onclose = () => {
-        console.log('WebSocket 已关闭');
+        message.error("网络断开,重新连接");
         isConnected.value = false;
         // 尝试重连
         setTimeout(connectWebSocket, reconnectInterval.value);

+ 19 - 1
webchat-front-client/src/views/video2User.vue

@@ -15,6 +15,7 @@
 <script>
   import axios from 'axios';
   import { inject, defineEmits, defineComponent, ref, onMounted, onUnmounted, watch, onBeforeUnmount } from 'vue';
+  import { message } from 'ant-design-vue';
   export default defineComponent({
     props: {
         openVideo: Boolean,
@@ -212,6 +213,7 @@
                         handleCandidate(messageData.candidate);
                         break;
                     case "leave" :
+                        handleLeave(messageData.sender);
                     break;
                 }
             };
@@ -297,8 +299,23 @@
             // TODO
         }
 
+        // 处理用户挂断场景
+        const handleLeave = (sender) => {
+            message.info("对方已挂断");
+            cleanup();
+            // 触发关闭事件
+            emit('close', 'close');
+        }
+
         const leave = () => {
+            // 通知远程用户对方已下线
+            sendObjMessage({
+                type: "leave",
+                userId: loginUser.value.userId,
+                targetUserId: selectChatUser.value.userId
+            });
             cleanup();
+            // 一对一音视频当前客户端挂断通话
             // 触发关闭事件
             emit('close', 'close');
         }
@@ -322,7 +339,8 @@
         handleAnswer,
         handleCandidate,
         cleanup,
-        leave
+        leave,
+        handleLeave
       };
     }
   });