1. 效果图
1.1 消息靠上接近导航栏,菜单显在消息体下方弹出,箭头向上
1.2 消息体没有贴近上方导航栏,菜单在消息体上方弹出,箭头向下
1.3 长消息,菜单在手指按下的位置弹出,无箭头
2. 代码实现
<view class="message-box" :id="'messageBox' + item.msgUID">
<view :class="['message-popup-box', elementPosition == 'nearTop' ? 'right-up-box' : elementPosition == 'nearBottom' ? 'right-down-box' : '']" :style="elementPosition == 'overstep' ? computedRightMenuStyle : ''" v-if="item.messagePopup">
<view class="message-operate">
<view class="operate-item" v-if="item.messageType == 'TxtMsg'" @click.stop="copyMessage(item, index)">
<image src="https://img.yiqitogether.com/yyqc/20231221/upload_o261zsurq4k7fugtfa3u2uvihq1ta4uo.png" mode="aspectFill" alt="" class="operate-item-icon"></image>
<view class="operate-item-txt">复制</view>
</view>
<view class="operate-item" v-if="item.messageType == 'TxtMsg' || item.messageType == 'ImgMsg' || item.messageType == 'ReferenceMsg'" @click.stop="referenceMessage(item, index)">
<image src="https://img.yiqitogether.com/yyqc/20231221/upload_60riqndcd93gnl4eiy8gejopf6xm9z48.png" mode="aspectFill" alt="" class="operate-item-icon"></image>
<view class="operate-item-txt">回复</view>
</view>
<view class="operate-item" v-if="isRecall" @click.stop="recallMessage(item, index)">
<image src="https://img.yiqitogether.com/yyqc/20231221/upload_dh3xhatlcjchcbh2blbke9c453sv580k.png" mode="aspectFill" alt="" class="operate-item-icon"></image>
<view class="operate-item-txt">撤回</view>
</view>
<view class="operate-item" v-if="!isRecall" @click.stop="deleteMessage(item, index)">
<image src="http://img.yiqitogether.com/static/images/messageGray/icon_delete_white.png" mode="aspectFill" alt="" class="operate-item-icon"></image>
<view class="operate-item-txt">删除</view>
</view>
<view class="operate-item" @click.stop="multipleChoice(item, index)">
<image src="https://img.yiqitogether.com/yyqc/20240222/upload_qegsjb8mwt6tr6nzp2yv7bmn4c03cn4x.png" mode="aspectFill" alt="" class="operate-item-icon"></image>
<view class="operate-item-txt">多选</view>
</view>
</view>
</view>
</view>
computed: {
computedRightMenuStyle() {
if (this.elementPosition == 'overstep') {
return { top: `${this.elTop}px`, right: 0 }
}
}
}
handleLongPress(e, item, index) {
if (this.isMultipleChoice) {
return
}
let currClientY = e.changedTouches[0].clientY
const query = uni.createSelectorQuery().in(this)
query
.select(`#messageBox${item.msgUID}`)
.boundingClientRect(res => {
if (res.top > 180) {
this.elementPosition = 'nearBottom'
} else {
if (res.height > this.windowSize.height / 2) {
if (currClientY < 200) {
this.elTop = -res.top + currClientY
} else {
this.elTop = -res.top + currClientY - 60
}
this.elementPosition = 'overstep'
} else {
this.elementPosition = 'nearTop'
}
}
})
.exec()
this.isRecall = false
if (this.messageItem) {
this.messageItem.messagePopup = false
}
this.messageItem = item
this.messageList[index].messagePopup = true
this.$forceUpdate()
let now = +new Date()
let recalTime = item.sendDate
let timediff = Math.abs(now - recalTime) / 1000
if (timediff <= 120) {
this.isRecall = true
} else {
this.isRecall = false
}
}
.message-popup-box {
position: absolute;
z-index: 99;
background: #595a5a;
border-radius: 16rpx;
.message-operate {
display: flex;
box-sizing: border-box;
padding: 16rpx;
.operate-item {
padding: 0 20rpx;
.operate-item-icon {
display: block;
width: 44rpx;
height: 44rpx;
background-size: cover;
}
.operate-item-txt {
font-size: 24rpx;
text-align: center;
color: #ffffff;
margin-top: 4rpx;
}
}
}
}
.right-up-box {
right: 0;
bottom: -78rpx;
}
.right-down-box {
right: 0;
top: -120rpx;
}
.right-up-box:before {
content: '';
position: absolute;
bottom: 98%;
right: 120rpx;
border: 16rpx solid transparent;
border-bottom: 16rpx solid #595a5a;
}
.right-down-box::before {
content: '';
position: absolute;
top: 98%;
right: 120rpx;
border: 16rpx solid transparent;
border-top: 16rpx solid #595a5a;
}