合并消息转发重构

This commit is contained in:
bob
2025-05-13 15:01:31 +08:00
parent fd5a645bba
commit 83376a2a6a
5 changed files with 185 additions and 76 deletions

View File

@@ -32,6 +32,10 @@ export const msgChatQuerySessionService = (obj) => {
return request.get('/chat/querySession', { params: obj })
}
export const msgChatQueryMessagesService = (obj) => {
return request.get('/chat/queryMessages', { params: obj })
}
export const msgChatCloseSessionService = (obj) => {
return request.post('/chat/closeSession', obj)
}

View File

@@ -21,7 +21,7 @@ export const msgContentType = {
EMOJI: 5, // 表情
VIDEO: 6, // 视频
DOCUMENT: 7, // 文档
FORWARD_TOGETHER: 8 // 合并转发消息
FORWARD_TOGETHER: 10 // 合并转发消息
}
// 消息发送状态

View File

@@ -1158,22 +1158,22 @@ const onSelectOprMenu = (label) => {
const inputMultiSelectRef = ref(null)
const isMultiSelect = ref(false)
const multiSelectedMsgIds = ref(new Set())
const multiSelectedMsgKeys = ref(new Set())
const handleMsgItemSelect = (msgKey, selected) => {
if (!isMultiSelect.value) {
isMultiSelect.value = true
}
if (selected) {
multiSelectedMsgIds.value.add(msgKey)
multiSelectedMsgKeys.value.add(msgKey)
} else {
multiSelectedMsgIds.value.delete(msgKey)
multiSelectedMsgKeys.value.delete(msgKey)
}
}
const handleCancleMultiSelect = () => {
isMultiSelect.value = false
multiSelectedMsgIds.value.clear()
multiSelectedMsgKeys.value.clear()
}
const handleForwardTogether = () => {
@@ -1187,14 +1187,17 @@ const handleForwardOneByOne = () => {
}
const handleBatchDeleteMsg = () => {
const deleteMsgIds = [...multiSelectedMsgKeys.value].map((item) => {
return messageData.getMsg(selectedSessionId.value, item).msgId
})
msgChatDeleteMsgService({
sessionId: selectedSessionId.value,
deleteMsgIds: [...multiSelectedMsgIds.value]
deleteMsgIds: [...deleteMsgIds]
})
.then((res) => {
if (res.data.code === 0) {
multiSelectedMsgIds.value.forEach((item) => {
messageData.removeMsgRecord(selectedSessionId.value, item)
multiSelectedMsgKeys.value.forEach((msgKey) => {
messageData.removeMsgRecord(selectedSessionId.value, msgKey)
})
handleCancleMultiSelect()
ElMessage.success('消息已删除')
@@ -1326,10 +1329,10 @@ const handleGlobalMouseUp = (e) => {
isMultiSelect.value = true
}
const msgId = el.dataset.msgId
const msgKey = el.dataset.msgKey
const disabled = el.dataset.disabled
if (disabled !== 'true' && !multiSelectedMsgIds.value.has(msgId)) {
multiSelectedMsgIds.value.add(msgId)
if (disabled !== 'true' && !multiSelectedMsgKeys.value.has(msgKey)) {
multiSelectedMsgKeys.value.add(msgKey)
}
const cancelClick = (e) => {
@@ -1346,40 +1349,6 @@ const handleGlobalMouseUp = (e) => {
const isShowForwardMsgDialog = ref(false)
const showForwardMsgDialogTitle = ref('')
// 待转发的消息
const forwardMsgs = computed(() => {
let msgs = []
multiSelectedMsgIds.value.forEach((item) => {
const msg = messageData.getMsg(selectedSessionId.value, item)
let nickName = ''
if (msg.msgType === MsgType.CHAT) {
if (myAccount.value === msg.fromId) {
nickName = userData.user.nickName
} else {
nickName = messageData.sessionList[msg.sessionId].objectInfo.nickName
}
} else if (msg.msgType === MsgType.GROUP_CHAT) {
const groupId = messageData.sessionList[msg.sessionId].remoteId
const members = groupData.groupMembersList[groupId]
nickName = members[msg.fromId].nickName
}
msgs.push({
...msg,
nickName
})
})
if (showForwardMsgDialogTitle.value === '合并转发') {
return [
{
type: msgContentType.FORWARD_TOGETHER,
value: msgs
}
]
} else {
return msgs
}
})
const sessionListSortedKey = computed(() => {
return sessionListSorted.value
@@ -1389,11 +1358,11 @@ const sessionListSortedKey = computed(() => {
.map((item) => item.sessionId)
})
const showForwardMsgDialog = (msgId) => {
multiSelectedMsgIds.value.clear()
multiSelectedMsgIds.value.add(msgId)
const showForwardMsgDialog = (msgKey) => {
multiSelectedMsgKeys.value.clear()
multiSelectedMsgKeys.value.add(msgKey)
isShowForwardMsgDialog.value = true
showForwardMsgDialogTitle.value = '转发消息'
showForwardMsgDialogTitle.value = '逐条转发'
}
const handleConfirmForwardMsg = async (sessions) => {
@@ -1412,14 +1381,43 @@ const handleConfirmForwardMsg = async (sessions) => {
messageData.addSession(res.data.data.session)
}
for (const forwardMsg of forwardMsgs.value) {
const content =
showForwardMsgDialogTitle.value !== '合并转发'
? forwardMsg.content
: JSON.stringify(forwardMsg)
if (showForwardMsgDialogTitle.value === '逐条转发') {
for (const msgKey of multiSelectedMsgKeys.value) {
const msg = messageData.getMsg(selectedSessionId.value, msgKey)
await handleSendForwardMsg({
session: item,
content: msg.content
})
}
} else if (showForwardMsgDialogTitle.value === '合并转发') {
const msgs = [...multiSelectedMsgKeys.value].map((item) => {
const msg = messageData.getMsg(selectedSessionId.value, item)
let nickName = ''
if (selectedSession.value.sessionType === MsgType.CHAT) {
if (myAccount.value === msg.fromId) {
nickName = userData.user.nickName
} else {
nickName = selectedSession.value.objectInfo.nickName
}
} else if (selectedSession.value.sessionType === MsgType.GROUP_CHAT) {
const groupId = selectedSession.value.remoteId
const members = groupData.groupMembersList[groupId]
nickName = members[msg.fromId].nickName
}
return {
nickName,
msgId: msg.msgId
}
})
await handleSendForwardMsg({
session: item,
content: content
content: JSON.stringify({
type: msgContentType.FORWARD_TOGETHER,
value: {
sessionId: selectedSessionId.value,
data: [...msgs]
}
})
})
}
}
@@ -1675,7 +1673,7 @@ const onShowRecorder = () => {
:isLoadMoreLoading="selectedSessionCache[selectedSessionId]?.isLoadMoreLoading"
:inputEditorRef="inputEditorRef"
:isMultiSelect="isMultiSelect"
:isSelected="multiSelectedMsgIds.has(item)"
:isSelected="multiSelectedMsgKeys.has(item)"
@loadMore="onLoadMore"
@showUserCard="onShowUserCard"
@showGroupCard="onShowGroupCard"
@@ -1728,7 +1726,7 @@ const onShowRecorder = () => {
<el-container v-if="isMultiSelect">
<InputMultiSelect
ref="inputMultiSelectRef"
:selectedCount="multiSelectedMsgIds.size"
:selectedCount="multiSelectedMsgKeys.size"
@exit="handleCancleMultiSelect"
@forwardTogether="handleForwardTogether"
@forwardOneByOne="handleForwardOneByOne"

View File

@@ -1,5 +1,5 @@
<script setup lang="jsx">
import { onMounted, computed, watch, createApp, h } from 'vue'
import { ref, onMounted, computed, watch, createApp, h } from 'vue'
import { ElDialog, ElLoading } from 'element-plus'
import {
useUserStore,
@@ -24,6 +24,7 @@ import MsgBoxDocument from '@/views/message/components/MsgBoxDocument.vue'
import DialogForMsgList from '@/views/message/components/DialogForMsgList.vue'
import { emojis } from '@/js/utils/emojis'
import { MsgType } from '@/proto/msg'
import { msgChatQueryMessagesService } from '@/api/message'
const props = defineProps(['isShow', 'title', 'sessionId', 'msgs', 'tier'])
const emit = defineEmits(['update:isShow', 'showUserCard', 'close'])
@@ -36,8 +37,16 @@ const audioData = useAudioStore()
const videoData = useVideoStore()
const documentData = useDocumentStore()
const msgsFromServer = ref({})
onMounted(async () => {
await messageData.preloadResource(props.msgs)
const loadingInstance = ElLoading.service(el_loading_options)
try {
await messageData.preloadResource(props.msgs)
await loadForwardTogetherMsgs()
} finally {
loadingInstance.close()
}
})
/**
@@ -50,6 +59,65 @@ watch(
}
)
const loadForwardTogetherMsgs = async () => {
for (const msg of props.msgs) {
const content = msg.content
const contentJson = jsonParseSafe(content)
if (!contentJson) {
return
}
const type = contentJson['type']
const value = contentJson['value']
if (!type || !value) {
return
} else {
if (type === msgContentType.FORWARD_TOGETHER) {
let res
try {
const msgIds = value.data
.map((item) => {
return item.msgId
})
.join(',')
res = await msgChatQueryMessagesService({
sessionId: value.sessionId,
msgIds
})
} catch (error) {
console.error(error)
return
}
const msgs = res.data.data
if (!res.data.data || res.data.data.length == 0) {
return
}
// value.data(取里面的nickName) 和 msgs合一
const newMsgs = {}
msgs.forEach((item) => {
newMsgs[item.msgId] = item
})
value.data.forEach((item) => {
if (item.msgId in newMsgs) {
newMsgs[item.msgId] = {
...newMsgs[item.msgId],
...item
}
}
})
msgsFromServer.value[msg.msgId] = Object.values(newMsgs).sort((a, b) => {
const timeA = new Date(a.sendTime || a.msgTime).getTime()
const timeB = new Date(b.sendTime || b.msgTime).getTime()
return timeA - timeB
})
}
}
}
}
const myAccount = computed(() => {
return userData.user.account
})
@@ -91,7 +159,7 @@ const renderContent = ({ msg }) => {
case msgContentType.DOCUMENT:
return renderDocument(value)
case msgContentType.FORWARD_TOGETHER:
return renderForwardTogether(value)
return renderForwardTogether(msgId)
default:
return <span>{content}</span>
}
@@ -232,13 +300,13 @@ const renderDocument = (content) => {
}
}
const renderForwardTogether = (msgs) => {
const renderForwardTogether = (msgId) => {
const title = '聊天记录'
const msgsSorted = msgs.sort((a, b) => {
const timeA = new Date(a.sendTime || a.msgTime).getTime()
const timeB = new Date(b.sendTime || b.msgTime).getTime()
return timeA - timeB
})
const msgsSorted = msgsFromServer.value[msgId]
if (!msgsSorted) {
return <div class={'forward-together'}></div>
}
return (
<div

View File

@@ -23,7 +23,11 @@ import MsgBoxVideo from '@/views/message/components/MsgBoxVideo.vue'
import MsgBoxDocument from '@/views/message/components/MsgBoxDocument.vue'
import MenuMsgItem from '@/views/message/components/MenuMsgItem.vue'
import { ElMessage } from 'element-plus'
import { msgChatDeleteMsgService, msgChatRevokeMsgService } from '@/api/message'
import {
msgChatDeleteMsgService,
msgChatQueryMessagesService,
msgChatRevokeMsgService
} from '@/api/message'
import DialogForMsgList from '@/views/message/components/DialogForMsgList.vue'
const props = defineProps([
@@ -61,18 +65,18 @@ const audioData = useAudioStore()
const videoData = useVideoStore()
const documentData = useDocumentStore()
onMounted(() => {
rendering()
onMounted(async () => {
await rendering()
})
let app = null
const rendering = () => {
const rendering = async () => {
const msgContent = document.querySelector(`#div-content-${msg.value.msgId}`)
if (msgContent) {
if (app) {
app.unmount()
}
const vnode = renderComponent(msg.value.content)
const vnode = await renderComponent(msg.value.content)
app = createApp({
render: () => vnode
})
@@ -84,7 +88,7 @@ const rendering = () => {
* 动态渲染消息内容
* @param content 消息内容
*/
const renderComponent = (content) => {
const renderComponent = async (content) => {
const contentJson = jsonParseSafe(content)
if (!contentJson) {
return renderMix(content)
@@ -114,7 +118,7 @@ const renderComponent = (content) => {
case msgContentType.DOCUMENT:
return renderDocument(value)
case msgContentType.FORWARD_TOGETHER:
return renderForwardTogether(value)
return await renderForwardTogether(value)
default:
return h('span', content)
}
@@ -263,11 +267,46 @@ const showMsgContentInForwardTogether = (content) => {
}
}
const renderForwardTogether = (msgs) => {
const renderForwardTogether = async (content) => {
let res
try {
const msgIds = content.data
.map((item) => {
return item.msgId
})
.join(',')
res = await msgChatQueryMessagesService({
sessionId: content.sessionId,
msgIds
})
} catch (error) {
console.error(error)
return h('span', content)
}
const msgs = res.data.data
if (!res.data.data || res.data.data.length == 0) {
return h('span', content)
}
// 把content.data(取里面的nickName) 和 msgs合一
const newMsgs = {}
msgs.forEach((item) => {
newMsgs[item.msgId] = item
})
content.data.forEach((item) => {
if (item.msgId in newMsgs) {
newMsgs[item.msgId] = {
...newMsgs[item.msgId],
...item
}
}
})
const title =
(msgs[0].msgType === MsgType.GROUP_CHAT ? '群聊' : nickNameFromMsg.value) + '的聊天记录'
const msgsSorted = msgs.sort((a, b) => {
const msgsSorted = Object.values(newMsgs).sort((a, b) => {
const timeA = new Date(a.sendTime || a.msgTime).getTime()
const timeB = new Date(b.sendTime || b.msgTime).getTime()
return timeA - timeB
@@ -1046,7 +1085,7 @@ const handleItemClick = () => {
</div>
<div
class="message-item"
:data-msg-id="props.msgKey"
:data-msg-key="props.msgKey"
:data-disabled="multiSelectOptionDisabled"
:class="{ unreadMsg: isUnreadMsg }"
>