撤回消息实现
This commit is contained in:
27
App.vue
27
App.vue
@@ -365,6 +365,33 @@ export default {
|
|||||||
IMSDK.IMEvents.OnConversationChanged,
|
IMSDK.IMEvents.OnConversationChanged,
|
||||||
conversationChangedHandler
|
conversationChangedHandler
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 撤回消息回调
|
||||||
|
const messageRevokedHandler = ({ data }) => {
|
||||||
|
if (this.storeIsSyncing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 兼容:如果没有 contentType,补充为撤回类型
|
||||||
|
if (!data.contentType) {
|
||||||
|
data.contentType = MessageType.RevokeMessage;
|
||||||
|
data.notificationElem = {
|
||||||
|
detail: JSON.stringify({
|
||||||
|
revokerID: data.revokerID,
|
||||||
|
revokerName: data.revokerNickname || data.revokerID
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 更新被撤回的消息为撤回通知消息
|
||||||
|
this.updateOneMessage({
|
||||||
|
message: data,
|
||||||
|
type: UpdateMessageTypes.Overall,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
IMSDK.subscribe(
|
||||||
|
IMSDK.IMEvents.OnNewRecvMessageRevoked,
|
||||||
|
messageRevokedHandler
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
tryLogin() {
|
tryLogin() {
|
||||||
|
|||||||
@@ -1,83 +1,100 @@
|
|||||||
<template>
|
<template>
|
||||||
<view
|
<view>
|
||||||
v-if="!getNoticeContent"
|
<view
|
||||||
:id="`auchor${source.clientMsgID}`"
|
v-if="!getNoticeContent"
|
||||||
class="message_item"
|
:id="`auchor${source.clientMsgID}`"
|
||||||
:class="{ message_item_self: isSender, message_item_active: isActive }"
|
class="message_item"
|
||||||
>
|
:class="{ message_item_self: isSender, message_item_active: isActive }"
|
||||||
<my-avatar
|
>
|
||||||
size="42"
|
<my-avatar
|
||||||
:desc="source.senderNickname"
|
size="42"
|
||||||
:src="source.senderFaceUrl"
|
:desc="source.senderNickname"
|
||||||
/>
|
:src="source.senderFaceUrl"
|
||||||
<view class="message_container">
|
/>
|
||||||
<view
|
<view class="message_container">
|
||||||
class="message_sender"
|
|
||||||
:style="{ 'flex-direction': !isSender ? 'row-reverse' : 'row' }"
|
|
||||||
>
|
|
||||||
<text>{{ formattedMessageTime }}</text>
|
|
||||||
<text style="margin-left: 2rpx; margin-right: 2rpx">{{ "" }}</text>
|
|
||||||
<text v-if="!isSingle">{{ source.senderNickname }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="message_send_state_box">
|
|
||||||
<view
|
<view
|
||||||
style="
|
class="message_sender"
|
||||||
height: 100%;
|
:style="{ 'flex-direction': !isSender ? 'row-reverse' : 'row' }"
|
||||||
display: flex;
|
|
||||||
justify-items: center;
|
|
||||||
align-items: center;
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<view class="message_send_state">
|
<text>{{ formattedMessageTime }}</text>
|
||||||
<u-loading-icon v-if="showSending && !isPreview" />
|
<text style="margin-left: 2rpx; margin-right: 2rpx">{{ "" }}</text>
|
||||||
<image
|
<text v-if="!isSingle">{{ source.senderNickname }}</text>
|
||||||
v-if="isFailedMessage && !isPreview"
|
|
||||||
src="@/static/images/chating_message_failed.png"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="message_content_wrap message_content_wrap_shadow">
|
<view class="message_send_state_box">
|
||||||
<template v-if="source.contentType === 101">
|
<view
|
||||||
<TextMessageRender :message="source" />
|
style="
|
||||||
</template>
|
height: 100%;
|
||||||
<template v-else-if="source.contentType === 102">
|
display: flex;
|
||||||
<MediaMessageRender :message="source" />
|
justify-items: center;
|
||||||
</template>
|
align-items: center;
|
||||||
<template v-else-if="source.contentType === 103">
|
"
|
||||||
<VoiceMessageRender :source="source" :isSender="isSender" />
|
>
|
||||||
</template>
|
<view class="message_send_state">
|
||||||
<template v-else-if="source.contentType === 104">
|
<u-loading-icon v-if="showSending && !isPreview" />
|
||||||
<view style="color:#999">[暂未实现] 视频消息</view>
|
<image
|
||||||
</template>
|
v-if="isFailedMessage && !isPreview"
|
||||||
<template v-else-if="source.contentType === 105">
|
src="@/static/images/chating_message_failed.png"
|
||||||
<FileMessageRender :source="source" />
|
/>
|
||||||
</template>
|
</view>
|
||||||
<template v-else-if="source.contentType === 106">
|
</view>
|
||||||
<view style="color:#999">[暂未实现] @消息</view>
|
<view class="message_content_wrap message_content_wrap_shadow" @longpress="handleLongPress">
|
||||||
</template>
|
<template v-if="source.contentType === 101">
|
||||||
<template v-else-if="source.contentType === 109">
|
<TextMessageRender :message="source" />
|
||||||
<view style="color:#999">[暂未实现] 位置消息</view>
|
</template>
|
||||||
</template>
|
<template v-else-if="source.contentType === 102">
|
||||||
<template v-else-if="source.contentType === 110">
|
<MediaMessageRender :message="source" />
|
||||||
<view style="color:#999">[暂未实现] 自定义消息</view>
|
</template>
|
||||||
</template>
|
<template v-else-if="source.contentType === 103">
|
||||||
<template v-else-if="source.contentType === 1400">
|
<VoiceMessageRender :source="source" :isSender="isSender" />
|
||||||
<view style="color:#999">[暂未实现] 系统通知</view>
|
</template>
|
||||||
</template>
|
<template v-else-if="source.contentType === 104">
|
||||||
<template v-else>
|
<view style="color:#999">[暂未实现] 视频消息</view>
|
||||||
<ErrorMessageRender :source="source" />
|
</template>
|
||||||
</template>
|
<template v-else-if="source.contentType === 105">
|
||||||
|
<FileMessageRender :source="source" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="source.contentType === 106">
|
||||||
|
<view style="color:#999">[暂未实现] @消息</view>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="source.contentType === 109">
|
||||||
|
<view style="color:#999">[暂未实现] 位置消息</view>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="source.contentType === 110">
|
||||||
|
<view style="color:#999">[暂未实现] 自定义消息</view>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="source.contentType === 1400">
|
||||||
|
<view style="color:#999">[暂未实现] 系统通知</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<ErrorMessageRender :source="source" />
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
|
|
||||||
<view
|
<view
|
||||||
v-else
|
v-else
|
||||||
class="notice_message_container"
|
class="notice_message_container"
|
||||||
:id="`auchor${source.clientMsgID}`"
|
:id="`auchor${source.clientMsgID}`"
|
||||||
>
|
>
|
||||||
<text>{{ getNoticeContent }}</text>
|
<text>{{ getNoticeContent }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- uni-popup 消息操作菜单 -->
|
||||||
|
<uni-popup ref="actionPopup" :show="showActionMenu" type="bottom" @change="onPopupChange">
|
||||||
|
<view class="popup-action-list">
|
||||||
|
<view
|
||||||
|
v-for="(action, index) in actionMenuItems"
|
||||||
|
:key="index"
|
||||||
|
class="popup-action-item"
|
||||||
|
@click="handleActionClick(action)"
|
||||||
|
>
|
||||||
|
<text class="popup-action-text">{{ action }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -94,8 +111,11 @@ import MediaMessageRender from "./MediaMessageRender.vue";
|
|||||||
import ErrorMessageRender from "./ErrorMessageRender.vue";
|
import ErrorMessageRender from "./ErrorMessageRender.vue";
|
||||||
import VoiceMessageRender from './VoiceMessageRender.vue'
|
import VoiceMessageRender from './VoiceMessageRender.vue'
|
||||||
import FileMessageRender from './FileMessageRender.vue'
|
import FileMessageRender from './FileMessageRender.vue'
|
||||||
|
import uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue';
|
||||||
|
|
||||||
import { noticeMessageTypes } from "@/constant";
|
import { noticeMessageTypes } from "@/constant";
|
||||||
import { tipMessaggeFormat, formatMessageTime } from "@/util/imCommon";
|
import { tipMessaggeFormat, formatMessageTime } from "@/util/imCommon";
|
||||||
|
import { revokeMessage, canRevokeMessage } from "@/util/revokeMessage";
|
||||||
|
|
||||||
const textRenderTypes = [MessageType.TextMessage];
|
const textRenderTypes = [MessageType.TextMessage];
|
||||||
|
|
||||||
@@ -108,7 +128,8 @@ export default {
|
|||||||
MediaMessageRender,
|
MediaMessageRender,
|
||||||
ErrorMessageRender,
|
ErrorMessageRender,
|
||||||
VoiceMessageRender,
|
VoiceMessageRender,
|
||||||
FileMessageRender
|
FileMessageRender,
|
||||||
|
uniPopup,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
source: Object,
|
source: Object,
|
||||||
@@ -120,12 +141,17 @@ export default {
|
|||||||
isActive: Boolean,
|
isActive: Boolean,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {
|
||||||
|
sendingDelay: true,
|
||||||
|
showActionMenu: false,
|
||||||
|
actionMenuItems: [],
|
||||||
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
"storeCurrentConversation",
|
"storeCurrentConversation",
|
||||||
"storeSelfInfo",
|
"storeSelfInfo",
|
||||||
|
"storeHistoryMessageList",
|
||||||
]),
|
]),
|
||||||
isSingle() {
|
isSingle() {
|
||||||
return (
|
return (
|
||||||
@@ -169,6 +195,14 @@ export default {
|
|||||||
this.$emit("messageItemRender", this.source.clientMsgID);
|
this.$emit("messageItemRender", this.source.clientMsgID);
|
||||||
this.setSendingDelay();
|
this.setSendingDelay();
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
source: {
|
||||||
|
handler(newVal, oldVal) {
|
||||||
|
console.log('MessageItem source changed:', { newVal, oldVal });
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setSendingDelay() {
|
setSendingDelay() {
|
||||||
if (this.source && this.source.status === MessageStatus.Sending) {
|
if (this.source && this.source.status === MessageStatus.Sending) {
|
||||||
@@ -177,6 +211,119 @@ export default {
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleLongPress(e) {
|
||||||
|
this.actionMenuItems = this.getActionMenuItems();
|
||||||
|
this.showActionMenu = true;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.actionPopup && this.$refs.actionPopup.open && this.$refs.actionPopup.open('bottom');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
getActionMenuItems() {
|
||||||
|
const items = [];
|
||||||
|
|
||||||
|
// 复制消息(文本消息)
|
||||||
|
if (this.source.contentType === 101) {
|
||||||
|
items.push('复制');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 撤回消息(只有自己发送的2分钟内消息才能撤回)
|
||||||
|
if (canRevokeMessage(this.source, this.storeSelfInfo.userID)) {
|
||||||
|
items.push('撤回');
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleActionClick(action) {
|
||||||
|
switch (action) {
|
||||||
|
case '复制':
|
||||||
|
this.copyMessage();
|
||||||
|
break;
|
||||||
|
case '撤回':
|
||||||
|
this.confirmRevokeMessage();
|
||||||
|
break;
|
||||||
|
case '转发':
|
||||||
|
this.forwardMessage();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log('未知操作:', action);
|
||||||
|
}
|
||||||
|
this.showActionMenu = false;
|
||||||
|
this.$refs.actionPopup && this.$refs.actionPopup.close && this.$refs.actionPopup.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
closeActionMenu() {
|
||||||
|
this.showActionMenu = false;
|
||||||
|
this.$refs.actionPopup && this.$refs.actionPopup.close && this.$refs.actionPopup.close();
|
||||||
|
},
|
||||||
|
onPopupChange(e) {
|
||||||
|
if (!e.show) {
|
||||||
|
this.showActionMenu = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
copyMessage() {
|
||||||
|
if (this.source.contentType === 101 && this.source.textElem && this.source.textElem.content) {
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: this.source.textElem.content,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '已复制到剪贴板',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
forwardMessage() {
|
||||||
|
// TODO: 实现转发消息功能
|
||||||
|
uni.showToast({
|
||||||
|
title: '转发功能开发中',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async confirmRevokeMessage() {
|
||||||
|
// 再次检查是否可以撤回(防止状态变化)
|
||||||
|
if (!canRevokeMessage(this.source, this.storeSelfInfo.userID)) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '只能撤回自己发送的2分钟内的消息',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '撤回中...'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 撤回消息
|
||||||
|
const result = await revokeMessage(
|
||||||
|
this.storeCurrentConversation.conversationID,
|
||||||
|
this.source.clientMsgID
|
||||||
|
);
|
||||||
|
|
||||||
|
uni.hideLoading();
|
||||||
|
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '撤回成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: '撤回失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
console.error('撤回消息失败:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -339,4 +486,38 @@ export default {
|
|||||||
.fade-enter {
|
.fade-enter {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup-action-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 24rpx 0;
|
||||||
|
background: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-action-item {
|
||||||
|
margin: 0 32rpx 20rpx 32rpx;
|
||||||
|
padding: 24rpx 0;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333;
|
||||||
|
transition: background 0.2s;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #e6f0ff;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-action-text {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -55,10 +55,11 @@ const actions = {
|
|||||||
isSuccess = false,
|
isSuccess = false,
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const tmpList = state.historyMessageList;
|
const tmpList = [...state.historyMessageList];
|
||||||
const idx = tmpList.findIndex(
|
const idx = tmpList.findIndex(
|
||||||
(msg) => msg.clientMsgID === message.clientMsgID,
|
(msg) => msg.clientMsgID === message.clientMsgID,
|
||||||
);
|
);
|
||||||
|
console.log('updateOneMessage:', { message, idx, listLength: tmpList.length });
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
if (type === UpdateMessageTypes.Overall) {
|
if (type === UpdateMessageTypes.Overall) {
|
||||||
tmpList[idx] = {
|
tmpList[idx] = {
|
||||||
@@ -71,6 +72,9 @@ const actions = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
commit("SET_HISTORY_MESSAGE_LIST", tmpList);
|
commit("SET_HISTORY_MESSAGE_LIST", tmpList);
|
||||||
|
console.log('消息更新成功,新的消息内容:', tmpList[idx]);
|
||||||
|
} else {
|
||||||
|
console.log('未找到要更新的消息:', message.clientMsgID);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetMessageState({ commit }) {
|
resetMessageState({ commit }) {
|
||||||
|
|||||||
@@ -188,6 +188,20 @@ export const parseMessageByType = (pmsg) => {
|
|||||||
return `${getName(groupNameUpdateUser)}修改了群名称为${
|
return `${getName(groupNameUpdateUser)}修改了群名称为${
|
||||||
groupNameUpdateDetail.group.groupName
|
groupNameUpdateDetail.group.groupName
|
||||||
}`;
|
}`;
|
||||||
|
case MessageType.RevokeMessage:
|
||||||
|
try {
|
||||||
|
const revokeDetails = JSON.parse(pmsg.notificationElem.detail);
|
||||||
|
const revokerID = revokeDetails.revokerID;
|
||||||
|
const revokerName = revokeDetails.revokerName || "未知用户";
|
||||||
|
|
||||||
|
if (revokerID === store.getters.storeCurrentUserID) {
|
||||||
|
return "你撤回了一条消息";
|
||||||
|
} else {
|
||||||
|
return `${revokerName}撤回了一条消息`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return "消息已被撤回";
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return "[暂未支持的消息类型]";
|
return "[暂未支持的消息类型]";
|
||||||
}
|
}
|
||||||
@@ -288,6 +302,20 @@ export const tipMessaggeFormat = (msg, currentUserID) => {
|
|||||||
const dismissDetails = JSON.parse(msg.notificationElem.detail);
|
const dismissDetails = JSON.parse(msg.notificationElem.detail);
|
||||||
const dismissUser = dismissDetails.opUser;
|
const dismissUser = dismissDetails.opUser;
|
||||||
return `${getName(dismissUser)}解散了群聊`;
|
return `${getName(dismissUser)}解散了群聊`;
|
||||||
|
case MessageType.RevokeMessage:
|
||||||
|
try {
|
||||||
|
const revokeDetails = JSON.parse(msg.notificationElem.detail);
|
||||||
|
const revokerID = revokeDetails.revokerID;
|
||||||
|
const revokerName = revokeDetails.revokerName || "未知用户";
|
||||||
|
|
||||||
|
if (revokerID === currentUserID) {
|
||||||
|
return "你撤回了一条消息";
|
||||||
|
} else {
|
||||||
|
return `${revokerName}撤回了一条消息`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return "消息已被撤回";
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
82
util/revokeMessage.js
Normal file
82
util/revokeMessage.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import IMSDK, { MessageStatus } from "openim-uniapp-polyfill";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤回消息
|
||||||
|
* @param {string} conversationID - 会话ID
|
||||||
|
* @param {string} clientMsgID - 消息客户端ID
|
||||||
|
* @returns {Promise} 撤回结果
|
||||||
|
*/
|
||||||
|
export const revokeMessage = async (conversationID, clientMsgID) => {
|
||||||
|
try {
|
||||||
|
const result = await IMSDK.asyncApi(
|
||||||
|
IMSDK.IMMethods.RevokeMessage,
|
||||||
|
IMSDK.uuid(),
|
||||||
|
{
|
||||||
|
conversationID,
|
||||||
|
clientMsgID,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("撤回消息失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查消息是否可以撤回
|
||||||
|
* @param {Object} message - 消息对象
|
||||||
|
* @param {string} currentUserID - 当前用户ID
|
||||||
|
* @returns {boolean} 是否可以撤回
|
||||||
|
*/
|
||||||
|
export const canRevokeMessage = (message, currentUserID) => {
|
||||||
|
console.log('检查消息是否可以撤回:', {
|
||||||
|
messageSendID: message.sendID,
|
||||||
|
currentUserID: currentUserID,
|
||||||
|
messageStatus: message.status,
|
||||||
|
contentType: message.contentType,
|
||||||
|
sendTime: message.sendTime,
|
||||||
|
now: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
// 只能撤回自己发送的消息
|
||||||
|
if (message.sendID !== currentUserID) {
|
||||||
|
console.log('不是自己发送的消息');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查消息状态是否为成功
|
||||||
|
if (message.status !== MessageStatus.Succeed) {
|
||||||
|
console.log('消息状态不是成功:', message.status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查消息类型是否支持撤回
|
||||||
|
const revokableTypes = [
|
||||||
|
101, // 文本消息
|
||||||
|
102, // 图片消息
|
||||||
|
103, // 语音消息
|
||||||
|
104, // 视频消息
|
||||||
|
105, // 文件消息
|
||||||
|
106, // @消息
|
||||||
|
110, // 自定义消息
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!revokableTypes.includes(message.contentType)) {
|
||||||
|
console.log('不支持撤回的消息类型:', message.contentType);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查消息发送时间是否在2分钟内(可配置)
|
||||||
|
const now = Date.now();
|
||||||
|
const messageTime = message.sendTime;
|
||||||
|
const timeLimit = 2 * 60 * 1000; // 2分钟
|
||||||
|
|
||||||
|
if (now - messageTime > timeLimit) {
|
||||||
|
console.log('消息发送时间超过2分钟');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('消息可以撤回');
|
||||||
|
return true;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user