Files
yudao-ui-admin-vue3/src/views/im/Message/components/messageList/index.vue
2024-04-28 22:02:38 +08:00

309 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { formatDate } from '@/utils/formatTime'
/* 默认头像 */
import defaultAvatar from '@/assets/imgs/avatar.gif'
/* emits */
const emit = defineEmits(['scroll-message-list', 're-edit-message', 'message-quote'])
const messageData = ref([
{
id: 1,
type: 'text',
isRecall: false,
time: '2024-04-01 12:00:00',
from: '1',
msg: 'Hello, world!',
modifiedInfo: {
operationCount: 1
}
},
{
id: 2,
type: 'text',
isRecall: false,
time: '2024-04-01 12:00:01',
from: '2',
msg: 'Hi, there!',
modifiedInfo: {
operationCount: 0
}
},
{
id: 3,
type: 'text',
isRecall: true,
time: '2024-04-01 12:00:02',
from: '1',
msg: 'Hello, world!',
modifiedInfo: {
operationCount: 0
}
}
])
const ALL_MESSAGE_TYPE = {
TEXT: 'txt',
IMAGE: 'img',
AUDIO: 'audio',
LOCAL: 'loc',
VIDEO: 'video',
FILE: 'file',
CUSTOM: 'custom',
CMD: 'cmd',
INFORM: 'inform' //这个类型不在环信消息类型内,属于自己定义的一种系统通知类的消息。
}
/* 处理时间显示间隔 */
const handleMsgTimeShow = (time, index) => {
const msgList = Array.from(messageData.value)
if (index !== 0) {
const lastTime = msgList[index - 1].time
return time - lastTime > 50000 ? formatDate(time, 'MM/DD/HH:mm') : false
} else {
return formatDate(time, 'MM/DD/HH:mm')
}
}
/* computed-- 消息来源是否为自己 */
const isMyself = (msgBody) => {
return msgBody.from === '1'
}
/* 获取自己的用户信息 */
const loginUserInfo = {
avatarurl: 'https://avatars.githubusercontent.com/u/1?v=4'
}
/* 获取他人的用户信息 */
const otherUserInfo = (from) => {
return {
avatarurl: 'https://avatars.githubusercontent.com/u/2?v=4'
}
}
//处理聊天对方昵称展示
const handleNickName = (from) => {
return from === '1' ? '我' : '对方'
}
//引用消息
let clickQuoteMsgId = ref('')
//音频播放状态
const audioPlayStatus = reactive({
isPlaying: false, //是否在播放中
playMsgId: '' //在播放的音频消息id,
})
//开始播放
const startplayAudio = (msgBody) => {
const armRec = new BenzAMRRecorder()
const src = msgBody.url
audioPlayStatus.playMsgId = msgBody.id
console.log('>>>>>开始播放音频', msgBody.url)
//初始化音频源并调用播放
armRec.initWithUrl(src).then(() => {
if (!audioPlayStatus.isPlaying) {
armRec.play()
}
})
//播放开始监听
armRec.onPlay(() => {
audioPlayStatus.isPlaying = true
audioPlayStatus.playMsgId = msgBody.id
})
//播放结束监听
armRec.onStop(() => {
audioPlayStatus.isPlaying = false
audioPlayStatus.playMsgId = ''
})
}
//父组件重新编辑方法
const reEdit = (msg) => emit('reEditMessage', msg)
//调用父组件引用消息
const onMsgQuote = (msg) => emit('messageQuote', msg)
</script>
<template>
<div>
<div
class="messageList_box"
v-for="(msgBody, index) in messageData"
:key="msgBody.id"
:data-mid="msgBody.id"
>
<!-- 普通消息气泡 -->
<div
v-if="!msgBody.isRecall && msgBody.type !== ALL_MESSAGE_TYPE.INFORM"
class="message_box_item"
:style="{
flexDirection: isMyself(msgBody) ? 'row-reverse' : 'row'
}"
>
<div class="message_item_time">
{{ handleMsgTimeShow(msgBody.time, index) || '' }}
</div>
<el-avatar
class="message_item_avator"
:src="
isMyself(msgBody)
? loginUserInfo.avatarurl
: otherUserInfo(msgBody.from).avatarurl || defaultAvatar
"
/>
<!-- 普通消息内容 -->
<div class="message_box_card">
<span v-show="!isMyself(msgBody)" class="message_box_nickname">{{
handleNickName(msgBody.from)
}}</span>
<el-dropdown
class="message_box_content"
:class="[
isMyself(msgBody) ? 'message_box_content_mine' : 'message_box_content_other',
clickQuoteMsgId === msgBody.id && 'quote_msg_avtive'
]"
trigger="contextmenu"
placement="bottom-end"
>
<!-- 文本类型消息 -->
<p
style="padding: 10px; line-height: 20px"
v-if="msgBody.type === ALL_MESSAGE_TYPE.TEXT"
>
<template v-if="!isLink(msgBody.msg)">
{{ msgBody.msg }}
<!-- 已编辑 -->
<sup
style="font-size: 7px; color: #707784"
v-show="msgBody?.modifiedInfo?.operationCount"
>已编辑</sup
>
</template>
<template v-else> <span v-html="paseLink(msgBody.msg).msg"> </span></template>
</p>
<!-- 图片类型消息 -->
<!-- <div> -->
<el-image
v-if="msgBody.type === ALL_MESSAGE_TYPE.IMAGE"
style="border-radius: 5px"
:src="msgBody.thumb"
:preview-src-list="[msgBody.url]"
:initial-index="1"
fit="cover"
/>
<!-- </div> -->
<!-- 语音类型消息 -->
<div
:class="[
'message_box_content_audio',
isMyself(msgBody)
? 'message_box_content_audio_mine'
: 'message_box_content_audio_other'
]"
v-if="msgBody.type === ALL_MESSAGE_TYPE.AUDIO"
@click="startplayAudio(msgBody)"
:style="`width:${msgBody.length * 10}px`"
>
<span class="audio_length_text"> {{ msgBody.length }} </span>
<div
:class="[
isMyself(msgBody) ? 'play_audio_icon_mine' : 'play_audio_icon_other',
audioPlayStatus.playMsgId === msgBody.id && 'start_play_audio'
]"
style="background-size: 100% 100%"
></div>
</div>
<div v-if="msgBody.type === ALL_MESSAGE_TYPE.LOCAL">
<p style="padding: 10px">[暂不支持位置消息展示]</p>
</div>
<!-- 文件类型消息 -->
<div v-if="msgBody.type === ALL_MESSAGE_TYPE.FILE" class="message_box_content_file">
<div class="file_text_box">
<div class="file_name">
{{ msgBody.filename }}
</div>
<div class="file_size">
{{ fileSizeFormat(msgBody.file_length) }}
</div>
<a class="file_download" :href="msgBody.url" download>点击下载</a>
</div>
<span class="iconfont icon-wenjian"></span>
</div>
<!-- 自定义类型消息 -->
<div v-if="msgBody.type === ALL_MESSAGE_TYPE.CUSTOM" class="message_box_content_custom">
<template v-if="msgBody.customEvent && CUSTOM_TYPE[msgBody.customEvent]">
<div class="user_card">
<div class="user_card_main">
<!-- 头像 -->
<el-avatar
shape="circle"
:size="50"
:src="
(msgBody.customExts && msgBody.customExts.avatarurl) ||
msgBody.customExts.avatar ||
defaultAvatar
"
fit="cover"
/>
<!-- 昵称 -->
<span class="nickname">{{
(msgBody.customExts && msgBody.customExts.nickname) || msgBody.customExts.uid
}}</span>
</div>
<el-divider style="margin: 5px 0; border-top: 1px solid black" />
<p style="font-size: 8px">个人名片</p>
</div>
</template>
</div>
<!-- 右键点击弹起更多功能栏 -->
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-if="msgBody.type === ALL_MESSAGE_TYPE.TEXT && isSupported"
@click="copyTextMessages(msgBody.msg)"
>
复制
</el-dropdown-item>
<el-dropdown-item v-if="isMyself(msgBody)" @click="recallMessage(msgBody)">
撤回
</el-dropdown-item>
<el-dropdown-item
v-if="msgBody.type === ALL_MESSAGE_TYPE.TEXT && isMyself(msgBody)"
@click="showModifyMsgModal(msgBody)"
>
编辑
</el-dropdown-item>
<el-dropdown-item @click="onMsgQuote(msgBody)"> 引用 </el-dropdown-item>
<el-dropdown-item @click="deleteMessage(msgBody)"> 删除 </el-dropdown-item>
<el-dropdown-item v-if="!isMyself(msgBody)" @click="informOnMessage(msgBody)">
举报
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- 引用消息展示框 -->
<div
class="message_quote_box"
v-if="msgBody?.ext?.msgQuote"
@click="clickQuoteMessage(msgBody.ext.msgQuote)"
>
<p>
{{ msgBody?.ext?.msgQuote?.msgSender }}{{ msgBody?.ext?.msgQuote?.msgPreview }}
</p>
</div>
</div>
</div>
<!-- 撤回消息通知通知 -->
<div v-if="msgBody.isRecall" class="recall_style">
{{ isMyself(msgBody) ? '' : `${msgBody.from}` }}撤回了一条消息<span
class="reEdit"
v-show="isMyself(msgBody) && msgBody.type === ALL_MESSAGE_TYPE.TEXT"
@click="reEdit(msgBody.msg)"
>重新编辑</span
>
</div>
<!-- 灰色系统通知 -->
<div v-if="msgBody.type === ALL_MESSAGE_TYPE.INFORM" class="inform_style">
<p>
{{ msgBody.msg }}
</p>
</div>
</div>
<ReportMessage ref="reportMessage" />
<ModifyMessage ref="modifyMessageRef" />
</div>
</template>
<style lang="scss" scoped>
@import './index.scss';
</style>