<template> <div class="chat-right-header-container" style="text-align: center; height: 49px; line-height: 49px;"> {{selectChatUser.userName}} (公众号) </div> <div class="chat-core-container" style="background-color: whitesmoke; height: 660px; text-align: center; padding-top: 20px;"> <div class="article-card" v-for="article in articleArr" @click="viewArticle(article.publicAccountArticle?.articleId)"> <div class="article-card-cover"> <img :src="article.publicAccountArticle?.cover"> </div> <div class="article-card-title"> {{ article.publicAccountArticle?.title }} </div> <div class="article-card-description"> {{ article.publicAccountArticle?.description }} </div> </div> </div> </template> <script> import { useRouter } from 'vue-router'; import axios from 'axios'; import { inject, defineComponent, ref, onMounted, onUnmounted, watch } from 'vue'; export default defineComponent({ props: { selectChatUserRef: Object }, setup(props) { const router = useRouter(); const selectChatUser = ref(props.selectChatUserRef); const socket = ref(null); const messageQueue = ref([]); // 存储未发送的消息队列 const isConnected = ref(false); const loginUser = ref(inject('loginUser')); const reconnectInterval = ref(5000); // 重连间隔时间,单位:毫秒 const heartbeatInterval = ref(30000); // 心跳间隔时间,单位:毫秒 const articleArr = ref([]); const loadChatMessages = (chatUserId) => { // 先清空 axios.get(`/client-service/chat/message/list/`+chatUserId, {}) .then(function (response) { if (response.data.code === 40001) { // 未登录 window.location.href = response.data.redirect_url; } else if (response.data.code === 200) { if (response.data.data.length > 0) { articleArr.value = response.data.data; } } }).catch(function (error) { // 处理错误 console.error(error); }); } loadChatMessages( selectChatUser.value.userId); const viewArticle = (articleId) => { // 使用 router.resolve 生成完整的 URL const routeData = router.resolve({ name: 'article', query: { id: articleId } }); // 使用 window.open 在新窗口中打开链接 window.open(routeData.href, '_blank'); } const connectWebSocket = () => { socket.value = new WebSocket(`/connect-service/ws/chat/PC_WEB_CHAT/` + loginUser.value.userId); socket.value.onopen = () => { console.log('WebSocket 已连接'); isConnected.value = true; // 发送队列中的消息 messageQueue.value.forEach(message => socket.value.send(message)); messageQueue.value = []; startHeartbeat(); }; socket.value.onmessage = (event) => { console.log('收到WS消息:', event.data); // 在此处理收到的消息 const socketMessage = JSON.parse(event.data); if(socketMessage.senderId != selectChatUser.value.userId) { return; } const messageType = socketMessage.type; if (messageType === 4) { // 处理对话 articleArr.value.push(socketMessage); } }; socket.value.onerror = (error) => { console.error('WebSocket 错误:', error); isConnected.value = false; // 关闭当前连接 socket.value.close(); // 尝试重连 setTimeout(connectWebSocket, reconnectInterval.value); }; socket.value.onclose = () => { console.log('WebSocket 已关闭'); isConnected.value = false; // 尝试重连 setTimeout(connectWebSocket, reconnectInterval.value); }; }; const startHeartbeat = () => { const heartbeat = () => { if (isConnected.value) { socket.value.send('ping'); // 发送心跳包 } }; setInterval(heartbeat, heartbeatInterval.value); }; // 链接ws connectWebSocket(); const sendMessage = (message) => { if (isConnected.value) { socket.value.send(message); } else { messageQueue.value.push(message); } }; // 切换对话用户,父组件传递选中用户信息,监听选中用户变化 watch(() => props.selectChatUserRef, (newValue) => { selectChatUser.value = newValue; }); onMounted(() => { }); onUnmounted(() => { }); return { selectChatUser, isConnected, messageQueue, loginUser, reconnectInterval, heartbeatInterval, articleArr, connectWebSocket, sendMessage, startHeartbeat, loadChatMessages, viewArticle }; } }); </script> <style scoped> .article-card { position: relative; width: 360px; min-height: 250px; height: auto; background-color: white; border-radius: 5px; overflow: hidden; } .article-card:hover { cursor: pointer; } .chat-core-container { justify-items: center; overflow-x: hidden; overflow-y: scroll; } .article-card { margin-top: 0px; margin-bottom: 40px; } .article-card-cover { position: relative; left: 0px; top: 0px; width: 100%; height: 140px; background-color: black; overflow: hidden; } .article-card-cover img { width: 100%; } .article-card-title { line-height: 50px; font-size: 15px; text-align: left; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden; padding: 0px 15px; } .article-card-description { line-height: 22px; font-size: 13px; text-align: left; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; padding: 0px 15px; margin-top: -5px; color: #8d8787; } .chat-core-container::-webkit-scrollbar {/*滚动条整体样式*/ width: 0px; /*高宽分别对应横竖滚动条的尺寸*/ height: 0px; } .chat-core-container::-webkit-scrollbar-thumb {/*滚动条里面小方块*/ border-radius: 10px; background-color: transparent; background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%, transparent 75%, transparent); } .chat-core-container::-webkit-scrollbar-track {/*滚动条里面轨道*/ -webkit-box-shadow: inset 0 0 1px rgba(0,0,0,0.2); /*border-radius: 10px;*/ background: #EDEDED; } </style>