ChatOfficial.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <template>
  2. <div class="chat-right-header-container" style="text-align: center; height: 49px; line-height: 49px;">
  3. {{selectChatUser.userName}} (公众号)
  4. </div>
  5. <div class="chat-core-container" style="background-color: whitesmoke; height: 660px; text-align: center; padding-top: 20px;">
  6. <div class="article-card" v-for="article in articleArr" @click="viewArticle(article.publicAccountArticle?.articleId)">
  7. <div class="article-card-cover">
  8. <img :src="article.publicAccountArticle?.cover">
  9. </div>
  10. <div class="article-card-title">
  11. {{ article.publicAccountArticle?.title }}
  12. </div>
  13. <div class="article-card-description">
  14. {{ article.publicAccountArticle?.description }}
  15. </div>
  16. </div>
  17. </div>
  18. </template>
  19. <script>
  20. import { useRouter } from 'vue-router';
  21. import axios from 'axios';
  22. import { inject, defineComponent, ref, onMounted, onUnmounted, watch } from 'vue';
  23. export default defineComponent({
  24. props: {
  25. selectChatUserRef: Object
  26. },
  27. setup(props) {
  28. const router = useRouter();
  29. const selectChatUser = ref(props.selectChatUserRef);
  30. const socket = ref(null);
  31. const messageQueue = ref([]); // 存储未发送的消息队列
  32. const isConnected = ref(false);
  33. const loginUser = ref(inject('loginUser'));
  34. const reconnectInterval = ref(5000); // 重连间隔时间,单位:毫秒
  35. const heartbeatInterval = ref(30000); // 心跳间隔时间,单位:毫秒
  36. const articleArr = ref([]);
  37. const loadChatMessages = (chatUserId) => {
  38. // 先清空
  39. axios.get(`/client-service/chat/message/list/`+chatUserId, {})
  40. .then(function (response) {
  41. if (response.data.code === 40001) {
  42. // 未登录
  43. window.location.href = response.data.redirect_url;
  44. } else if (response.data.code === 200) {
  45. if (response.data.data.length > 0) {
  46. articleArr.value = response.data.data;
  47. }
  48. }
  49. }).catch(function (error) {
  50. // 处理错误
  51. console.error(error);
  52. });
  53. }
  54. loadChatMessages( selectChatUser.value.userId);
  55. const viewArticle = (articleId) => {
  56. // 使用 router.resolve 生成完整的 URL
  57. const routeData = router.resolve({
  58. name: 'article',
  59. query: { id: articleId }
  60. });
  61. // 使用 window.open 在新窗口中打开链接
  62. window.open(routeData.href, '_blank');
  63. }
  64. const connectWebSocket = () => {
  65. socket.value = new WebSocket(`/connect-service/ws/chat/PC_WEB_CHAT/` + loginUser.value.userId);
  66. socket.value.onopen = () => {
  67. console.log('WebSocket 已连接');
  68. isConnected.value = true;
  69. // 发送队列中的消息
  70. messageQueue.value.forEach(message => socket.value.send(message));
  71. messageQueue.value = [];
  72. startHeartbeat();
  73. };
  74. socket.value.onmessage = (event) => {
  75. console.log('收到WS消息:', event.data);
  76. // 在此处理收到的消息
  77. const socketMessage = JSON.parse(event.data);
  78. if(socketMessage.senderId != selectChatUser.value.userId) {
  79. return;
  80. }
  81. const messageType = socketMessage.type;
  82. if (messageType === 4) {
  83. // 处理对话
  84. articleArr.value.push(socketMessage);
  85. }
  86. };
  87. socket.value.onerror = (error) => {
  88. console.error('WebSocket 错误:', error);
  89. isConnected.value = false;
  90. // 关闭当前连接
  91. socket.value.close();
  92. // 尝试重连
  93. setTimeout(connectWebSocket, reconnectInterval.value);
  94. };
  95. socket.value.onclose = () => {
  96. console.log('WebSocket 已关闭');
  97. isConnected.value = false;
  98. // 尝试重连
  99. setTimeout(connectWebSocket, reconnectInterval.value);
  100. };
  101. };
  102. const startHeartbeat = () => {
  103. const heartbeat = () => {
  104. if (isConnected.value) {
  105. socket.value.send('ping'); // 发送心跳包
  106. }
  107. };
  108. setInterval(heartbeat, heartbeatInterval.value);
  109. };
  110. // 链接ws
  111. connectWebSocket();
  112. const sendMessage = (message) => {
  113. if (isConnected.value) {
  114. socket.value.send(message);
  115. } else {
  116. messageQueue.value.push(message);
  117. }
  118. };
  119. // 切换对话用户,父组件传递选中用户信息,监听选中用户变化
  120. watch(() => props.selectChatUserRef, (newValue) => {
  121. selectChatUser.value = newValue;
  122. });
  123. onMounted(() => {
  124. });
  125. onUnmounted(() => {
  126. });
  127. return {
  128. selectChatUser,
  129. isConnected,
  130. messageQueue,
  131. loginUser,
  132. reconnectInterval,
  133. heartbeatInterval,
  134. articleArr,
  135. connectWebSocket,
  136. sendMessage,
  137. startHeartbeat,
  138. loadChatMessages,
  139. viewArticle
  140. };
  141. }
  142. });
  143. </script>
  144. <style scoped>
  145. .article-card {
  146. position: relative;
  147. width: 360px;
  148. min-height: 250px;
  149. height: auto;
  150. background-color: white;
  151. border-radius: 5px;
  152. overflow: hidden;
  153. }
  154. .article-card:hover {
  155. cursor: pointer;
  156. }
  157. .chat-core-container {
  158. justify-items: center;
  159. overflow-x: hidden;
  160. overflow-y: scroll;
  161. }
  162. .article-card {
  163. margin-top: 0px;
  164. margin-bottom: 40px;
  165. }
  166. .article-card-cover {
  167. position: relative;
  168. left: 0px;
  169. top: 0px;
  170. width: 100%;
  171. height: 140px;
  172. background-color: black;
  173. overflow: hidden;
  174. }
  175. .article-card-cover img {
  176. width: 100%;
  177. }
  178. .article-card-title {
  179. line-height: 50px;
  180. font-size: 15px;
  181. text-align: left;
  182. display: -webkit-box;
  183. -webkit-box-orient: vertical;
  184. -webkit-line-clamp: 1;
  185. overflow: hidden;
  186. padding: 0px 15px;
  187. }
  188. .article-card-description {
  189. line-height: 22px;
  190. font-size: 13px;
  191. text-align: left;
  192. display: -webkit-box;
  193. -webkit-box-orient: vertical;
  194. -webkit-line-clamp: 2;
  195. overflow: hidden;
  196. padding: 0px 15px;
  197. margin-top: -5px;
  198. color: #8d8787;
  199. }
  200. .chat-core-container::-webkit-scrollbar {/*滚动条整体样式*/
  201. width: 0px; /*高宽分别对应横竖滚动条的尺寸*/
  202. height: 0px;
  203. }
  204. .chat-core-container::-webkit-scrollbar-thumb {/*滚动条里面小方块*/
  205. border-radius: 10px;
  206. background-color: transparent;
  207. 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);
  208. }
  209. .chat-core-container::-webkit-scrollbar-track {/*滚动条里面轨道*/
  210. -webkit-box-shadow: inset 0 0 1px rgba(0,0,0,0.2);
  211. /*border-radius: 10px;*/
  212. background: #EDEDED;
  213. }
  214. </style>