Files
im/pages/conversation/chating/components/MessageItem/index.vue
T

426 lines
10 KiB
Vue
Raw Normal View History

2025-11-07 09:56:20 +08:00
<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" @click="viewDetail" />
<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 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>
2025-12-05 16:10:52 +08:00
<view class="message_content_wrap message_content_wrap_shadow" :id="`message_content_wrap_${source.clientMsgID}`" @longtap.stop.prevent="longtapEvent($event)">
2025-11-07 09:56:20 +08:00
<text-message-render v-if="showTextRender" :message="source" />
<media-message-render v-else-if="showMediaRender" :message="source" />
<error-message-render v-else />
</view>
</view>
</view>
2025-12-02 03:05:52 +08:00
<!-- 长按菜单 -->
<chunLei-popups v-model="toolTipFlag" :popData="toolTipData" @tapPopup="tapPopup"
:x="toolTipX" :y="toolTipY" :placement="popPostion"
2025-12-05 16:10:52 +08:00
:triangle="false"
2025-12-02 03:05:52 +08:00
direction="row" theme="dark" :dynamic="true">
</chunLei-popups>
2025-11-07 09:56:20 +08:00
</view>
2025-12-05 16:10:52 +08:00
<view v-else class="notice_message_container" style="margin: 0 auto;" :id="`auchor${source.clientMsgID}`">
2025-11-07 09:56:20 +08:00
<text>{{ getNoticeContent }}</text>
</view>
</template>
<script>
2025-12-02 03:05:52 +08:00
import {mapGetters} from "vuex";
import {MessageStatus,MessageType,SessionType,} from "openim-uniapp-polyfill";
import chunLeiPopups from "@/components/chunLei-popups.vue";
2025-11-07 09:56:20 +08:00
import MyAvatar from "@/components/MyAvatar/index.vue";
import TextMessageRender from "./TextMessageRender.vue";
import MediaMessageRender from "./MediaMessageRender.vue";
import ErrorMessageRender from "./ErrorMessageRender.vue";
2025-12-02 03:05:52 +08:00
import {noticeMessageTypes} from "@/constant";
import {tipMessaggeFormat,formatMessageTime} from "@/util/imCommon";
2025-11-07 09:56:20 +08:00
const textRenderTypes = [MessageType.TextMessage];
const mediaRenderTypes = [MessageType.PictureMessage];
export default {
components: {
MyAvatar,
TextMessageRender,
MediaMessageRender,
ErrorMessageRender,
2025-12-02 03:05:52 +08:00
chunLeiPopups
2025-11-07 09:56:20 +08:00
},
props: {
source: Object,
isSender: {
type: Boolean,
default: false,
},
isPreview: Boolean,
isActive: Boolean,
},
data() {
2025-12-02 03:05:52 +08:00
return {
toolTipX: 0,
toolTipY: 0,
toolTipFlag: false,
popPostion:"default",
toolTipData: [],
};
2025-11-07 09:56:20 +08:00
},
computed: {
...mapGetters([
"storeCurrentConversation",
"storeSelfInfo",
]),
isSingle() {
return (
this.storeCurrentConversation.conversationType === SessionType.Single
);
},
formattedMessageTime() {
return formatMessageTime(this.source.sendTime);
},
showTextRender() {
return textRenderTypes.includes(this.source.contentType);
},
showMediaRender() {
return mediaRenderTypes.includes(this.source.contentType);
},
getNoticeContent() {
const isNoticeMessage = noticeMessageTypes.includes(
this.source.contentType
);
return !isNoticeMessage ?
"" :
tipMessaggeFormat(
this.source,
this.$store.getters.storeCurrentUserID
);
},
isSuccessMessage() {
return this.source.status === MessageStatus.Succeed;
},
isFailedMessage() {
return this.source.status === MessageStatus.Failed;
},
showSending() {
return this.source.status === MessageStatus.Sending && !this.sendingDelay;
},
},
mounted() {
this.$emit("messageItemRender", this.source.clientMsgID);
this.setSendingDelay();
},
methods: {
viewDetail(){
if(this.isSender){
}else{
uni.navigateTo({
url:"/pages/common/userCard/index?sourceID="+this.source.sendID
})
}
},
setSendingDelay() {
if (this.source.status === MessageStatus.Sending) {
setTimeout(() => {
this.sendingDelay = false;
}, 2000);
}
},
2025-12-05 16:10:52 +08:00
longtapEvent(e){
console.log('longtapEvent');
// console.log("#"+e.currentTarget.id,e);
// let view = uni.createSelectorQuery().in(this).select("#"+e.currentTarget.id.replace('message_content_wrap_','auchor'));
// console.log(e.touches[0].clientX,e.touches[0].clientY);
// view.boundingClientRect((data) => {
// console.log(data.left,data.top);
// //console.log("得到布局位置信息" + JSON.stringify(data));
// }).exec();
this.$emit('userEvent',{type:"longtapMsgContent"},this.source);
//this.showToolTip(e);
2025-12-02 03:05:52 +08:00
},
//操作项
showToolTip: function(e) {
let that = this;
that.toolTipData=[
{
id: 1,
icon: '/static/images/chat/longTipIcon/copy.png',
title: '复制',
disabled: false
},
{
id: 4,
icon: '/static/images/chat/longTipIcon/zhuanfa.png',
title: '转发',
disabled: false
},
{
2025-12-05 16:10:52 +08:00
id: 6,
icon: '/static/images/chat/longTipIcon/select.png',
title: '多选',
2025-12-02 03:05:52 +08:00
disabled: false
2025-12-05 16:10:52 +08:00
},
{
id: 7,
icon: '/static/images/chat/longTipIcon/delete.png',
title: '删除',
disabled: false
},
// {
// id: 5,
// icon: '/static/images/chat/longTipIcon/tag.png',
// title: '编辑',
// disabled: false
// }
2025-12-02 03:05:52 +08:00
];
that.toolTipX =e.touches[0].clientX;
that.toolTipY = e.touches[0].clientY-20;
that.toolTipFlag = !that.toolTipFlag;
if(that.toolTipFlag==true){
let nowTime=new Date().getTime();
let msgTime=that.source.createTime;
let diff= nowTime-msgTime;
if(this.isSender&&diff<120000){
that.toolTipData.push({
id: 3,
icon: '/static/images/chat/longTipIcon/revert.png',
title: '撤回',
disabled: false
})
}
}
},
tapPopup(e) {
let that = this;
let currentHandleMsg=that.source;
2025-12-05 16:10:52 +08:00
console.log(currentHandleMsg);
2025-12-02 03:05:52 +08:00
if (e.title == "撤回") {
that.toolTipFlag = false;
2025-12-05 16:10:52 +08:00
that.$emit('userEvent',{type:'revokeMessage'},that.source);
2025-12-02 03:05:52 +08:00
}
if (e.title == "复制") {
2025-12-05 16:10:52 +08:00
let content = currentHandleMsg.textElem?.content;
2025-12-02 03:05:52 +08:00
if (content) {
let copyContent =content.text;
2025-12-05 16:10:52 +08:00
let formatStr = copyContent;
//let formatStr = this.replaceReseverEmoji(copyContent);
uni.setClipboardData({
data:formatStr,
2025-12-02 03:05:52 +08:00
success: (res) => {
uni.showToast({
title: res,
icon: 'none'
})
},
error: (e) => {
uni.showToast({
title: e,
icon: 'none',
duration: 3000,
})
}
})
}
that.toolTipFlag = false;
return;
}
if (e.title == "转发") {
2025-12-05 16:10:52 +08:00
that.$emit('userEvent',{type:'forward'},that.source);
2025-12-02 03:05:52 +08:00
that.toolTipFlag = false;
return
}
if(e.title=="调换"){
that.toolTipFlag = false;
return;
}
if (e.title == "多选") {
2025-12-05 16:10:52 +08:00
that.$emit('userEvent',{type:'select'},that.source);
2025-12-02 03:05:52 +08:00
that.toolTipFlag = false;
return
}
2025-12-05 16:10:52 +08:00
if (e.title == "删除") {
2025-12-02 03:05:52 +08:00
that.toolTipFlag = false;
2025-12-05 16:10:52 +08:00
that.$emit('userEvent',{type:'deleteMsg'},that.source);
return
2025-12-02 03:05:52 +08:00
}
2025-12-05 16:10:52 +08:00
2025-12-02 03:05:52 +08:00
},
2025-11-07 09:56:20 +08:00
},
};
</script>
<style scoped lang="scss">
.message_item {
display: flex;
flex-direction: row;
padding: 16rpx 44rpx;
// padding-top: 48rpx;
position: relative;
.check_wrap {
@include centerBox();
box-sizing: border-box;
width: 40rpx;
min-width: 40rpx;
height: 40rpx;
min-height: 40rpx;
border: 2px solid #979797;
border-radius: 50%;
margin-top: 16rpx;
margin-right: 24rpx;
&_active {
background-color: #1d6bed;
border: none;
}
&_disabled {
background-color: #c8c9cc;
}
}
.message_container {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-left: 20rpx;
2025-12-05 16:10:52 +08:00
// text-align: flex-start;
2025-11-07 09:56:20 +08:00
max-width: 80%;
position: relative;
.message_sender {
@include nomalEllipsis();
display: flex;
max-width: 480rpx;
// font-size: 24rpx;
font-size: 0.85rem;
color: #666;
margin-bottom: 6rpx;
}
2025-12-02 03:05:52 +08:00
.message_content_wrap_shadow {
box-shadow: 0px 0px 2px rgba(0,0,0,0.1);
}
2025-11-07 09:56:20 +08:00
.message_content_wrap {
@include vCenterBox();
2025-12-05 16:10:52 +08:00
text-align: flex-start;
2025-11-07 09:56:20 +08:00
// font-size: 14px;
color: $uni-text-color;
width: fit-content;
2025-12-02 03:05:52 +08:00
word-wrap: break-word;
word-break: break-word;
2025-11-07 09:56:20 +08:00
.bg_container {
padding: 16rpx 24rpx;
2025-12-05 16:10:52 +08:00
border-radius: 12rpx;
2025-12-02 03:05:52 +08:00
background-color: #fff;
2025-11-07 09:56:20 +08:00
}
}
}
.message_send_state_box {
display: flex;
flex-direction: row-reverse;
}
.message_send_state {
@include centerBox();
margin-left: 12rpx;
// margin-top: 48rpx;
width: 48rpx;
height: 48rpx;
.read_limit_count {
// font-size: 24rpx;
font-size: 0.85rem;
color: #999;
}
image {
width: 16px;
height: 16px;
}
}
::v-deep.emoji_display {
width: 24px;
height: 18px;
vertical-align: sub;
}
&_self {
flex-direction: row-reverse;
.check_wrap {
margin-right: 0;
margin-left: 24rpx;
}
.message_container {
margin-left: 0;
margin-right: 20rpx;
// text-align: end;
align-items: flex-end;
2025-12-02 03:05:52 +08:00
.message_content_wrap_shadow {
box-shadow: 0px 0px 2px #95e261;
}
2025-11-07 09:56:20 +08:00
.message_content_wrap {
flex-direction: row-reverse;
.bg_container {
border-radius: 12rpx 0 12rpx 12rpx;
2025-12-02 03:05:52 +08:00
background-color: #94ec68 !important;
2025-11-07 09:56:20 +08:00
}
}
}
.message_send_state_box {
flex-direction: row;
}
.message_send_state {
margin-left: 0rpx;
margin-right: 12rpx;
}
}
&_active {
background-color: #fdf5e9;
}
}
.notice_message_container {
@include ellipsisWithLine(2);
text-align: center;
margin: 24rpx 48rpx;
// font-size: 24rpx;
font-size: 0.85rem;
color: #999;
position: relative;
}
.fade-leave,
.fade-enter-to {
opacity: 1;
}
.fade-leave-active,
.fade-enter-active {
transition: all 0.5s;
}
.fade-leave-to,
.fade-enter {
opacity: 0;
}
</style>