撤回消息实现
This commit is contained in:
@@ -1,83 +1,100 @@
|
||||
<template>
|
||||
<view
|
||||
v-if="!getNoticeContent"
|
||||
:id="`auchor${source.clientMsgID}`"
|
||||
class="message_item"
|
||||
:class="{ message_item_self: isSender, message_item_active: isActive }"
|
||||
>
|
||||
<my-avatar
|
||||
size="42"
|
||||
:desc="source.senderNickname"
|
||||
:src="source.senderFaceUrl"
|
||||
/>
|
||||
<view class="message_container">
|
||||
<view
|
||||
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
|
||||
v-if="!getNoticeContent"
|
||||
:id="`auchor${source.clientMsgID}`"
|
||||
class="message_item"
|
||||
:class="{ message_item_self: isSender, message_item_active: isActive }"
|
||||
>
|
||||
<my-avatar
|
||||
size="42"
|
||||
:desc="source.senderNickname"
|
||||
:src="source.senderFaceUrl"
|
||||
/>
|
||||
<view class="message_container">
|
||||
<view
|
||||
style="
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
"
|
||||
class="message_sender"
|
||||
:style="{ 'flex-direction': !isSender ? 'row-reverse' : 'row' }"
|
||||
>
|
||||
<view class="message_send_state">
|
||||
<u-loading-icon v-if="showSending && !isPreview" />
|
||||
<image
|
||||
v-if="isFailedMessage && !isPreview"
|
||||
src="@/static/images/chating_message_failed.png"
|
||||
/>
|
||||
</view>
|
||||
<text>{{ formattedMessageTime }}</text>
|
||||
<text style="margin-left: 2rpx; margin-right: 2rpx">{{ "" }}</text>
|
||||
<text v-if="!isSingle">{{ source.senderNickname }}</text>
|
||||
</view>
|
||||
<view class="message_content_wrap message_content_wrap_shadow">
|
||||
<template v-if="source.contentType === 101">
|
||||
<TextMessageRender :message="source" />
|
||||
</template>
|
||||
<template v-else-if="source.contentType === 102">
|
||||
<MediaMessageRender :message="source" />
|
||||
</template>
|
||||
<template v-else-if="source.contentType === 103">
|
||||
<VoiceMessageRender :source="source" :isSender="isSender" />
|
||||
</template>
|
||||
<template v-else-if="source.contentType === 104">
|
||||
<view style="color:#999">[暂未实现] 视频消息</view>
|
||||
</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 class="message_send_state_box">
|
||||
<view
|
||||
style="
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<view class="message_send_state">
|
||||
<u-loading-icon v-if="showSending && !isPreview" />
|
||||
<image
|
||||
v-if="isFailedMessage && !isPreview"
|
||||
src="@/static/images/chating_message_failed.png"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="message_content_wrap message_content_wrap_shadow" @longpress="handleLongPress">
|
||||
<template v-if="source.contentType === 101">
|
||||
<TextMessageRender :message="source" />
|
||||
</template>
|
||||
<template v-else-if="source.contentType === 102">
|
||||
<MediaMessageRender :message="source" />
|
||||
</template>
|
||||
<template v-else-if="source.contentType === 103">
|
||||
<VoiceMessageRender :source="source" :isSender="isSender" />
|
||||
</template>
|
||||
<template v-else-if="source.contentType === 104">
|
||||
<view style="color:#999">[暂未实现] 视频消息</view>
|
||||
</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
|
||||
v-else
|
||||
class="notice_message_container"
|
||||
:id="`auchor${source.clientMsgID}`"
|
||||
>
|
||||
<text>{{ getNoticeContent }}</text>
|
||||
<view
|
||||
v-else
|
||||
class="notice_message_container"
|
||||
:id="`auchor${source.clientMsgID}`"
|
||||
>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
@@ -94,8 +111,11 @@ import MediaMessageRender from "./MediaMessageRender.vue";
|
||||
import ErrorMessageRender from "./ErrorMessageRender.vue";
|
||||
import VoiceMessageRender from './VoiceMessageRender.vue'
|
||||
import FileMessageRender from './FileMessageRender.vue'
|
||||
import uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue';
|
||||
|
||||
import { noticeMessageTypes } from "@/constant";
|
||||
import { tipMessaggeFormat, formatMessageTime } from "@/util/imCommon";
|
||||
import { revokeMessage, canRevokeMessage } from "@/util/revokeMessage";
|
||||
|
||||
const textRenderTypes = [MessageType.TextMessage];
|
||||
|
||||
@@ -108,7 +128,8 @@ export default {
|
||||
MediaMessageRender,
|
||||
ErrorMessageRender,
|
||||
VoiceMessageRender,
|
||||
FileMessageRender
|
||||
FileMessageRender,
|
||||
uniPopup,
|
||||
},
|
||||
props: {
|
||||
source: Object,
|
||||
@@ -120,12 +141,17 @@ export default {
|
||||
isActive: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
return {
|
||||
sendingDelay: true,
|
||||
showActionMenu: false,
|
||||
actionMenuItems: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
"storeCurrentConversation",
|
||||
"storeSelfInfo",
|
||||
"storeHistoryMessageList",
|
||||
]),
|
||||
isSingle() {
|
||||
return (
|
||||
@@ -169,6 +195,14 @@ export default {
|
||||
this.$emit("messageItemRender", this.source.clientMsgID);
|
||||
this.setSendingDelay();
|
||||
},
|
||||
watch: {
|
||||
source: {
|
||||
handler(newVal, oldVal) {
|
||||
console.log('MessageItem source changed:', { newVal, oldVal });
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setSendingDelay() {
|
||||
if (this.source && this.source.status === MessageStatus.Sending) {
|
||||
@@ -177,6 +211,119 @@ export default {
|
||||
}, 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>
|
||||
@@ -339,4 +486,38 @@ export default {
|
||||
.fade-enter {
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user