uniapp 实现腾讯云 IM 消息撤回功能实战指南
一、功能实现原理
腾讯云 IM 的消息撤回功能通过 消息修订(Message Revision) 机制实现,核心流程如下:
- 发送方调用撤回 API 删除指定消息
- 云端生成撤回通知消息(类型为
TIM.TYPES.MSG_REVOKED
) - 接收方收到通知后执行本地消息删除
- 全平台自动同步消息状态(需开启消息漫游)
二、核心实现步骤
1. 发送方撤回逻辑
javascript
// services/im.js
export async function revokeMessage(message) {
const tim = initIM()
try {
// 执行消息撤回操作
const res = await tim.revokeMessage(message)
// 更新本地消息状态(立即生效)
if (res.data.revokeMessage) {
const conv = tim.getConversationProfile(message.conversationID)
conv.setMessageRevoked(message.clientMsgID)
}
return res
} catch (error) {
console.error('撤回失败:', error)
throw new Error('消息撤回失败,请检查网络')
}
}
2. 接收方消息处理
javascript
// 消息监听器(全局注册)
export function setupMessageListener(callback) {
const tim = initIM()
tim.on(tim.EVENT.MESSAGE_RECEIVED, (event) => {
event.data.forEach(msg => {
// 处理撤回通知
if (msg.type === tim.TYPES.MSG_REVOKED) {
handleRevokeNotice(msg)
return
}
callback(msg)
})
})
}
// 撤回通知处理
function handleRevokeNotice(notice) {
const { revokedMessageClientMsgID, operator } = notice.payload
// 查找本地对应消息
const conversation = tim.getConversationProfile(notice.conversationID)
const originalMsg = conversation.getMessage(revokedMessageClientMsgID)
if (!originalMsg) return
// 权限验证(仅允许发送者撤回)
if (originalMsg.from !== operator.userID) {
console.warn('非法撤回操作', operator)
return
}
// 执行本地删除
conversation.deleteMessage(revokedMessageClientMsgID)
// 触发UI更新(示例)
uni.$emit('message-revoked', {
conversationID: notice.conversationID,
clientMsgID: revokedMessageClientMsgID
})
}
3. UI 层集成示例
html
<template>
<view class="message-list">
<view
v-for="(msg, index) in messages"
:key="msg.clientMsgID"
class="message-item"
>
<!-- 消息内容 -->
<template v-if="!msg.isRevoked">
{{ msg.payload.text }}
</template>
<!-- 撤回提示 -->
<view v-else class="revoked-tip">
"{{ msg.payload.description }}" 已被撤回
</view>
<!-- 长按操作菜单 -->
<view
v-if="canRevoke(msg)"
class="action-menu"
@longpress="showActionSheet(msg)"
>
⋮
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
messages: []
}
},
methods: {
// 权限校验
canRevoke(msg) {
return msg.from === this.currentUser.userID &&
!msg.isRevoked &&
Date.now() - msg.time < 2 * 60 * 1000 // 2分钟内可撤回
},
// 执行撤回
async handleRevoke(msg) {
try {
await revokeMessage(msg)
uni.showToast({ title: '撤回成功', icon: 'none' })
} catch (error) {
uni.showToast({ title: error.message, icon: 'none' })
}
}
}
}
</script>
三、关键问题处理
1. 撤回时间限制
javascript
// 配置中心(建议)
const IM_CONFIG = {
REVOKE_TIME_LIMIT: 2 * 60 * 1000 // 2分钟
}
// 权限校验时使用
if (Date.now() - msg.time > IM_CONFIG.REVOKE_TIME_LIMIT) {
throw new Error('超过可撤回时间')
}
2. 消息状态同步
javascript
// 消息漫游配置(初始化时)
tim = TIM.create({
SDKAppID: config.SDKAppID
})
// 开启消息漫游(需在控制台配置)
tim.setMessageRevokeMode({
mode: TIM.TYPES.REVOKE_MODE_SENDER, // 仅发送方可撤回
syncOtherMachine: true // 同步到其他端
})
3. 异常场景处理
javascript
// 撤回失败重试机制
export async function revokeWithRetry(msg, retries = 3) {
try {
return await revokeMessage(msg)
} catch (error) {
if (retries <= 0) throw error
await new Promise(resolve => setTimeout(resolve, 1000))
return revokeWithRetry(msg, retries - 1)
}
}
四、高级功能扩展
1. 富媒体消息撤回
javascript
// 自定义撤回描述(图片/文件等)
function getRevokeDescription(msg) {
switch(msg.type) {
case TIM.TYPES.MSG_IMAGE:
return '[图片]'
case TIM.TYPES.MSG_FILE:
return '[文件]'
case TIM.TYPES.MSG_CUSTOM:
return JSON.parse(msg.payload.data).description || '[自定义消息]'
default:
return msg.payload.text || '[未知消息]'
}
}
2. 撤回动画效果
css
/* 添加CSS过渡 */
.message-item.revoking {
animation: fadeOut 0.3s forwards;
}
@keyframes fadeOut {
to {
opacity: 0;
transform: translateX(20px);
}
}
3. 服务端日志记录
javascript
// 撤回事件上报(示例)
async function logRevokeEvent(msg, operator) {
await axios.post('/api/im/revoke-log', {
sdk_app_id: process.env.SDKAppID,
group_id: msg.groupID,
operator_id: operator.userID,
target_msg_id: msg.clientMsgID,
timestamp: Date.now()
})
}
五、常见问题排查
-
Q: 撤回后对方仍显示消息
A: 检查消息漫游是否开启,确认双方客户端版本 ≥ 2.15.0
-
Q: 无法撤回超过2分钟的消息
A: 腾讯云默认限制为2分钟,需在控制台申请延长权限
-
Q: 群聊中非群主成员撤回失败
A: 确认群类型是否为 Private(私有群),Public 群需群主操作
-
Q: 撤回通知不显示描述
A: 检查自定义消息解析逻辑,确保 payload 格式正确
六、性能优化建议
- 使用
tim.getMessageRevokeStatus()
批量查询消息状态 - 对已撤回消息进行本地缓存,避免重复查询
- 添加防抖处理,防止快速连续撤回导致性能问题