mirror of
https://gitee.com/lijingbo-2021/open-anylink-web.git
synced 2025-12-30 02:52:26 +00:00
多选模式1
This commit is contained in:
1
src/assets/svg/cancle.svg
Normal file
1
src/assets/svg/cancle.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1746605979357" class="svg-icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5945" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M850.538343 895.516744c-11.494799 0-22.988574-4.386914-31.763424-13.161764L141.103692 204.669426c-17.548678-17.534352-17.548678-45.992497 0-63.525825 17.548678-17.548678 45.977147-17.548678 63.525825 0l677.671227 677.685553c17.548678 17.534352 17.548678 45.992497 0 63.525825C873.526917 891.128807 862.032118 895.516744 850.538343 895.516744z" fill="#000000" p-id="5946"></path><path d="M172.867116 895.516744c-11.494799 0-22.988574-4.386914-31.763424-13.161764-17.548678-17.534352-17.548678-45.992497 0-63.525825l677.671227-677.685553c17.548678-17.548678 45.977147-17.548678 63.525825 0 17.548678 17.534352 17.548678 45.992497 0 63.525825L204.629517 882.354979C195.85569 891.128807 184.360891 895.516744 172.867116 895.516744z" fill="#000000" p-id="5947"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
src/assets/svg/forwardobo.svg
Normal file
1
src/assets/svg/forwardobo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1746604353367" class="svg-icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4613" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M933.686613 826.823111c0 33.28-26.908444 60.245333-60.131555 60.245333H151.350613a60.188444 60.188444 0 0 1-60.188444-60.245333H0.879502c-9.102222 93.809778 53.930667 150.641778 120.376889 150.641778h782.392889A120.433778 120.433778 0 0 0 1024.026169 856.974222v-180.679111h-90.339556v150.528zM978.91328 0H587.688391a45.169778 45.169778 0 0 0 0 90.282667h297.244445L446.490169 529.123556a45.169778 45.169778 0 0 0 63.829333 63.886222l423.367111-423.765334v267.434667a45.169778 45.169778 0 0 0 90.225778 0V45.169778a44.942222 44.942222 0 0 0-44.942222-45.169778z" fill="#000000" p-id="4614"></path><path d="M0.026169 102.4m42.666667 0l426.666666 0q42.666667 0 42.666667 42.666667l0 0q0 42.666667-42.666667 42.666666l-426.666666 0q-42.666667 0-42.666667-42.666666l0 0q0-42.666667 42.666667-42.666667Z" fill="#000000" p-id="4615"></path><path d="M0.026169 327.395556m42.666667 0l290.133333 0q42.666667 0 42.666667 42.666666l0 0q0 42.666667-42.666667 42.666667l-290.133333 0q-42.666667 0-42.666667-42.666667l0 0q0-42.666667 42.666667-42.666666Z" fill="#000000" p-id="4616"></path><path d="M0.026169 552.334222m42.666667 0l290.133333 0q42.666667 0 42.666667 42.666667l0 0q0 42.666667-42.666667 42.666667l-290.133333 0q-42.666667 0-42.666667-42.666667l0 0q0-42.666667 42.666667-42.666667Z" fill="#000000" p-id="4617"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -52,6 +52,7 @@ import MenuMsgMain from '@/views/message/components/MenuMsgMain.vue'
|
||||
import MessageGroupRightSide from '@/views/message/components/MessageGroupRightSide.vue'
|
||||
import HashNoData from '@/components/common/HasNoData.vue'
|
||||
import InputRecorder from '@/views/message/components/InputRecorder.vue'
|
||||
import InputMultiSelect from '@/views/message/components/InputMultiSelect.vue'
|
||||
import { playMsgSend } from '@/js/utils/audio'
|
||||
|
||||
const userData = useUserStore()
|
||||
@@ -255,6 +256,7 @@ const initSession = (sessionId) => {
|
||||
}
|
||||
isShowRecorder.value = false // 麦克风输入状态重置
|
||||
inputRecorderRef.value?.cancelSend() // 取消音频发送
|
||||
inputMultiSelectRef.value?.cancel() // 取消多选模式
|
||||
imageData.clearImageInSession(sessionId) // 清除待渲染的图片队列
|
||||
readAtMsgIds.value = []
|
||||
}
|
||||
@@ -1153,6 +1155,153 @@ const onSelectOprMenu = (label) => {
|
||||
}
|
||||
}
|
||||
|
||||
const inputMultiSelectRef = ref(null)
|
||||
const isMultiSelect = ref(false)
|
||||
const multiSelectedMsgIds = ref(new Set())
|
||||
const handleMsgItemSelect = (msgKey, selected) => {
|
||||
if (!isMultiSelect.value) {
|
||||
isMultiSelect.value = true
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
multiSelectedMsgIds.value.add(msgKey)
|
||||
} else {
|
||||
multiSelectedMsgIds.value.delete(msgKey)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancleMultiSelect = () => {
|
||||
isMultiSelect.value = false
|
||||
multiSelectedMsgIds.value.clear()
|
||||
}
|
||||
|
||||
// const toggleMultiSelect = () => {
|
||||
// isMultiSelect.value = !isMultiSelect.value
|
||||
// if (!isMultiSelect.value) {
|
||||
// multiSelectedMsgIds.value.clear()
|
||||
// }
|
||||
// }
|
||||
|
||||
// const handleDeleteMessages = async () => {
|
||||
// // 实现批量删除逻辑
|
||||
// }
|
||||
|
||||
// const handleForwardSelected = () => {
|
||||
// // 实现批量转发逻辑
|
||||
// }
|
||||
|
||||
// 选区相关状态
|
||||
const selection = ref({
|
||||
isSelecting: false,
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
currentX: 0,
|
||||
currentY: 0
|
||||
})
|
||||
|
||||
// 计算选区样式
|
||||
const selectionStyle = computed(() => {
|
||||
if (!selection.value.isSelecting) return { display: 'none' }
|
||||
|
||||
const left = Math.min(selection.value.startX, selection.value.currentX)
|
||||
const top = Math.min(selection.value.startY, selection.value.currentY)
|
||||
const width = Math.abs(selection.value.currentX - selection.value.startX)
|
||||
const height = Math.abs(selection.value.currentY - selection.value.startY)
|
||||
|
||||
return {
|
||||
display: 'block',
|
||||
left: `${left}px`,
|
||||
top: `${top}px`,
|
||||
width: `${width}px`,
|
||||
height: `${height}px`
|
||||
}
|
||||
})
|
||||
|
||||
// 处理鼠标按下
|
||||
const handleMouseDown = (e) => {
|
||||
if (e.button !== 0) return // 如果不是左键则返回
|
||||
|
||||
const rect = msgListDiv.value.getBoundingClientRect()
|
||||
selection.value.isSelecting = true
|
||||
selection.value.startX = e.clientX - rect.left
|
||||
selection.value.startY = e.clientY - rect.top
|
||||
selection.value.currentX = selection.value.startX
|
||||
selection.value.currentY = selection.value.startY
|
||||
|
||||
// 添加全局监听
|
||||
document.addEventListener('mousemove', handleGlobalMouseMove)
|
||||
document.addEventListener('mouseup', handleGlobalMouseUp)
|
||||
}
|
||||
|
||||
// 处理鼠标移动(节流处理)
|
||||
const handleGlobalMouseMove = (e) => {
|
||||
if (!selection.value.isSelecting) return
|
||||
|
||||
const rect = msgListDiv.value.getBoundingClientRect()
|
||||
selection.value.currentX = e.clientX - rect.left
|
||||
selection.value.currentY = e.clientY - rect.top
|
||||
}
|
||||
|
||||
// 处理鼠标释放
|
||||
const handleGlobalMouseUp = (e) => {
|
||||
// 只在鼠标左键释放时处理
|
||||
if (e.button !== 0) return
|
||||
|
||||
document.removeEventListener('mousemove', handleGlobalMouseMove)
|
||||
document.removeEventListener('mouseup', handleGlobalMouseUp)
|
||||
|
||||
if (
|
||||
!selection.value.isSelecting ||
|
||||
(selection.value.isSelecting &&
|
||||
selection.value.currentX === selection.value.startX &&
|
||||
selection.value.currentY === selection.value.startY)
|
||||
) {
|
||||
selection.value.isSelecting = false
|
||||
return
|
||||
}
|
||||
|
||||
selection.value.isSelecting = false
|
||||
|
||||
// 检测选区内的消息项
|
||||
const selectionRect = {
|
||||
left: Math.min(selection.value.startX, selection.value.currentX),
|
||||
top: Math.min(selection.value.startY, selection.value.currentY),
|
||||
right: Math.max(selection.value.startX, selection.value.currentX),
|
||||
bottom: Math.max(selection.value.startY, selection.value.currentY)
|
||||
}
|
||||
|
||||
const rect = msgListDiv.value.getBoundingClientRect()
|
||||
msgListDiv.value.querySelectorAll('.message-item').forEach((el) => {
|
||||
const itemRect = el.getBoundingClientRect()
|
||||
const itemLeft = itemRect.left - rect.left
|
||||
const itemTop = itemRect.top - rect.top
|
||||
const itemRight = itemRect.right - rect.left
|
||||
const itemBottom = itemRect.bottom - rect.top
|
||||
|
||||
const isIntersect = !(
|
||||
itemBottom < selectionRect.top ||
|
||||
itemTop > selectionRect.bottom ||
|
||||
itemRight < selectionRect.left ||
|
||||
itemLeft > selectionRect.right
|
||||
)
|
||||
|
||||
if (isIntersect) {
|
||||
if (!isMultiSelect.value) {
|
||||
isMultiSelect.value = true
|
||||
}
|
||||
|
||||
const msgId = el.dataset.msgId
|
||||
const isRecording = el.dataset.isRecording
|
||||
if (multiSelectedMsgIds.value.has(msgId)) {
|
||||
multiSelectedMsgIds.value.delete(msgId)
|
||||
} else if (isRecording !== 'true') {
|
||||
// 语音消息不能被选中
|
||||
multiSelectedMsgIds.value.add(msgId)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const isShowForwardMsgDialog = ref(false)
|
||||
let forwardMsg = {} // 待转发的消息
|
||||
const sessionListSortedKey = computed(() => {
|
||||
@@ -1429,7 +1578,9 @@ const onShowRecorder = () => {
|
||||
class="message-main my-scrollbar"
|
||||
ref="msgListDiv"
|
||||
@wheel="handleMsgListWheel"
|
||||
@mousedown="handleMouseDown"
|
||||
>
|
||||
<div class="selection-box" :style="selectionStyle"></div>
|
||||
<MenuMsgMain @selectMenu="onSelectMsgMainMenu">
|
||||
<MessageItem
|
||||
v-for="item in msgKeysShow"
|
||||
@@ -1447,6 +1598,8 @@ const onShowRecorder = () => {
|
||||
:hasNoMoreMsg="hasNoMoreMsg"
|
||||
:isLoadMoreLoading="selectedSessionCache[selectedSessionId]?.isLoadMoreLoading"
|
||||
:inputEditorRef="inputEditorRef"
|
||||
:isMultiSelect="isMultiSelect"
|
||||
:isSelected="multiSelectedMsgIds.has(item)"
|
||||
@loadMore="onLoadMore"
|
||||
@showUserCard="onShowUserCard"
|
||||
@showGroupCard="onShowGroupCard"
|
||||
@@ -1454,6 +1607,7 @@ const onShowRecorder = () => {
|
||||
@loadFinished="updateScroll"
|
||||
@showHighlight="handleShowHighlight"
|
||||
@forwardMsg="showForwardMsgDialog"
|
||||
@select="handleMsgItemSelect"
|
||||
></MessageItem>
|
||||
</MenuMsgMain>
|
||||
</div>
|
||||
@@ -1495,7 +1649,14 @@ const onShowRecorder = () => {
|
||||
</transition>
|
||||
</div>
|
||||
<div class="input-box bdr-t" :style="{ height: inputBoxHeight + 'px' }">
|
||||
<el-container v-if="isShowRecorder">
|
||||
<el-container v-if="isMultiSelect">
|
||||
<InputMultiSelect
|
||||
ref="inputMultiSelectRef"
|
||||
:selectedCount="multiSelectedMsgIds.size"
|
||||
@exit="handleCancleMultiSelect"
|
||||
></InputMultiSelect>
|
||||
</el-container>
|
||||
<el-container v-else-if="isShowRecorder">
|
||||
<InputRecorder
|
||||
ref="inputRecorderRef"
|
||||
@exit="isShowRecorder = false"
|
||||
@@ -1749,6 +1910,13 @@ const onShowRecorder = () => {
|
||||
padding: 10px;
|
||||
overflow-y: scroll; // 用它的滚动条
|
||||
|
||||
.selection-box {
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.message-item {
|
||||
transition: all 1s ease;
|
||||
}
|
||||
|
||||
109
src/views/message/components/InputMultiSelect.vue
Normal file
109
src/views/message/components/InputMultiSelect.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<script setup>
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import ForwardIcon from '@/assets/svg/forward.svg'
|
||||
import ForwardoboIcon from '@/assets/svg/forwardobo.svg'
|
||||
import DeletemsgIcon from '@/assets/svg/deletemsg.svg'
|
||||
import CancleIcon from '@/assets/svg/cancle.svg'
|
||||
|
||||
const props = defineProps(['selectedCount'])
|
||||
const emit = defineEmits(['exit', 'forwardTogether', 'forwardOneByOne', 'batchDelete'])
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
if (event.key === 'Escape') {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
emit('exit')
|
||||
}
|
||||
|
||||
defineExpose({ cancel })
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keydown', handleKeyDown)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="input-multi-select">
|
||||
<span class="selected-count">已选中:{{ props.selectedCount || 0 }}条消息</span>
|
||||
<div class="multi-select-funtions">
|
||||
<div class="function-item">
|
||||
<div class="fun-icon">
|
||||
<ForwardIcon></ForwardIcon>
|
||||
</div>
|
||||
<span>合并转发</span>
|
||||
</div>
|
||||
<div class="function-item">
|
||||
<div class="fun-icon">
|
||||
<ForwardoboIcon style="width: 20px; height: 20px"></ForwardoboIcon>
|
||||
</div>
|
||||
<span>逐条转发</span>
|
||||
</div>
|
||||
<div class="function-item">
|
||||
<div class="fun-icon">
|
||||
<DeletemsgIcon></DeletemsgIcon>
|
||||
</div>
|
||||
<span>批量删除</span>
|
||||
</div>
|
||||
<div class="function-item">
|
||||
<div class="fun-icon" @click="cancel">
|
||||
<CancleIcon></CancleIcon>
|
||||
</div>
|
||||
<span>取消</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.input-multi-select {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
font-size: 14px;
|
||||
color: gray;
|
||||
|
||||
.multi-select-funtions {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
|
||||
.function-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
.fun-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
|
||||
&:hover {
|
||||
background-color: #409eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
// fill: gray;
|
||||
}
|
||||
</style>
|
||||
@@ -36,7 +36,9 @@ const props = defineProps([
|
||||
'lastMsgId',
|
||||
'hasNoMoreMsg',
|
||||
'isLoadMoreLoading',
|
||||
'inputEditorRef'
|
||||
'inputEditorRef',
|
||||
'isMultiSelect',
|
||||
'isSelected'
|
||||
])
|
||||
const emit = defineEmits([
|
||||
'loadMore',
|
||||
@@ -45,7 +47,8 @@ const emit = defineEmits([
|
||||
'resendMsg',
|
||||
'loadFinished',
|
||||
'showHighlight',
|
||||
'forwardMsg'
|
||||
'forwardMsg',
|
||||
'select'
|
||||
])
|
||||
|
||||
const userData = useUserStore()
|
||||
@@ -345,6 +348,21 @@ const renderDocument = (content) => {
|
||||
}
|
||||
}
|
||||
|
||||
const contentType = computed(() => {
|
||||
const contentJson = jsonParseSafe(msg.value.content)
|
||||
if (!contentJson) {
|
||||
return msgContentType.MIX
|
||||
}
|
||||
|
||||
const type = contentJson['type']
|
||||
const value = contentJson['value']
|
||||
if (!type || !value) {
|
||||
return msgContentType.MIX
|
||||
} else {
|
||||
return type
|
||||
}
|
||||
})
|
||||
|
||||
const msg = computed(() => {
|
||||
return messageData.getMsg(props.sessionId, props.msgKey)
|
||||
})
|
||||
@@ -854,6 +872,9 @@ const onSelectMenuMsgItem = async (label) => {
|
||||
case 'forward':
|
||||
emit('forwardMsg', props.msgKey)
|
||||
break
|
||||
case 'multiSelect':
|
||||
emit('select', props.msgKey, true)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -876,132 +897,191 @@ watch(
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const handleItemClick = () => {
|
||||
if (props.isMultiSelect && contentType.value !== msgContentType.RECORDING) {
|
||||
emit('select', props.msgKey, !props.isSelected)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="message-item" :class="{ unreadMsg: isUnreadMsg }">
|
||||
<span v-if="isShowNoMoreMsg" class="no-more-message">当前无更多消息</span>
|
||||
<div v-if="isShowLoadMore" class="load-more-wrapper">
|
||||
<div
|
||||
class="load-more"
|
||||
v-loading="props.isLoadMoreLoading"
|
||||
@click="onLoadMore"
|
||||
:style="{ cursor: loadMoreCursor }"
|
||||
>
|
||||
{{ loadMoreTips }}
|
||||
</div>
|
||||
<div
|
||||
class="message-item-wrapper"
|
||||
:class="{
|
||||
'multi-select-mode': props.isMultiSelect,
|
||||
'is-selected': props.isSelected,
|
||||
'is-valid-option': props.isMultiSelect && contentType !== msgContentType.RECORDING
|
||||
}"
|
||||
@click="handleItemClick"
|
||||
>
|
||||
<div v-if="props.isMultiSelect" class="message-checkbox">
|
||||
<el-checkbox
|
||||
:model-value="props.isSelected"
|
||||
:disabled="contentType === msgContentType.RECORDING"
|
||||
@update:model-value="handleItemClick"
|
||||
@click.stop
|
||||
/>
|
||||
</div>
|
||||
<el-divider v-if="props.extend.isFirstNew" class="new-messages-tips" content-position="center"
|
||||
>以下是新消息</el-divider
|
||||
>
|
||||
<span v-if="!isContinuousSession" class="datetime">{{ sysShowTime }}</span>
|
||||
<div
|
||||
v-if="isSystemMsg"
|
||||
class="system-message"
|
||||
v-html="systemMsgContent"
|
||||
@click="onClickSystemMsg"
|
||||
></div>
|
||||
<div v-else-if="!isSystemMsg && isRevoke" class="revoke-delete">
|
||||
<div v-if="isSelf">
|
||||
<span>你撤回了一条消息</span>
|
||||
<span
|
||||
v-if="isReedit && !isReeditTimeOut"
|
||||
style="margin-left: 2px; color: #409eff; cursor: pointer"
|
||||
@click="handleReedit"
|
||||
class="message-item"
|
||||
:data-msg-id="props.msgKey"
|
||||
:data-is-recording="contentType === msgContentType.RECORDING"
|
||||
:class="{ unreadMsg: isUnreadMsg }"
|
||||
>
|
||||
<span v-if="isShowNoMoreMsg" class="no-more-message">当前无更多消息</span>
|
||||
<div v-if="isShowLoadMore" class="load-more-wrapper">
|
||||
<div
|
||||
class="load-more"
|
||||
v-loading="props.isLoadMoreLoading"
|
||||
@click="onLoadMore"
|
||||
:style="{ cursor: loadMoreCursor }"
|
||||
>
|
||||
重新编辑
|
||||
</span>
|
||||
{{ loadMoreTips }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="isChatMsgType">对方撤回了一条消息</div>
|
||||
<div v-else>{{ `“${objectInfoFromMsg.nickName}”撤回了一条消息` }}</div>
|
||||
<el-divider v-if="props.extend.isFirstNew" class="new-messages-tips" content-position="center"
|
||||
>以下是新消息</el-divider
|
||||
>
|
||||
<span v-if="!isContinuousSession" class="datetime">{{ sysShowTime }}</span>
|
||||
<div
|
||||
v-if="isSystemMsg"
|
||||
class="system-message"
|
||||
v-html="systemMsgContent"
|
||||
@click="onClickSystemMsg"
|
||||
></div>
|
||||
<div v-else-if="!isSystemMsg && isRevoke" class="revoke-delete">
|
||||
<div v-if="isSelf">
|
||||
<span>你撤回了一条消息</span>
|
||||
<span
|
||||
v-if="isReedit && !isReeditTimeOut"
|
||||
style="margin-left: 2px; color: #409eff; cursor: pointer"
|
||||
@click="handleReedit"
|
||||
>
|
||||
重新编辑
|
||||
</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="isChatMsgType">对方撤回了一条消息</div>
|
||||
<div v-else>{{ `“${objectInfoFromMsg.nickName}”撤回了一条消息` }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="!isSystemMsg && isDelete" class="revoke-delete">
|
||||
<span>消息已删除</span>
|
||||
</div>
|
||||
<div v-else class="message-container-wrapper">
|
||||
<el-container class="el-container-right" v-if="isSelf">
|
||||
<el-main class="el-main-right">
|
||||
<el-container class="message-content-wrapper">
|
||||
<el-header class="message-time">
|
||||
<span v-if="isGroupChatMsgType">{{ nickName }}</span>
|
||||
<span style="margin-left: 5px">{{ msgTime }}</span>
|
||||
</el-header>
|
||||
<el-main class="message-content">
|
||||
<div
|
||||
v-if="msgStatus === msgSendStatus.PENDING"
|
||||
class="my-message-status my-message-status-pending"
|
||||
>
|
||||
<div class="loading-circular" v-loading="true"></div>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="
|
||||
msgStatus === msgSendStatus.FAILED || msgStatus === msgSendStatus.UPLOAD_FAILED
|
||||
"
|
||||
class="my-message-status my-message-status-failed"
|
||||
>
|
||||
<el-icon
|
||||
color="red"
|
||||
:title="msgStatus === msgSendStatus.FAILED ? '点击重发' : ''"
|
||||
@click="onResendMsg"
|
||||
><WarningFilled
|
||||
/></el-icon>
|
||||
</div>
|
||||
<div v-else-if="isChatMsgType" class="my-message-status">
|
||||
<div v-if="myMsgIsRead" class="remote_read"></div>
|
||||
<div v-else class="remote_unread"></div>
|
||||
</div>
|
||||
<MenuMsgItem :msg="msg" @selectMenu="onSelectMenuMsgItem">
|
||||
<div class="div-content" :id="`div-content-${msg.msgId}`"></div>
|
||||
</MenuMsgItem>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
<el-aside class="el-aside-right">
|
||||
<UserAvatarIcon
|
||||
class="avatar-message-item"
|
||||
:showId="account"
|
||||
:showName="nickName"
|
||||
:showAvatarThumb="avatarThumb"
|
||||
@click="onShowUserCard"
|
||||
></UserAvatarIcon>
|
||||
</el-aside>
|
||||
</el-container>
|
||||
<div v-else-if="!isSystemMsg && isDelete" class="revoke-delete">
|
||||
<span>消息已删除</span>
|
||||
</div>
|
||||
<div v-else class="message-container-wrapper">
|
||||
<el-container class="el-container-right" v-if="isSelf">
|
||||
<el-main class="el-main-right">
|
||||
<el-container class="message-content-wrapper">
|
||||
<el-header class="message-time">
|
||||
<span v-if="isGroupChatMsgType">{{ nickName }}</span>
|
||||
<span style="margin-left: 5px">{{ msgTime }}</span>
|
||||
</el-header>
|
||||
<el-main class="message-content">
|
||||
<div
|
||||
v-if="msgStatus === msgSendStatus.PENDING"
|
||||
class="my-message-status my-message-status-pending"
|
||||
>
|
||||
<div class="loading-circular" v-loading="true"></div>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="
|
||||
msgStatus === msgSendStatus.FAILED || msgStatus === msgSendStatus.UPLOAD_FAILED
|
||||
"
|
||||
class="my-message-status my-message-status-failed"
|
||||
>
|
||||
<el-icon
|
||||
color="red"
|
||||
:title="msgStatus === msgSendStatus.FAILED ? '点击重发' : ''"
|
||||
@click="onResendMsg"
|
||||
><WarningFilled
|
||||
/></el-icon>
|
||||
</div>
|
||||
<div v-else-if="isChatMsgType" class="my-message-status">
|
||||
<div v-if="myMsgIsRead" class="remote_read"></div>
|
||||
<div v-else class="remote_unread"></div>
|
||||
</div>
|
||||
<MenuMsgItem :msg="msg" @selectMenu="onSelectMenuMsgItem">
|
||||
<div class="div-content" :id="`div-content-${msg.msgId}`"></div>
|
||||
</MenuMsgItem>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
<el-aside class="el-aside-right">
|
||||
<UserAvatarIcon
|
||||
class="avatar-message-item"
|
||||
:showId="account"
|
||||
:showName="nickName"
|
||||
:showAvatarThumb="avatarThumb"
|
||||
@click="onShowUserCard"
|
||||
></UserAvatarIcon>
|
||||
</el-aside>
|
||||
</el-container>
|
||||
|
||||
<el-container class="el-container-left" v-else>
|
||||
<el-aside class="el-aside-left">
|
||||
<UserAvatarIcon
|
||||
class="avatar-message-item"
|
||||
:showId="account"
|
||||
:showName="nickName"
|
||||
:showAvatarThumb="avatarThumb"
|
||||
@click="onShowUserCard"
|
||||
></UserAvatarIcon>
|
||||
</el-aside>
|
||||
<el-main class="el-main-left">
|
||||
<el-container class="message-content-wrapper">
|
||||
<el-header class="message-time">
|
||||
<span v-if="isGroupChatMsgType" style="margin-right: 5px">{{ nickName }}</span>
|
||||
<span>{{ msgTime }}</span>
|
||||
</el-header>
|
||||
<el-main class="message-content">
|
||||
<MenuMsgItem :msg="msg" @selectMenu="onSelectMenuMsgItem">
|
||||
<div class="div-content" :id="`div-content-${msg.msgId}`"></div>
|
||||
</MenuMsgItem>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
</el-container>
|
||||
<el-container class="el-container-left" v-else>
|
||||
<el-aside class="el-aside-left">
|
||||
<UserAvatarIcon
|
||||
class="avatar-message-item"
|
||||
:showId="account"
|
||||
:showName="nickName"
|
||||
:showAvatarThumb="avatarThumb"
|
||||
@click="onShowUserCard"
|
||||
></UserAvatarIcon>
|
||||
</el-aside>
|
||||
<el-main class="el-main-left">
|
||||
<el-container class="message-content-wrapper">
|
||||
<el-header class="message-time">
|
||||
<span v-if="isGroupChatMsgType" style="margin-right: 5px">{{ nickName }}</span>
|
||||
<span>{{ msgTime }}</span>
|
||||
</el-header>
|
||||
<el-main class="message-content">
|
||||
<MenuMsgItem :msg="msg" @selectMenu="onSelectMenuMsgItem">
|
||||
<div class="div-content" :id="`div-content-${msg.msgId}`"></div>
|
||||
</MenuMsgItem>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.message-item-wrapper {
|
||||
position: relative;
|
||||
padding-left: 8px;
|
||||
border-radius: 5px;
|
||||
transition: background-color 0.3s;
|
||||
|
||||
&.multi-select-mode {
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&.is-valid-option {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.message-checkbox {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.message-item {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
Reference in New Issue
Block a user