diff --git a/src/utils/pk-mini/goeasy.js b/src/utils/pk-mini/goeasy.js index 0d929cc..f3614cd 100644 --- a/src/utils/pk-mini/goeasy.js +++ b/src/utils/pk-mini/goeasy.js @@ -263,6 +263,28 @@ export function goEasyMessageRead(data) { }) } +// 撤回消息 +export function goEasyRecallMessage(message) { + if (!isGoEasyEnabled()) { + return Promise.reject(new Error('GoEasy 未启用')) + } + + const goeasy = getPkGoEasy() + const im = goeasy.im + return new Promise((resolve, reject) => { + im.recallMessage({ + messages: [message], + onSuccess: function () { + resolve(true) + }, + onFailed: function (error) { + console.log('撤回失败, code:' + error.code + ' content:' + error.content) + reject(error) + } + }) + }) +} + // 删除会话 export function goEasyRemoveConversation(conversation) { if (!isGoEasyEnabled()) { diff --git a/src/views/pk-mini/Message.vue b/src/views/pk-mini/Message.vue index 3553c77..6547420 100644 --- a/src/views/pk-mini/Message.vue +++ b/src/views/pk-mini/Message.vue @@ -1,6 +1,6 @@ @@ -104,6 +120,7 @@ import { goEasySendImageMessage, goEasyMessageRead, goEasyRemoveConversation, + goEasyRecallMessage, getPkGoEasy, GoEasy } from '@/utils/pk-mini/goeasy' @@ -123,6 +140,53 @@ const isScrollReady = ref(false) const chatMessagesRef = ref(null) const fileInputRef = ref(null) const unreadStore = pkUnreadStore() +const contextMenu = ref({ visible: false, x: 0, y: 0, msg: null, index: -1 }) +const messagePageRef = ref(null) + +function showContextMenu(event, msg, index) { + const menuWidth = 110 + const menuHeight = msg.senderId == currentUser.value.id ? 80 : 44 + const rect = messagePageRef.value?.getBoundingClientRect() || { left: 0, top: 0, width: window.innerWidth, height: window.innerHeight } + let x = event.clientX - rect.left + let y = event.clientY - rect.top + if (x + menuWidth > rect.width) x -= menuWidth + if (y + menuHeight > rect.height) y -= menuHeight + contextMenu.value = { visible: true, x, y, msg, index } +} + +function hideContextMenu() { + contextMenu.value.visible = false +} + +function copyMessage() { + const text = contextMenu.value.msg?.payload?.text || '' + if (navigator.clipboard) { + navigator.clipboard.writeText(text).catch(() => {}) + } else { + const el = document.createElement('textarea') + el.value = text + document.body.appendChild(el) + el.select() + document.execCommand('copy') + document.body.removeChild(el) + } + hideContextMenu() +} + +async function recallMessage() { + const msg = contextMenu.value.msg + const index = contextMenu.value.index + hideContextMenu() + if (!msg) return + try { + await goEasyRecallMessage(msg) + // 用对象替换触发 Vue 响应式更新 + messagesList.value.splice(index, 1, { ...msg, recalled: true }) + } catch (e) { + console.error('撤回失败', e) + ElMessage.error('撤回失败,消息超过4小时或发送中') + } +} const formatTime = TimestamptolocalTime @@ -273,6 +337,7 @@ async function removeConversation(item, index) { onMounted(() => { currentUser.value = getMainUserData() || {} + document.addEventListener('click', hideContextMenu) if (isGoEasyEnabled()) { loadConversations() const goeasy = getPkGoEasy() @@ -280,6 +345,7 @@ onMounted(() => { goeasy.im.on(GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, onMessageReceived) goeasy.im.on(GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, onConversationsUpdated) goeasy.im.on(GoEasy.IM_EVENT.MESSAGE_READ, onMessageRead) + goeasy.im.on(GoEasy.IM_EVENT.MESSAGE_RECALLED, onMessageRecalled) } } // 切换回消息页面时,滚到聊天记录最底部 @@ -302,11 +368,19 @@ function onConversationsUpdated(conversations) { } function onMessageRead(messages) { - // 收到对方已读回执,更新本地消息的 read 状态 messages.forEach(readMsg => { - const target = messagesList.value.find(m => m.messageId === readMsg.messageId) - if (target) { - target.read = true + const index = messagesList.value.findIndex(m => m.messageId === readMsg.messageId) + if (index !== -1) { + messagesList.value.splice(index, 1, { ...messagesList.value[index], read: true }) + } + }) +} + +function onMessageRecalled(messages) { + messages.forEach(recalled => { + const index = messagesList.value.findIndex(m => m.messageId === recalled.messageId) + if (index !== -1) { + messagesList.value.splice(index, 1, { ...messagesList.value[index], recalled: true }) } }) } @@ -327,6 +401,7 @@ function onMessageReceived(message) { } onUnmounted(() => { + document.removeEventListener('click', hideContextMenu) if (isGoEasyEnabled()) { const goeasy = getPkGoEasy() if (goeasy) { @@ -334,6 +409,7 @@ onUnmounted(() => { goeasy.im.off(GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, onMessageReceived) goeasy.im.off(GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, onConversationsUpdated) goeasy.im.off(GoEasy.IM_EVENT.MESSAGE_READ, onMessageRead) + goeasy.im.off(GoEasy.IM_EVENT.MESSAGE_RECALLED, onMessageRecalled) } catch (e) { console.warn('清理 GoEasy 监听器失败', e) } @@ -344,6 +420,7 @@ onUnmounted(() => {