diff --git a/mining-pool/.env.development b/mining-pool/.env.development index 9f360fd..40adac6 100644 --- a/mining-pool/.env.development +++ b/mining-pool/.env.development @@ -5,8 +5,8 @@ VUE_APP_TITLE = m2pool ENV = 'development' #开发环境 -VUE_APP_BASE_API = 'https://test.m2pool.com/api/' -# VUE_APP_BASE_API = 'http://10.168.2.150:8101/' +# VUE_APP_BASE_API = 'https://test.m2pool.com/api/' + VUE_APP_BASE_API = 'http://10.168.2.150:8101/' VUE_APP_BASE_URL = 'https://test.m2pool.com/' # 路由懒加载 VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/mining-pool/src/api/customerService.js b/mining-pool/src/api/customerService.js index 9ff785d..6558277 100644 --- a/mining-pool/src/api/customerService.js +++ b/mining-pool/src/api/customerService.js @@ -58,11 +58,11 @@ export function getUpdateRoom(data) { //图根据当前用户邮箱查询聊天室id -export function getUserid() { +export function getUserid(data) { return request({ url: `chat/rooms/find/room/by/userid`, - method: 'get', - + method: 'post', + data }) } diff --git a/mining-pool/src/components/ChatWidget.vue b/mining-pool/src/components/ChatWidget.vue index 3f7d644..e2f8517 100644 --- a/mining-pool/src/components/ChatWidget.vue +++ b/mining-pool/src/components/ChatWidget.vue @@ -220,10 +220,16 @@ export default { cachedMessages: {}, // 缓存各聊天室的消息 isMinimized: false, // 区分最小化和关闭状态 + + reconnectAttempts: 0, + maxReconnectAttempts: 5, + reconnectInterval: 5000, // 5秒 + isReconnecting: false, }; }, - + async created() { + this.determineUserType(); // 页面加载时立即获取用户信息 await this.initChatSystem(); }, @@ -247,7 +253,7 @@ export default { async initChatSystem() { try { // 获取用户ID和未读消息数 - const userData = await this.fetchUserid(); + const userData = await this.fetchUserid({ email: this.userEmail }); if (userData) { this.roomId = userData.id; this.receivingEmail = userData.userEmail; @@ -273,7 +279,7 @@ export default { determineUserType() { try { const token = localStorage.getItem("token"); - + console.log("token", token); if (!token) { // 游客身份 this.userType = 0; @@ -293,11 +299,13 @@ export default { if (userInfo.roleKey === "customer_service") { // 客服用户 this.userType = 2; + this.userEmail = ""; } else { // 登录用户 this.userType = 1; + this.userEmail = email; } - this.userEmail = email; + } catch (parseError) { console.error("解析用户信息失败:", parseError); // 解析失败时默认为游客 @@ -317,68 +325,124 @@ export default { }, // 添加订阅消息的方法 - subscribeToPersonalMessages() { - if (!this.stompClient || !this.isWebSocketConnected) return; + subscribeToPersonalMessages() { + if (!this.stompClient || !this.isWebSocketConnected) return; - try { - // 订阅个人消息频道 - this.stompClient.subscribe( - `/user/queue/${this.userEmail}`, - this.onMessageReceived, - { - id: `chat_${this.userEmail}`, - } - ); - - console.log("成功订阅消息频道:", `/user/queue/${this.userEmail}`); - } catch (error) { - console.error("订阅消息失败:", error); - this.$message.error("消息订阅失败,可能无法接收新消息"); - } - }, + try { + // 订阅个人消息频道 + this.stompClient.subscribe( + `/sub/queue/user/${this.userEmail}`, + this.onMessageReceived, + // { + // id: `chat_${this.userEmail}`, + // } + ); + console.log("成功订阅消息频道:", `/sub/queue/user/${this.userEmail}`); + } catch (error) { + console.error("订阅消息失败:", error); + this.$message.error("消息订阅失败,可能无法接收新消息"); + } + }, // 连接 WebSocket connectWebSocket() { - if (this.isWebSocketConnected) return; + if (this.isWebSocketConnected || this.isReconnecting) return; - this.connectionStatus = "connecting"; - try { - const wsUrl = `${process.env.VUE_APP_BASE_API}chat/ws`; - this.stompClient = Stomp.client(wsUrl); + this.connectionStatus = "connecting"; + this.isReconnecting = true; - const headers = { - email: this.userEmail, - type: this.userType, - }; + try { + const wsUrl = `${process.env.VUE_APP_BASE_API}chat/ws`; + this.stompClient = Stomp.client(wsUrl); + // 配置 STOMP 客户端参数 + this.stompClient.splitLargeFrames = true; // 启用大型消息帧分割 + // 添加详细的调试日志 + // this.stompClient.debug = (str) => { + // console.log("STOMP Debug:", str); + // // 记录连接状态变化 + // if (str.includes("CONNECTED")) { + // console.log("WebSocket 连接成功"); + // this.isWebSocketConnected = true; + // this.connectionStatus = "connected"; + // this.reconnectAttempts = 0; + // this.isReconnecting = false; + // } else if ( + // str.includes("DISCONNECTED") || + // str.includes("Connection closed") + // ) { + // console.log("WebSocket 连接断开"); + // this.handleDisconnect(); + // } + // }; - this.stompClient.connect( - headers, - (frame) => { - console.log("WebSocket Connected:", frame); - this.isWebSocketConnected = true; - this.connectionStatus = "connected"; - - // 连接成功后立即订阅消息 - this.subscribeToPersonalMessages(); - }, - (error) => { - console.error("WebSocket Error:", error); - this.isWebSocketConnected = false; - this.connectionStatus = "error"; - // 添加重连逻辑 - setTimeout(() => this.connectWebSocket(), 5000); - } - ); + const headers = { + email: this.userEmail, + type: this.userType, + }; - // 配置心跳 - this.stompClient.heartbeat.outgoing = 20000; - this.stompClient.heartbeat.incoming = 20000; - } catch (error) { - console.error("初始化 WebSocket 失败:", error); + // 添加连接状态监听 + this.stompClient.onStompError = (frame) => { + console.error("STOMP 错误:", frame); + this.handleDisconnect(); + }; + + this.stompClient.connect( + headers, + (frame) => { + console.log("WebSocket Connected:", frame); + this.isWebSocketConnected = true; + this.connectionStatus = "connected"; + this.reconnectAttempts = 0; + this.isReconnecting = false; + + // 连接成功后立即订阅消息 + this.subscribeToPersonalMessages(); + + // 显示连接成功提示 + this.$message.success("连接成功"); + }, + (error) => { + console.error("WebSocket Error:", error); + this.handleDisconnect(); + } + ); + + // 配置心跳 + this.stompClient.heartbeat.outgoing = 20000; + this.stompClient.heartbeat.incoming = 20000; + } catch (error) { + console.error("初始化 WebSocket 失败:", error); + this.handleDisconnect(); + } + }, + // 添加新重连最多重连5次 + handleDisconnect() { + this.isWebSocketConnected = false; this.connectionStatus = "error"; - } - }, + this.isReconnecting = false; + + // 如果重连次数未超过最大尝试次数,则尝试重连 + if (this.reconnectAttempts < this.maxReconnectAttempts) { + this.reconnectAttempts++; + console.log( + `尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...` + ); + + this.$message.warning( + `连接断开,${this.reconnectInterval / 1000}秒后重试...` + ); + + setTimeout(() => { + if (!this.isWebSocketConnected) { + this.connectWebSocket(); + } + }, this.reconnectInterval); + } else { + console.log("达到最大重连次数,停止重连"); + this.$message.error("连接失败,请刷新页面重试"); + } + }, // 新增:页面卸载时的处理 handleBeforeUnload() { @@ -387,73 +451,93 @@ export default { // 发送消息 sendMessage() { - if (!this.inputMessage.trim() || this.connectionStatus !== "connected") - return; + if (!this.inputMessage.trim()) return; + + // 检查 WebSocket 连接状态 + if (!this.stompClient || !this.stompClient.connected) { + console.log('发送消息时连接已断开,尝试重连...'); + this.$message.warning('连接已断开,正在重新连接...'); + this.handleDisconnect(); + return; + } const messageText = this.inputMessage.trim(); - // 添加用户消息到界面 - this.messages.push({ - type: "user", - text: messageText, - time: new Date(), - email: this.receivingEmail, - receiveUserType: 2, //接收消息用户类型 - roomId: this.roomId, - isRead: false, // 新发送的消息默认未读 - }); - const message = { - content: messageText, - type: 1, // 1 表示文字消息 - email: this.receivingEmail, - receiveUserType: 2, - roomId: this.roomId, - }; - // 发送消息 - this.stompClient.send("/point/send/message", {}, JSON.stringify(message)); - // 通过 WebSocket 发送消息 - // if (this.stompClient && this.stompClient.connected) { - // this.stompClient.send({ - // destination: "/point/send/message", - // body: JSON.stringify({ - // content: messageText, - // type: 1, - // email: this.receivingEmail, - // receiveUserType:2,//当前用户类型 - // roomId: this.roomId, - // }), - // }); - // } else { - // this.handleAutoResponse(messageText); - // } + try { + // 添加用户消息到界面 + this.messages.push({ + type: "user", + text: messageText, + time: new Date(), + email: this.receivingEmail, + receiveUserType: 2, //接收消息用户类型 + roomId: this.roomId, + isRead: false, // 新发送的消息默认未读 + }); + const message = { + content: messageText, + type: 1, // 1 表示文字消息 + email: this.receivingEmail, + receiveUserType: 2, + roomId: this.roomId, + }; + // 发送消息 + this.stompClient.send( + "/point/send/message/to/customer", + {}, + JSON.stringify(message) + ); - this.inputMessage = ""; + // 通过 WebSocket 发送消息 + // if (this.stompClient && this.stompClient.connected) { + // this.stompClient.send({ + // destination: "/point/send/message", + // body: JSON.stringify({ + // content: messageText, + // type: 1, + // email: this.receivingEmail, + // receiveUserType:2,//当前用户类型 + // roomId: this.roomId, + // }), + // }); + // } else { + // this.handleAutoResponse(messageText); + // } - this.$nextTick(() => { - this.scrollToBottom(); - }); + this.inputMessage = ""; + + this.$nextTick(() => { + this.scrollToBottom(); + }); + } catch (error) { + console.error("发送消息失败:", error); + this.$message.error("发送消息失败,请重试"); + this.handleDisconnect(); + } }, // 断开 WebSocket 连接 disconnectWebSocket() { - if (this.stompClient) { - try { - // 取消所有订阅 - if (this.stompClient.subscriptions) { - Object.keys(this.stompClient.subscriptions).forEach(id => { - this.stompClient.unsubscribe(id); - }); + if (this.stompClient) { + try { + // 取消所有订阅 + if (this.stompClient.subscriptions) { + Object.keys(this.stompClient.subscriptions).forEach((id) => { + this.stompClient.unsubscribe(id); + }); + } + + // 断开连接 + this.stompClient.deactivate(); + this.isWebSocketConnected = false; + this.connectionStatus = "disconnected"; + this.reconnectAttempts = 0; + this.isReconnecting = false; + } catch (error) { + console.error("断开 WebSocket 连接失败:", error); } - - // 断开连接 - this.stompClient.deactivate(); - this.isWebSocketConnected = false; - this.connectionStatus = "disconnected"; - } catch (error) { - console.error("断开 WebSocket 连接失败:", error); } - } - }, + }, // 处理页面可见性变化 handleVisibilityChange() { // 当页面变为可见且聊天窗口已打开时,标记消息为已读 @@ -496,53 +580,61 @@ export default { this.isLoadingHistory = true; try { - const response = await getHistory7({ roomId: this.roomId, userType: this.userType }); + const response = await getHistory7({ + roomId: this.roomId, + userType: this.userType, + email: this.userEmail, + }); console.log("历史消息数据:", response); - + if (response?.code === 200 && Array.isArray(response.data)) { // 处理历史消息 - const historyMessages = response.data.map(msg => ({ - type: msg.isSelf === 1 ? "user" : "system", // 根据 isSelf 判断消息类型 + const historyMessages = response.data.map((msg) => ({ + type: msg.isSelf === 1 ? "user" : "system", text: msg.content, isImage: msg.type === 2, imageUrl: msg.type === 2 ? msg.content : null, - time: new Date(msg.createTime), // 使用 createTime 字段 + time: new Date(msg.createTime), id: msg.id, roomId: msg.roomId, sender: msg.sendEmail, isHistory: true, - isRead: true // 历史消息默认已读 + isRead: true, })); // 按时间顺序排序 - this.messages = historyMessages.sort((a, b) => - new Date(a.time) - new Date(b.time) + this.messages = historyMessages.sort( + (a, b) => new Date(a.time) - new Date(b.time) ); - // 滚动到底部 - this.$nextTick(() => { - this.scrollToBottom(); - }); + // 等待 DOM 更新和图片加载完成后再滚动 + await this.$nextTick(); + // 添加一个小延时确保所有内容都渲染完成 + setTimeout(() => { + this.scrollToBottom(true); // 传入 true 表示强制滚动 + }, 100); } else { - // 没有历史消息时显示提示 - this.messages = [{ - type: "system", - text: "暂无历史消息", - isSystemHint: true, - time: new Date() - }]; + this.messages = [ + { + type: "system", + text: "暂无历史消息", + isSystemHint: true, + time: new Date(), + }, + ]; } } catch (error) { console.error("加载历史消息失败:", error); this.$message.error("加载历史消息失败"); - // 显示错误提示 - this.messages = [{ - type: "system", - text: "加载历史消息失败,请重试", - isSystemHint: true, - time: new Date(), - isError: true - }]; + this.messages = [ + { + type: "system", + text: "加载历史消息失败,请重试", + isSystemHint: true, + time: new Date(), + isError: true, + }, + ]; } finally { this.isLoadingHistory = false; } @@ -550,105 +642,118 @@ export default { // 加载更多历史消息(超过7天的) async loadMoreHistory() { - if (this.isLoadingHistory || !this.roomId) return; + if (this.isLoadingHistory || !this.roomId) return; - this.isLoadingHistory = true; + this.isLoadingHistory = true; - try { - // 显示加载中提示 - const loadingMsg = { - type: "system", - text: "正在加载更多历史消息...", - isLoading: true, - time: new Date(), - }; - this.messages.unshift(loadingMsg); + try { + // 获取当前消息列表中最旧消息的 ID + const oldestMessage = this.messages.find(msg => !msg.isSystemHint && !msg.isLoading); + if (!oldestMessage || !oldestMessage.id) { + console.warn('没有找到有效的消息ID'); + this.hasMoreHistory = false; + return; + } - // 获取更早的聊天记录 - const response = await getHistory({ roomId: this.roomId }); + // 显示加载中提示 + const loadingMsg = { + type: "system", + text: "正在加载更多历史消息...", + isLoading: true, + time: new Date(), + }; + this.messages.unshift(loadingMsg); - // 移除加载中提示 - this.messages = this.messages.filter((msg) => !msg.isLoading); + // 获取更早的聊天记录,添加 id 参数 + const response = await getHistory7({ + roomId: this.roomId, + userType: this.userType, + email: this.userEmail, + id: oldestMessage.id // 添加最旧消息的 ID + }); - if ( - response && - response.code === 200 && - response.data && - response.data.length > 0 - ) { - // 处理并添加历史消息 - const historyMessages = this.formatHistoryMessages(response.data); + // 移除加载中提示 + this.messages = this.messages.filter((msg) => !msg.isLoading); - // 将历史消息添加到消息列表的前面 - this.messages = [...historyMessages, ...this.messages]; + if ( + response && + response.code === 200 && + response.data && + response.data.length > 0 + ) { + // 处理并添加历史消息 + const historyMessages = this.formatHistoryMessages(response.data); - // 如果没有数据返回,表示没有更多历史记录 - this.hasMoreHistory = historyMessages.length > 0; + // 将历史消息添加到消息列表的前面 + this.messages = [...historyMessages, ...this.messages]; - if (historyMessages.length === 0) { - this.messages.unshift({ - type: "system", - text: "没有更多历史消息了", - isSystemHint: true, - time: new Date(), - }); - } - } else { - this.hasMoreHistory = false; - this.messages.unshift({ - type: "system", - text: "没有更多历史消息了", - isSystemHint: true, - time: new Date(), - }); - } - } catch (error) { - console.error("加载更多历史消息失败:", error); + // 如果没有数据返回,表示没有更多历史记录 + this.hasMoreHistory = historyMessages.length > 0; + + if (historyMessages.length === 0) { this.messages.unshift({ type: "system", - text: "加载更多历史消息失败", - isError: true, + text: "没有更多历史消息了", + isSystemHint: true, time: new Date(), }); - } finally { - this.isLoadingHistory = false; } - }, + } else { + this.hasMoreHistory = false; + this.messages.unshift({ + type: "system", + text: "没有更多历史消息了", + isSystemHint: true, + time: new Date(), + }); + } + } catch (error) { + console.error("加载更多历史消息失败:", error); + this.messages.unshift({ + type: "system", + text: "加载更多历史消息失败", + isError: true, + time: new Date(), + }); + } finally { + this.isLoadingHistory = false; + } +}, // 格式化历史消息数据 formatHistoryMessages(messagesData) { - if (!messagesData || !Array.isArray(messagesData)) return []; + if (!messagesData || !Array.isArray(messagesData)) return []; - return messagesData - .map((msg) => ({ - type: msg.isSelf === 1 ? "user" : "system", // 根据 isSelf 判断消息类型 - text: msg.content || "", - isImage: msg.type === 2, - imageUrl: msg.type === 2 ? msg.content : null, - time: new Date(msg.createTime), - id: msg.id, - roomId: msg.roomId, - sender: msg.sendEmail, - isHistory: true, - isRead: true - })) - .sort((a, b) => new Date(a.time) - new Date(b.time)); -}, + return messagesData + .map((msg) => ({ + type: msg.isSelf === 1 ? "user" : "system", // 根据 isSelf 判断消息类型 + text: msg.content || "", + isImage: msg.type === 2, + imageUrl: msg.type === 2 ? msg.content : null, + time: new Date(msg.createTime), + id: msg.id, + roomId: msg.roomId, + sender: msg.sendEmail, + isHistory: true, + isRead: true, + })) + .sort((a, b) => new Date(a.time) - new Date(b.time)); + }, // 修改 fetchUserid 方法,添加 token 检查 - async fetchUserid() { + async fetchUserid(params) { try { // 先检查是否有 token - const token = localStorage.getItem("token"); - if (!token) { - console.log("用户未登录,不发起 getUserid 请求"); - // 对于未登录用户,可以生成一个临时 ID - this.roomId = `guest_${Date.now()}`; - this.receivingEmail = "customer_service@example.com"; // 或默认客服邮箱 - return null; - } + // const token = localStorage.getItem("token"); + // if (!token) { + // console.log("用户未登录,不发起 getUserid 请求"); + // // 对于未登录用户,可以生成一个临时 ID + // this.roomId = `guest_${Date.now()}`; + // this.receivingEmail = "customer_service@example.com"; // 或默认客服邮箱 + // return null; + // } - const res = await getUserid(); + const res = await getUserid(params); if (res && res.code == 200) { console.log("获取用户ID成功:", res); this.receivingEmail = res.data.userEmail; @@ -685,50 +790,49 @@ export default { // 接收消息处理 onMessageReceived(message) { - try { - const data = JSON.parse(message.body); - console.log("收到新消息:", data); - - // 构造消息对象 - const messageObj = { - type: data.sendUserType === this.userType ? "user" : "system", // 用户类型判断 - text: data.content, - isImage: data.type === 2, - imageUrl: data.type === 2 ? data.content : null, - time: new Date(data.sendTime), - id: data.id, - roomId: data.roomId, - sender: data.sendEmail, - isRead: false - }; + try { + const data = JSON.parse(message.body); + console.log("收到新消息:", data); - // 直接添加到消息列表 - this.messages.push(messageObj); + // 构造消息对象 + const messageObj = { + type: data.sendUserType === this.userType ? "user" : "system", // 用户类型判断 + text: data.content, + isImage: data.type === 2, + imageUrl: data.type === 2 ? data.content : null, + time: new Date(data.sendTime), + id: data.id, + roomId: data.roomId, + sender: data.sendEmail, + isRead: false, + }; - // 如果聊天窗口未打开,增加未读消息数 - if (!this.isChatOpen) { - // 使用服务器返回的未读数,如果没有则增加1 - if (data.clientReadNum !== undefined) { - this.unreadMessages = data.clientReadNum; - } else { - this.unreadMessages++; + // 直接添加到消息列表 + this.messages.push(messageObj); + + // 如果聊天窗口未打开,增加未读消息数 + if (!this.isChatOpen) { + // 使用服务器返回的未读数,如果没有则增加1 + if (data.clientReadNum !== undefined) { + this.unreadMessages = data.clientReadNum; + } else { + this.unreadMessages++; + } + // 显示消息通知 + this.showNotification(messageObj); + } else { + // 如果聊天窗口已打开,立即标记为已读 + this.markMessagesAsRead(); + } + + // 滚动到底部 + this.$nextTick(() => { + this.scrollToBottom(); + }); + } catch (error) { + console.error("处理消息失败:", error); } - // 显示消息通知 - this.showNotification(messageObj); - } else { - // 如果聊天窗口已打开,立即标记为已读 - this.markMessagesAsRead(); - } - - // 滚动到底部 - this.$nextTick(() => { - this.scrollToBottom(); - }); - - } catch (error) { - console.error("处理消息失败:", error); - } -}, + }, // 显示消息通知 showNotification(message) { @@ -778,35 +882,46 @@ export default { } }, - // 切换聊天窗口 + // 打开聊天框 async toggleChat() { this.isChatOpen = !this.isChatOpen; + // 1. 判别身份 + this.determineUserType(); + + // 2. 如果是客服,跳转到客服页面 + if (this.userType === 2) { + const lang = this.$i18n.locale; + this.$router.push(`/${lang}/customerService`); + return; + } + if (this.isChatOpen) { - // 打开聊天窗口时 try { // 确定用户类型 this.determineUserType(); - // 如果未连接,则初始化 WebSocket - if (this.connectionStatus === "disconnected") { - await this.initWebSocket(); + // 如果未连接或连接断开,则重新初始化 WebSocket + if (!this.isWebSocketConnected || this.connectionStatus === "disconnected") { + await this.connectWebSocket(); } // 如果消息列表为空,加载历史消息 if (this.messages.length === 0) { await this.loadHistoryMessages(); + } else { + // 如果已有消息,确保滚动到底部 + await this.$nextTick(); + setTimeout(() => { + this.scrollToBottom(true); + }, 100); } // 标记消息为已读 await this.markMessagesAsRead(); - - // 滚动到底部 - this.$nextTick(() => { - this.scrollToBottom(); - }); } catch (error) { console.error("初始化聊天失败:", error); + this.$message.error("初始化聊天失败,请重试"); } } }, @@ -879,22 +994,22 @@ export default { this.loadMoreHistory(); } }, + //滚动到底部 + scrollToBottom(force = false) { + if (!this.$refs.chatBody) return; - scrollToBottom() { - if (this.$refs.chatBody) { - const scrollOptions = { - top: this.$refs.chatBody.scrollHeight, - behavior: "smooth", - }; + const scrollOptions = { + top: this.$refs.chatBody.scrollHeight, + behavior: force ? 'auto' : 'smooth' // 强制滚动时使用 'auto' + }; - try { - this.$refs.chatBody.scrollTo(scrollOptions); - } catch (error) { - // 如果平滑滚动不支持,则直接设置 - this.$refs.chatBody.scrollTop = this.$refs.chatBody.scrollHeight; - } - } - }, + try { + this.$refs.chatBody.scrollTo(scrollOptions); + } catch (error) { + // 如果平滑滚动不支持,则直接设置 + this.$refs.chatBody.scrollTop = this.$refs.chatBody.scrollHeight; + } +}, formatTime(date) { if (!date || !(date instanceof Date) || isNaN(date.getTime())) { @@ -950,7 +1065,10 @@ export default { // 处理图片上传 async handleImageUpload(event) { - if (this.connectionStatus !== "connected") return; + if (this.connectionStatus !== "connected") { + console.log("当前连接状态:", this.connectionStatus); + return; + } const file = event.target.files[0]; if (!file) return; @@ -981,62 +1099,71 @@ export default { type: "info", }); - // 准备FormData - const formData = new FormData(); - formData.append("file", file); + // 将文件转换为 base64 + const reader = new FileReader(); + reader.onload = (e) => { + const base64Image = e.target.result; - // 上传文件到后端 - const response = await getFileUpdate(formData); - console.log("文件上传返回:", response); - - // 检查上传结果 - if (response && response.code === 200 && response.data) { - // 从后端响应中获取图片信息 - const imageData = response.data; - // 使用后端返回的URL - const imageUrl = this.formatImageUrl(imageData.url); - - console.log("图片URL:", imageUrl); // 调试用:打印URL + // 检查连接状态 + if (!this.stompClient || !this.stompClient.connected) { + console.error("发送图片时连接已断开"); + this.$message.error("连接已断开,正在重新连接..."); + this.connectWebSocket(); + return; + } // 添加用户图片消息到界面(本地显示) this.messages.push({ type: "user", - text: "", // 保留空字符串 + text: "", isImage: true, - imageUrl: imageUrl, // 确保URL正确 + imageUrl: base64Image, time: new Date(), email: this.receivingEmail, sendUserType: this.userType, roomId: this.roomId, isRead: false, }); - // 通过WebSocket发送图片消息 - if (this.stompClient && this.stompClient.connected) { + + try { + // 通过 WebSocket 发送图片消息 const message = { - content: imageUrl, // URL作为消息内容 - type: 2, // 使用数字类型2表示图片消息 + content: base64Image, + type: 2, email: this.receivingEmail, receiveUserType: 2, roomId: this.roomId, }; - // 使用WebSocket发送消息 + console.log( + "准备发送图片消息,当前连接状态:", + this.stompClient.connected + ); this.stompClient.send( "/point/send/message", {}, JSON.stringify(message) ); - } + console.log("图片消息发送完成"); - this.$nextTick(() => { - this.scrollToBottom(); - }); - } else { - this.$message.error("图片上传失败: " + (response?.msg || "未知错误")); - } + this.$nextTick(() => { + this.scrollToBottom(); + }); + } catch (sendError) { + console.error("发送图片消息失败:", sendError); + this.$message.error("发送图片失败,请重试"); + } + }; + + reader.onerror = (error) => { + console.error("读取文件失败:", error); + this.$message.error("读取图片失败,请重试"); + }; + + reader.readAsDataURL(file); } catch (error) { - console.error("图片上传异常:", error); - this.$message.error("图片上传失败,请重试"); + console.error("图片处理失败:", error); + this.$message.error("图片处理失败,请重试"); } finally { // 清空input,允许重复选择同一文件 this.$refs.imageUpload.value = ""; @@ -1070,7 +1197,7 @@ export default { }, beforeDestroy() { - this.disconnectWebSocket(); + // 移除滚动监听 if (this.$refs.chatBody) { @@ -1081,7 +1208,11 @@ export default { "visibilitychange", this.handleVisibilityChange ); - + // 确保在销毁时断开连接 + if (this.stompClient) { + this.stompClient.disconnect(); + this.stompClient = null; + } // 断开 WebSocket 连接 this.disconnectWebSocket(); }, diff --git a/mining-pool/src/views/customerService/index.vue b/mining-pool/src/views/customerService/index.vue index 2558b64..73424f6 100644 --- a/mining-pool/src/views/customerService/index.vue +++ b/mining-pool/src/views/customerService/index.vue @@ -202,7 +202,7 @@ v-model="inputMessage" :rows="3" :disabled="!currentContact" - placeholder="请输入消息,按Enter键发送,按Ctrl+Enter键换行" + placeholder="请输入消息,按Enter键发送,按Ctrl+Enter键换行" @keydown.enter.native="handleKeyDown" > @@ -213,7 +213,6 @@ type="primary" :disabled="!currentContact || !inputMessage.trim() || sending" @click="sendMessage" - > 发送 @@ -270,18 +269,22 @@ export default { userScrolled: false, // 新增:用户是否手动滚动过 history7Params: { //7天历史消息参数 - id: "", //最后一条消息id + id: "", //最后一条消息id roomId: "", //聊天室id userType: 2, //用户类型 + email: "497681109@qq.com", //客服邮箱 }, historyAllParams: { //7天以前的历史消息 - id: "", //最后一条消息id + id: "", //最后一条消息id roomId: "", //聊天室id userType: 2, //用户类型 }, receiveUserType: "", //接收者类型 manualCreatedRooms: [], //手动创建的聊天室 + chatRooms: [], // 初始化聊天室列表数组 + isWebSocketConnected: false, + connectionStatus: "disconnected", }; }, computed: { @@ -307,18 +310,23 @@ export default { }, async created() { - // 初始化 WebSocket 连接 - this.initWebSocket(); + try { + // 初始化用户信息 + // this.determineUserType(); - // 获取聊天室列表 - await this.fetchRoomList(); - - // 默认选择第一个联系人 - if (this.contacts.length > 0) { - this.selectContact(this.contacts[0].roomId); + // 获取聊天室列表 + await this.fetchRoomList(); + // 默认选择第一个联系人 + if (this.contacts.length > 0) { + this.selectContact(this.contacts[0].roomId); + } + // 在组件创建时加载手动创建的聊天室 + this.loadManualCreatedRooms(); + // 初始化 WebSocket 连接 + this.initWebSocket(); + } catch (error) { + console.error("初始化失败:", error); } - // 在组件创建时加载手动创建的聊天室 - this.loadManualCreatedRooms(); }, async mounted() { // 获取聊天室列表 @@ -328,17 +336,14 @@ export default { if (this.contacts.length > 0) { this.selectContact(this.contacts[0].roomId); } - this.confirmUserType(); //确认用户类型 - // 定时刷新聊天室列表,例如每30秒刷新一次 - // this.roomListInterval = setInterval(() => { - // this.fetchRoomList(); - - // // 如果有选中联系人,刷新消息 - // if (this.currentContactId) { - // this.loadMessages(this.currentContactId); - // } - // }, 30000); + + let userEmail = localStorage.getItem("userEmail"); + this.userEmail = JSON.parse(userEmail); + window.addEventListener("setItem", () => { + let userEmail = localStorage.getItem("userEmail"); + this.userEmail = JSON.parse(userEmail); + }); // 确保初始加载后滚动到底部 this.$nextTick(() => { this.scrollToBottom(); @@ -356,7 +361,7 @@ export default { methods: { handleKeyDown(e) { console.log("e:hhhhshshsshshshhh好的好的和", e); - // 如果按住了 Ctrl 键,则不发送消息(换行) + // 如果按住了 Ctrl 键,则不发送消息(换行) if (e.ctrlKey) { return; } @@ -365,93 +370,120 @@ export default { // 发送消息 this.sendMessage(); }, - confirmUserType() { - try { - const token = localStorage.getItem("token"); - - if (!token) { - // 游客身份 - this.userType = 0; - - - this.userEmail = `guest_${Date.now()}_${Math.random() - .toString(36) - .substr(2, 9)}`; - return; - } - - try { - const userInfo = JSON.parse( - localStorage.getItem("jurisdiction") || "{}" - ); - const email = JSON.parse(localStorage.getItem("userEmail") || "{}"); - - if (userInfo.roleKey === "customer_service") { - // 客服用户 - this.userType = 2; - - - } else { - // 登录用户 - this.userType = 1; - - - } - this.userEmail = email; - } catch (parseError) { - console.error("解析用户信息失败:", parseError); - // 解析失败时默认为游客 - this.userType = 0; - this.userEmail = `guest_${Date.now()}_${Math.random() - .toString(36) - .substr(2, 9)}`; - } - } catch (error) { - console.error("获取用户信息失败:", error); - // 出错时默认为游客 - this.userType = 0; - this.userEmail = `guest_${Date.now()}_${Math.random() - .toString(36) - .substr(2, 9)}`; - } - }, // 初始化 WebSocket 连接 initWebSocket() { - const wsUrl = `${process.env.VUE_APP_BASE_API}chat/ws`; + if (this.isWebSocketConnected) return; - this.stompClient = Stomp.client(wsUrl); + try { + const wsUrl = `${process.env.VUE_APP_BASE_API}chat/ws`; + this.stompClient = Stomp.client(wsUrl); - // 配置连接参数 - const headers = { - email: this.userEmail, - type: 2, - }; - // 连接并传入参数 - this.stompClient.connect( - headers, // 连接参数 - (frame) => { - // 连接成功回调 - console.log("WebSocket Connected:", frame); - this.wsConnected = true; - this.subscribeToPersonalMessages(); - }, - (error) => { - // 连接错误回调 - console.error("WebSocket Error:", error); - this.wsConnected = false; - this.$message.error("连接失败,正在重试..."); + // 配置 STOMP 客户端参数 + this.stompClient.splitLargeFrames = true; // 启用大型消息帧分割 + + // 修改调试日志的方式 + this.stompClient.debug = (str) => { + // 只打印与客服相关的日志 + if ( + str.includes("CONNECTED") || + str.includes("DISCONNECTED") || + str.includes("ERROR") + ) { + console.log("[客服系统]", str); + } + }; + + const headers = { + email: this.userEmail, + type: this.userType, + }; + + this.stompClient.connect( + headers, + (frame) => { + console.log("[客服系统] WebSocket 连接成功", frame); + this.isWebSocketConnected = true; + this.connectionStatus = "connected"; + this.subscribeToMessages(); + }, + (error) => { + console.error("[客服系统] WebSocket 错误:", error); + this.handleDisconnect(); + } + ); + + // 配置心跳 + this.stompClient.heartbeat.outgoing = 20000; + this.stompClient.heartbeat.incoming = 20000; + } catch (error) { + console.error("初始化 CustomerService WebSocket 失败:", error); + this.handleDisconnect(); + } + }, + + // // 订阅消息 + subscribeToMessages() { + if (!this.stompClient || !this.isWebSocketConnected) return; + + try { + // 修改订阅路径,使用客服特定的订阅路径 + this.stompClient.subscribe( + `/sub/queue/customer/${this.userEmail}`, + this.handleIncomingMessage, + { + id: `customer_${this.userEmail}`, + } + ); + // this.stompClient.subscribe( + // `/user/queue/customer_service`, // 修改为客服专用的订阅路径 + // this.handleIncomingMessage, + // { + // id: `customer_service_${this.userEmail}`, + // } + // ); + + console.log( + "CustomerService 成功订阅消息频道:", + `/sub/queue/customer/${this.userEmail}` + ); + } catch (error) { + console.error("CustomerService 订阅消息失败:", error); + } + }, + + // 断开连接 + disconnectWebSocket() { + if (this.stompClient) { + try { + // 取消所有订阅 + if (this.stompClient.subscriptions) { + Object.keys(this.stompClient.subscriptions).forEach((id) => { + this.stompClient.unsubscribe(id); + }); + } + + // 断开连接 + this.stompClient.deactivate(); + this.isWebSocketConnected = false; + this.connectionStatus = "disconnected"; + } catch (error) { + console.error("断开 CustomerService WebSocket 连接失败:", error); } - ); + } + }, - // 可选:配置心跳 - this.stompClient.heartbeat.outgoing = 20000; // 20秒 - this.stompClient.heartbeat.incoming = 20000; + // 处理断开连接 + handleDisconnect() { + this.isWebSocketConnected = false; + this.connectionStatus = "error"; - // 可选:开启调试日志 - this.stompClient.debug = function (str) { - console.log("STOMP: " + str); - }; + // 尝试重新连接 + setTimeout(() => { + if (!this.isWebSocketConnected) { + this.initWebSocket(); + } + }, 5000); }, // 获取当前的 UTC 时间 @@ -489,12 +521,12 @@ export default { // 发送消息 this.stompClient.send( - "/point/send/message", + "/point/send/message/to/user", {}, JSON.stringify(message) ); - // 添加到本地消息列表 + // 添加到本地消息列表 this.addMessageToChat({ sender: "我", content: messageContent, @@ -502,7 +534,7 @@ export default { isSelf: true, isImage: false, roomId: this.currentContactId, - type: 1 + type: 1, }); // 重置当前聊天室的未读消息数 @@ -530,39 +562,92 @@ export default { }, // 处理接收到的消息 - handleIncomingMessage(message) { + async handleIncomingMessage(message) { + try { const msg = JSON.parse(message.body); - + console.log("客服收到的消息", msg); + const messageData = { id: msg.id, sender: msg.sendEmail, - avatar: msg.sendUserType === 2 ? "iconfont icon-icon28" : "iconfont icon-user", + avatar: + msg.sendUserType === 2 + ? "iconfont icon-icon28" + : "iconfont icon-user", content: msg.content, time: new Date(msg.sendTime), - isSelf: msg.sendUserType === this.userType && msg.sendEmail === this.userEmail, + isSelf: + msg.sendUserType === this.userType && + msg.sendEmail === this.userEmail, isImage: msg.type === 2, type: msg.type, roomId: msg.roomId, sendUserType: msg.sendUserType, isCreate: msg.isCreate, - clientReadNum: msg.clientReadNum + clientReadNum: msg.clientReadNum, }; - // 新聊天室处理 - if (messageData.isCreate) { - this.handleNewChatRoom(messageData); + // 更新或创建聊天室 + const existingContact = this.contacts.find( + (c) => c.roomId === messageData.roomId + ); + + if (!existingContact) { + // 如果聊天室不存在,创建新的聊天室 + const newContact = { + roomId: messageData.roomId, + name: messageData.sender, + lastMessage: messageData.isImage ? "[图片]" : messageData.content, + lastTime: messageData.time, + unread: 1, + important: false, + isGuest: messageData.sendUserType === 0, + sendUserType: messageData.sendUserType, + isManualCreated: true, + }; + + this.contacts.push(newContact); + this.$set(this.messages, messageData.roomId, []); + } else { + // 如果聊天室已存在,更新最后一条消息 + existingContact.lastMessage = messageData.isImage ? "[图片]" : messageData.content; + existingContact.lastTime = messageData.time; } - this.updateChatRoomList(messageData); + // 添加消息到聊天记录 + if (!this.messages[messageData.roomId]) { + this.$set(this.messages, messageData.roomId, []); + } - // 当前聊天室消息 + this.messages[messageData.roomId].push({ + id: messageData.id, + sender: messageData.sender, + avatar: messageData.avatar, + content: messageData.content, + time: messageData.time, + isSelf: messageData.isSelf, + isImage: messageData.isImage, + type: messageData.type, + roomId: messageData.roomId, + }); + + // 如果是当前选中的聊天室,标记为已读 if (messageData.roomId === this.currentContactId) { - this.addMessageToChat(messageData); this.markMessagesAsRead(messageData.roomId); } else { - this.incrementUnreadCount(messageData.roomId, messageData.clientReadNum); + // 更新未读消息数 + const contact = this.contacts.find((c) => c.roomId === messageData.roomId); + if (contact) { + contact.unread = (contact.unread || 0) + 1; + } } - }, + + // 重新排序联系人列表 + this.sortContacts(); + } catch (error) { + console.error("处理新消息失败:", error); + } +}, // 处理新聊天室创建 handleNewChatRoom(messageData) { @@ -570,24 +655,21 @@ export default { const existingContact = this.contacts.find( (c) => c.roomId === messageData.roomId ); - console.log("messageData:hhhhshshsshshshhh好的好的和", messageData); - - - // && messageData.isCreate && messageData.sender - if (!existingContact) {//只需要判断是否在联系人列表中存在 如果不在就创建新的聊天室对象 + if (!existingContact) { // 创建新的聊天室对象 const newContact = { roomId: messageData.roomId, name: messageData.sender, + // 修改这里:使用实际收到的消息内容作为最后一条消息 lastMessage: messageData.isImage ? "[图片]" : messageData.content, lastTime: messageData.time, unread: 1, important: false, isGuest: messageData.sendUserType === 0, sendUserType: messageData.sendUserType, - isManualCreated: true, // 新增:标记为手动创建 - clientReadNum: messageData.clientReadNum, //未读条数 + isManualCreated: true, + clientReadNum: messageData.clientReadNum, }; // 添加到聊天列表 @@ -616,6 +698,7 @@ export default { this.manualCreatedRooms.push(newContact); this.saveManualCreatedRooms(); + // 重新排序联系人列表 this.sortContacts(); } }, @@ -657,23 +740,53 @@ export default { console.error("加载手动创建的聊天室失败:", error); } }, + + // 添加新方法:创建新聊天室 + async createNewChatRoom(messageData) { + try { + // 调用后端 API 创建新的聊天室 + const response = await createChatRoom({ + userEmail: messageData.sender, + userType: messageData.sendUserType, + }); + + if (response && response.code === 200) { + const newRoom = { + userEmail: messageData.sender, + roomId: response.data.roomId, + lastMessage: messageData.content, + lastMessageTime: messageData.time, + unreadCount: messageData.clientReadNum || 0, + userType: messageData.sendUserType, + }; + + this.chatRooms.unshift(newRoom); + return newRoom; + } + } catch (error) { + console.error("创建新聊天室失败:", error); + throw error; + } + }, // 更新聊天室列表 updateChatRoomList(messageData) { - // 寻找现有联系人 - const contactIndex = this.contacts.findIndex( - (c) => c.roomId === messageData.roomId + const roomIndex = this.chatRooms.findIndex( + (room) => room.roomId === messageData.roomId ); - if (contactIndex !== -1) { - // 更新现有联系人的最后消息和时间 - const contact = this.contacts[contactIndex]; - contact.lastMessage = messageData.isImage - ? "[图片]" - : messageData.content; - contact.lastTime = messageData.time; + if (roomIndex !== -1) { + // 更新现有聊天室信息 + this.chatRooms[roomIndex] = { + ...this.chatRooms[roomIndex], + lastMessage: messageData.content, + lastMessageTime: messageData.time, + unreadCount: + messageData.clientReadNum || this.chatRooms[roomIndex].unreadCount, + }; - // 重新排序联系人列表,将最新消息的联系人移到前面 - this.sortContacts(); + // 将更新的聊天室移到列表顶部 + const updatedRoom = this.chatRooms.splice(roomIndex, 1)[0]; + this.chatRooms.unshift(updatedRoom); } }, // 修改标记为已读方法,添加参数支持 @@ -683,7 +796,7 @@ export default { try { const data = { roomId: roomId, - userType:2, + userType: 2, }; const response = await getReadMessage(data); @@ -717,69 +830,63 @@ export default { // 获取聊天室列表 async fetchRoomList() { - try { - this.loadingRooms = true; - const response = await getRoomList(); - if (response?.code === 200) { - // 创建全新的联系人数组 - const newContacts = response.rows.map((room) => { - // 查找旧联系人中是否存在相同roomId - const existingContact = this.contacts.find( - (c) => c.roomId === room.id - ); + try { + this.loadingRooms = true; + const response = await getRoomList(); + if (response?.code === 200) { + const newContacts = response.rows.map((room) => { + const existingContact = this.contacts.find( + (c) => c.roomId === room.id + ); - const isImportant = - room.flag === 1 - ? true - : room.flag === 0 - ? false - : room.important === 1 - ? true - : existingContact - ? existingContact.important - : false; + const manualRoom = this.manualCreatedRooms.find( + (c) => c.roomId === room.id + ); - return { - roomId: room.id, - name: room.userEmail || "未命名聊天室", - avatar: this.getDefaultAvatar(room.roomName || "未命名聊天室"), - lastMessage: room.lastMessage || "暂无消息", - lastTime: room.lastUserSendTime - ? new Date(room.lastUserSendTime) - : new Date(), - unread: existingContact?.unread ?? room.unreadCount ?? 0, - important: isImportant, - isManualCreated: existingContact?.isManualCreated || false, // 保持手动创建标记 - }; - }); + const isImportant = + room.flag === 1 + ? true + : room.flag === 0 + ? false + : room.important === 1 + ? true + : existingContact + ? existingContact.important + : false; - // 合并手动创建的聊天室 - this.manualCreatedRooms.forEach((room) => { - const exists = newContacts.find((c) => c.roomId === room.roomId); - if (!exists) { - newContacts.push({ - ...room, - lastTime: new Date(room.lastTime), - }); - } - }); + return { + roomId: room.id, + name: room.userEmail || "未命名聊天室", + avatar: this.getDefaultAvatar(room.roomName || "未命名聊天室"), + // 修改这里:优先使用服务器返回的消息,其次是现有联系人的消息 + lastMessage: room.lastMessage || (existingContact ? existingContact.lastMessage : "暂无消息"), + lastTime: room.lastUserSendTime + ? new Date(room.lastUserSendTime) + : new Date(), + unread: existingContact?.unread ?? room.unreadCount ?? 0, + important: isImportant, + isManualCreated: manualRoom ? true : false, + sendUserType: room.sendUserType, + isGuest: room.sendUserType === 0, + }; + }); - // 更新联系人列表 - this.contacts = newContacts; - this.sortContacts(); - } - } catch (error) { - console.error("获取聊天室列表异常:", error); - this.$message.error("获取聊天室列表异常"); - } finally { - this.loadingRooms = false; - } - }, + // 更新联系人列表 + this.contacts = newContacts; + this.sortContacts(); + } + } catch (error) { + console.error("获取聊天室列表异常:", error); + this.$message.error("获取聊天室列表异常"); + } finally { + this.loadingRooms = false; + } +}, // 加载更多历史消息 async loadMoreHistory() { if (!this.currentContactId) return; - + // 获取当前已加载的消息列表 const currentMsgs = this.messages[this.currentContactId] || []; @@ -789,7 +896,7 @@ export default { this.history7Params.id = lastMsg ? lastMsg.id : ""; // this.history7Params.pageNum += 1; // 递增页码 this.history7Params.roomId = this.currentContactId; - + this.history7Params.email = this.userEmail; try { this.messagesLoading = true; @@ -830,38 +937,37 @@ export default { // 选择联系人 async selectContact(roomId) { - if (this.currentContactId === roomId) return; - - try { - this.messagesLoading = true; // 显示加载状态 - this.currentContactId = roomId; - this.userViewHistory = false; - - // 重置分页参数 - this.history7Params = { - id: "", // 首次加载为空 - // pageNum: 1, // 首次页码为1 - roomId: roomId, - userType:2, - }; + if (this.currentContactId === roomId) return; - // 加载历史消息 - await this.loadMessages(roomId); - - // 标记消息为已读 - await this.markMessagesAsRead(roomId); + try { + this.messagesLoading = true; // 显示加载状态 + this.currentContactId = roomId; + this.userViewHistory = false; - } catch (error) { - console.error("选择联系人失败:", error); - this.$message.error("加载聊天记录失败"); - } finally { - this.messagesLoading = false; - // 滚动到底部 - this.$nextTick(() => { - this.scrollToBottom(); - }); - } -}, + // 重置分页参数 + this.history7Params = { + id: "", // 首次加载为空 + // pageNum: 1, // 首次页码为1 + roomId: roomId, + userType: 2, + }; + + // 加载历史消息 + await this.loadMessages(roomId); + + // 标记消息为已读 + await this.markMessagesAsRead(roomId); + } catch (error) { + console.error("选择联系人失败:", error); + this.$message.error("加载聊天记录失败"); + } finally { + this.messagesLoading = false; + // 滚动到底部 + this.$nextTick(() => { + this.scrollToBottom(); + }); + } + }, //判断是否在聊天框底部 isAtBottom() { const container = this.$refs.messageContainer; @@ -875,175 +981,192 @@ export default { // 加载聊天消息 async loadMessages(roomId) { - if (!roomId) return; + if (!roomId) return; + + try { + this.history7Params.email = this.userEmail; + this.history7Params.roomId = roomId; + const response = await getHistory7(this.history7Params); + + if (response?.code === 200 && response.data) { + // 处理消息数据 + let roomMessages = response.data + .filter((msg) => msg.roomId == roomId) + .map((msg) => ({ + id: msg.id, + sender: msg.isSelf === 1 ? "我" : msg.sendEmail || "未知发送者", + avatar: + msg.sendUserType == 2 + ? "iconfont icon-icon28" + : "iconfont icon-user", + content: msg.content, + time: new Date(msg.createTime), + isSelf: msg.isSelf === 1, + isImage: msg.type === 2, + isRead: msg.isRead === 1, + type: msg.type, + roomId: msg.roomId, + sendUserType: msg.sendUserType, + })); + + // 按时间排序 + roomMessages = roomMessages.sort((a, b) => a.time - b.time); + + // 更新消息列表 + this.$set(this.messages, roomId, roomMessages); + + // 更新联系人的未读状态 + const contact = this.contacts.find((c) => c.roomId === roomId); + if (contact) { + contact.unread = 0; + } + } else { + // 如果没有消息数据,初始化为空数组 + this.$set(this.messages, roomId, []); + if (response?.code !== 200) { + this.$message.warning("获取聊天记录失败"); + } + } + } catch (error) { + console.error("加载消息异常:", error); + this.$message.error("加载消息异常"); + this.$set(this.messages, roomId, []); + } + }, + + // 添加消息到聊天记录 + addMessageToChat(messageData) { + const roomId = messageData.roomId || this.currentContactId; + + // 如果该聊天室的消息数组不存在,则初始化 + if (!this.messages[roomId]) { + this.$set(this.messages, roomId, []); + } + + // 构造消息对象 + const message = { + id: messageData.id || Date.now(), // 如果没有id则使用时间戳 + sender: messageData.sender, + avatar: messageData.avatar || (messageData.isSelf ? "iconfont icon-icon28" : "iconfont icon-user"), + content: messageData.content, + time: messageData.time || new Date(), + isSelf: messageData.isSelf, + isImage: messageData.isImage || false, + type: messageData.type || 1, + roomId: roomId, + isRead: messageData.isRead || false, + }; + + // 添加消息到数组 + this.messages[roomId].push(message); + + // 更新最后一条消息 + this.updateContactLastMessage({ + roomId: roomId, + content: message.isImage ? "[图片]" : message.content, + isImage: message.isImage, + }); + + // 如果是当前聊天室且用户在底部,滚动到底部 + if (roomId === this.currentContactId && !this.userViewHistory) { + this.$nextTick(() => { + this.scrollToBottom(); + }); + } +}, + // 处理图片上传 + async handleImageUpload(event) { + // 检查是否有选中的联系人和 WebSocket 连接 + if (!this.currentContact) { + this.$message.warning("请先选择联系人"); + return; + } + + if (!this.stompClient || !this.isWebSocketConnected) { + this.$message.warning("聊天连接已断开,请刷新页面重试"); + return; + } + + const file = event.target.files[0]; + if (!file) return; + + // 检查是否为图片 + if (!file.type.startsWith("image/")) { + this.$message.warning("只能上传图片文件!"); + return; + } + + // 检查文件大小 (限制为5MB) + const maxSize = 5 * 1024 * 1024; + if (file.size > maxSize) { + this.$message.warning("图片大小不能超过5MB!"); + return; + } + + this.sending = true; try { - const response = await getHistory7(this.history7Params); - - if (response?.code === 200 && response.data) { - // 处理消息数据 - let roomMessages = response.data - .filter(msg => msg.roomId == roomId) - .map(msg => ({ - id: msg.id, - sender: msg.isSelf === 1 ? "我" : msg.sendEmail || "未知发送者", - avatar: msg.sendUserType == 2 ? "iconfont icon-icon28" : "iconfont icon-user", - content: msg.content, - time: new Date(msg.createTime), - isSelf: msg.isSelf === 1, - isImage: msg.type === 2, - isRead: msg.isRead === 1, - type: msg.type, - roomId: msg.roomId, - sendUserType: msg.sendUserType - })); + // 将图片转换为 base64 + const reader = new FileReader(); + reader.onload = (e) => { + const base64Image = e.target.result; + + // 构建WebSocket消息对象 + const message = { + content: base64Image, + type: 2, // 类型2表示图片消息 + email: this.currentContact.name, + receiveUserType: this.currentContact.sendUserType || 1, // 使用联系人的用户类型 + roomId: this.currentContactId, + }; - // 按时间排序 - roomMessages = roomMessages.sort((a, b) => a.time - b.time); + // 发送WebSocket消息 + this.stompClient.send( + "/point/send/message/to/user", + {}, + JSON.stringify(message) + ); - // 更新消息列表 - this.$set(this.messages, roomId, roomMessages); + // 添加到本地消息列表 + this.addMessageToChat({ + sender: "我", + content: base64Image, + time: new Date(), + isSelf: true, + isImage: true, + type: 2, + roomId: this.currentContactId, + }); - // 更新联系人的未读状态 - const contact = this.contacts.find(c => c.roomId === roomId); - if (contact) { - contact.unread = 0; - } + // 更新联系人最后一条消息 + this.updateContactLastMessage({ + roomId: this.currentContactId, + content: "[图片]", + isImage: true + }); + + this.$message.success("图片已发送"); + }; + + reader.onerror = () => { + this.$message.error("图片读取失败"); + this.sending = false; + }; + + // 开始读取文件 + reader.readAsDataURL(file); - } else { - // 如果没有消息数据,初始化为空数组 - this.$set(this.messages, roomId, []); - if (response?.code !== 200) { - this.$message.warning("获取聊天记录失败"); - } - } } catch (error) { - console.error("加载消息异常:", error); - this.$message.error("加载消息异常"); - this.$set(this.messages, roomId, []); + console.error("上传图片异常:", error); + this.$message.error("上传图片失败,请重试"); + } finally { + this.sending = false; + // 清空文件选择器 + this.$refs.imageInput.value = ""; } }, - // 添加消息到聊天记录 - addMessageToChat(messageData) { - const roomId = messageData.roomId || this.currentContactId; - - // 如果该聊天室的消息数组不存在,则初始化 - if (!this.messages[roomId]) { - this.$set(this.messages, roomId, []); - } - - // 构造消息对象 - const message = { - id: messageData.id, - sender: messageData.sender, - avatar: messageData.avatar || (messageData.isSelf ? "iconfont icon-icon28" : "iconfont icon-user"), - content: messageData.content, - time: messageData.time || new Date(), - isSelf: messageData.isSelf, - isImage: messageData.isImage || false, - type: messageData.type || 1, - roomId: roomId, - isRead: messageData.isRead || false - }; - - // 添加消息到数组 - this.messages[roomId].push(message); - - // 更新最后一条消息 - this.updateContactLastMessage({ - roomId: roomId, - content: message.content, - isImage: message.isImage - }); - - // 如果是当前聊天室且用户在底部,滚动到底部 - if (roomId === this.currentContactId && !this.userViewHistory) { - this.$nextTick(() => { - this.scrollToBottom(); - }); - } - }, - - // 处理图片上传 - async handleImageUpload(event) { - if (!this.currentContact || !this.wsConnected) return; - - const file = event.target.files[0]; - if (!file) return; - - // 检查是否为图片 - if (!file.type.startsWith("image/")) { - this.$message.warning("只能上传图片文件!"); - return; - } - - // 检查文件大小 (限制为5MB) - const maxSize = 5 * 1024 * 1024; - if (file.size > maxSize) { - this.$message.warning("图片大小不能超过5MB!"); - return; - } - - this.sending = true; - - try { - // 准备FormData - const formData = new FormData(); - formData.append("file", file); - - this.$message.info("正在上传图片..."); - - // 调用API上传图片到后端 - const response = await getFileUpdate(formData); - console.log("图片上传响应:", response); - - if (response && response.code === 200 && response.data) { - // 获取后端返回的图片信息 - const imageData = response.data; - const imageUrl = imageData.url; - - console.log("后端返回的图片URL:", imageUrl); - - // 构建WebSocket消息对象 - const message = { - content: imageUrl, // 使用后端返回的URL作为内容 - type: 2, // 类型2表示图片消息 - email: this.currentContact.name, - receiveUserType: this.userType, - roomId: this.currentContactId, - }; - - // 发送WebSocket消息 - this.stompClient.send( - "/point/send/message", - {}, - JSON.stringify(message) - ); - - // 添加到本地消息列表以在UI中显示 - this.addMessageToChat({ - sender: "我", - avatar: "iconfont icon-icon28", - content: imageUrl, - time: new Date(), - isSelf: true, - isImage: true, // 标记为图片消息 - }); - - this.$message.success("图片已发送"); - } else { - this.$message.error("图片上传失败: " + (response?.msg || "未知错误")); - } - } catch (error) { - console.error("上传图片异常:", error); - this.$message.error("上传图片失败,请重试"); - } finally { - this.sending = false; - // 清空文件选择器,以便于用户可以再次选择同一个文件 - this.$refs.imageInput.value = ""; - } - }, + // 更新联系人最后一条消息 updateContactLastMessage(message) { @@ -1249,7 +1372,7 @@ export default { } }, - // 加载历史消息(7天前) + // 小时钟加载历史消息(7天前) async loadHistory() { this.loadingHistory = true; this.userViewHistory = true; // 用户主动查看历史 @@ -1258,8 +1381,17 @@ export default { try { this.messagesLoading = true; + // 获取当前已加载的消息列表 + const currentMsgs = this.messages[this.currentContactId] || []; - const response = await getHistory({ roomId: this.currentContactId }); + // 取最后一条消息的id + const lastMsg = + currentMsgs.length > 0 ? currentMsgs[currentMsgs.length - 1] : null; + this.history7Params.id = lastMsg ? lastMsg.id : ""; + // this.history7Params.pageNum += 1; // 递增页码 + this.history7Params.roomId = this.currentContactId; + this.history7Params.email = this.userEmail; + const response = await getHistory7(this.history7Params); if (response && response.code === 200 && response.data) { let historyMessages = response.data @@ -1320,6 +1452,9 @@ export default { } } + // 组件销毁前断开连接 + this.disconnectWebSocket(); + // if (this.roomListInterval) { // clearInterval(this.roomListInterval); // } diff --git a/mining-pool/src/views/home/index.js b/mining-pool/src/views/home/index.js index 348c381..3f53e5b 100644 --- a/mining-pool/src/views/home/index.js +++ b/mining-pool/src/views/home/index.js @@ -76,22 +76,22 @@ export default { nameTextStyle: { padding: [0, 0, 0, -40], - } + }, // min: `dataMin`, // max: `dataMax`, - // axisLabel: { - // formatter: function (value) { - // let data - // if (value > 10000000) { - // data = `${(value / 10000000)} KW` - // } else if (value > 1000000) { - // data = `${(value / 1000000)} M` - // } else if (value / 10000) { - // data = `${(value / 10000)} W` - // } - // return data - // } - // } + axisLabel: { + formatter: function (value) { + // let data + // if (value > 10000000) { + // data = `${(value / 10000000)} KW` + // } else if (value > 1000000) { + // data = `${(value / 1000000)} M` + // } else if (value / 10000) { + // data = `${(value / 10000)} W` + // } + return value + } + } }, { @@ -506,7 +506,8 @@ export default { show: true, amount: 1, - } + }, + // { //告知已删除此币种 Radiant // value: "dgb2_odo", // label: "dgb-odocrypt-pool2", @@ -825,8 +826,37 @@ export default { } + }else{ //动态计算图表的grid.left 让左侧的Y轴标签显示完全 + + + const yAxis = this.option.yAxis[0]; // 第一个 Y 轴 + const maxValue = Math.max(...this.option.series[0].data); // 获取数据最大值 + const formatter = yAxis.axisLabel.formatter; + const formattedValue = formatter(maxValue); // 格式化最大值 + + // 创建一个临时 DOM 元素计算宽度 + const tempDiv = document.createElement('div'); + tempDiv.style.position = 'absolute'; + tempDiv.style.visibility = 'hidden'; + tempDiv.style.fontSize = '12px'; // 与 axisLabel.fontSize 一致 + tempDiv.innerText = formattedValue; + document.body.appendChild(tempDiv); + const labelWidth = tempDiv.offsetWidth; + document.body.removeChild(tempDiv); + + // 动态设置 grid.left,加上安全边距 + const safeMargin = 20; + this.option.grid.left = labelWidth + safeMargin + 'px'; + // this.$nextTick( + // // 更新图表 + // this.inCharts() + + // ) + } + + this.getBlockInfoData(this.BlockInfoParams) this.getCoinInfoData(this.params) @@ -1395,52 +1425,111 @@ export default { handelCalculation() { this.calculateIncome() }, - - // 左滑动逻辑 - scrollLeft() { - const allLength = this.currencyList.length * 120 - const boxLength = document.getElementById('list-box').clientWidth - if (allLength < boxLength) return - const listEl = document.getElementById('list') - const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left)) - if (leftMove + boxLength - 360 < boxLength) { - // 到最开始的时候 - listEl.style.left = '0PX' - } else { - listEl.style.left = '-' + (leftMove - 360) + 'PX' - } - }, - // 右滑动逻辑 - scrollRight() { - const allLength = this.currencyList.length * 120 - const boxLength = document.getElementById('list-box').clientWidth - if (allLength < boxLength) return - const listEl = document.getElementById('list') - const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left)) - if (leftMove + boxLength + 360 > allLength) { - listEl.style.left = '-' + (allLength - boxLength) + 'PX' - } else { - listEl.style.left = '-' + (leftMove + 360) + 'PX' - } - }, - - // clickCurrency: throttle(function(item) { - // this.currency = item.label - // this.currencyPath = item.imgUrl - // this.params.coin = item.value - // this.BlockInfoParams.coin = item.value - // this.itemActive = item.value - // this.PowerParams.coin = item.value - // this.getCoinInfoData(this.params) - // this.getBlockInfoData(this.BlockInfoParams) - // // this.getPoolPowerData(this.PowerParams) - // // this.getMinerCountData(this.params) - // if (this.powerActive) { - // this.handelPower() - // } else if (!this.powerActive) { - // this.handelMiner() + // 获取单个币种项目的完整宽度(包含margin) + getItemFullWidth() { + const listEl = this.$refs.currencyList; + if (!listEl) return 120; + + const firstItem = listEl.querySelector('.list-item'); + if (!firstItem) return 120; + + const style = window.getComputedStyle(firstItem); + const width = firstItem.offsetWidth; + const marginLeft = parseInt(style.marginLeft) || 0; + const marginRight = parseInt(style.marginRight) || 0; + + return width + marginLeft + marginRight; + }, + + // 左滑动逻辑 + scrollLeft() { + const listEl = this.$refs.currencyList; + const listBox = this.$refs.listBox; + if (!listEl || !listBox) return; + + const itemFullWidth = this.getItemFullWidth(); + const step = 2 * itemFullWidth; // 每次滑动2个币种 + const allLength = this.currencyList.length * itemFullWidth; + const boxLength = listBox.clientWidth; + + if (allLength <= boxLength) return; + + let currentLeft = Math.abs(parseInt(listEl.style.transform.replace('translateX(', '').replace('px)', '')) || 0); + let newLeft = currentLeft - step; + + listEl.classList.add('scrolling'); + + if (newLeft <= 0) { + listEl.style.transform = 'translateX(0)'; + } else { + listEl.style.transform = `translateX(-${newLeft}px)`; + } + + // 增加动画时间到 500ms + setTimeout(() => { + listEl.classList.remove('scrolling'); + }, 500); + }, + + // 右滑动逻辑 + scrollRight() { + const listEl = this.$refs.currencyList; + const listBox = this.$refs.listBox; + if (!listEl || !listBox) return; + + const itemFullWidth = this.getItemFullWidth(); + const step = 2 * itemFullWidth; // 每次滑动2个币种 + const allLength = this.currencyList.length * itemFullWidth; + const boxLength = listBox.clientWidth; + + if (allLength <= boxLength) return; + + let currentLeft = Math.abs(parseInt(listEl.style.transform.replace('translateX(', '').replace('px)', '')) || 0); + let newLeft = currentLeft + step; + const maxLeft = allLength - boxLength; + + listEl.classList.add('scrolling'); + + if (newLeft >= maxLeft) { + listEl.style.transform = `translateX(-${maxLeft}px)`; + } else { + listEl.style.transform = `translateX(-${newLeft}px)`; + } + + // 增加动画时间到 500ms + setTimeout(() => { + listEl.classList.remove('scrolling'); + }, 500); + }, + // // 左滑动逻辑 + // scrollLeft() { + // const allLength = this.currencyList.length * 120 + // const boxLength = document.getElementById('list-box').clientWidth + // if (allLength < boxLength) return + // const listEl = document.getElementById('list') + // const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left)) + // if (leftMove + boxLength - 360 < boxLength) { + // // 到最开始的时候 + // listEl.style.left = '0PX' + // } else { + // listEl.style.left = '-' + (leftMove - 360) + 'PX' // } - // }, 1000), + // }, + // // 右滑动逻辑 + // scrollRight() { + // const allLength = this.currencyList.length * 120 + // const boxLength = document.getElementById('list-box').clientWidth + // if (allLength < boxLength) return + // const listEl = document.getElementById('list') + // const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left)) + // if (leftMove + boxLength + 360 > allLength) { + // listEl.style.left = '-' + (allLength - boxLength) + 'PX' + // } else { + // listEl.style.left = '-' + (leftMove + 360) + 'PX' + // } + // }, + + handleActiveItemChange(item) { if (!item) return; diff --git a/mining-pool/src/views/home/index.vue b/mining-pool/src/views/home/index.vue index 40299be..671711e 100644 --- a/mining-pool/src/views/home/index.vue +++ b/mining-pool/src/views/home/index.vue @@ -340,6 +340,29 @@
+
+ +
+
+
+
+ coin + + {{ item.label }} + +
+
+
+
+ +
+
+
@@ -405,7 +428,6 @@ >{{ $t(item.label) }} -