同一session中共用一个preview-src-list

This commit is contained in:
bob
2025-04-10 09:28:32 +08:00
parent 34a4ad40e8
commit 2e8c834fe6
14 changed files with 110 additions and 77 deletions

View File

@@ -44,7 +44,7 @@ export const onReceiveChatMsg = (updateScroll, capacity) => {
content: msg.body.content,
msgTime: now
}
await messageData.preloadResource(sessionId, [showMsg])
await messageData.preloadResource([showMsg])
messageData.addMsgRecords(sessionId, [showMsg])
messageData.updateMsgIdSort(sessionId)

View File

@@ -42,7 +42,7 @@ export const onReceiveGroupChatMsg = (updateScroll, capacity) => {
content: msg.body.content,
msgTime: now
}
await messageData.preloadResource(sessionId, [showMsg])
await messageData.preloadResource([showMsg])
messageData.addMsgRecords(sessionId, [showMsg])
messageData.updateMsgIdSort(sessionId)

View File

@@ -34,7 +34,7 @@ export const onReceiveGroupSystemMsg = (updateScroll, capacity) => {
content: msg.body.content,
msgTime: now
}
await messageData.preloadResource(sessionId, [showMsg])
await messageData.preloadResource([showMsg])
messageData.addMsgRecords(sessionId, [showMsg])
messageData.updateMsgIdSort(sessionId)

View File

@@ -14,11 +14,11 @@ export const useAudioStore = defineStore('anylink-audio', () => {
*/
const audio = ref({})
const setAudio = (sessionId, obj) => {
const setAudio = (obj) => {
audio.value[obj.objectId] = obj
}
const preloadAudio = async (sessionId, msgRecords) => {
const preloadAudio = async (msgRecords) => {
const audioIds = new Set()
msgRecords.forEach((item) => {
const content = item.content
@@ -37,7 +37,7 @@ export const useAudioStore = defineStore('anylink-audio', () => {
if (audioIds.size > 0) {
const res = await mtsAudioService({ objectIds: [...audioIds].join(',') })
res.data.data.forEach((item) => {
setAudio(sessionId, item)
setAudio(item)
})
}
}

View File

@@ -14,11 +14,11 @@ export const useDocumentStore = defineStore('anylink-document', () => {
*/
const document = ref({})
const setDocument = (sessionId, obj) => {
const setDocument = (obj) => {
document.value[obj.objectId] = obj
}
const preloadDocument = async (sessionId, msgRecords) => {
const preloadDocument = async (msgRecords) => {
const documentIds = new Set()
msgRecords.forEach((item) => {
const content = item.content
@@ -34,7 +34,7 @@ export const useDocumentStore = defineStore('anylink-document', () => {
if (documentIds.size > 0) {
const res = await mtsDocumentService({ objectIds: [...documentIds].join(',') })
res.data.data.forEach((item) => {
setDocument(sessionId, item)
setDocument(item)
})
}
}

View File

@@ -17,24 +17,25 @@ export const useImageStore = defineStore('anylink-image', () => {
const image = ref({})
/**
* 在同一个session中的imageid集合
* 在同一个session中的需要渲染的image对象数组
*/
const imageInSession = ref({})
/**
* 本地图片只是临时的不用放进imageInSession
* @param {*} obj
*/
const setLocalImage = (obj) => {
const setImage = (obj) => {
image.value[obj.objectId] = obj
}
const setServerImage = (sessionId, obj) => {
image.value[obj.objectId] = obj
const setImageInSession = (sessionId, obj) => {
if (!imageInSession.value[sessionId]) {
imageInSession.value[sessionId] = []
}
imageInSession.value[sessionId].push(obj.objectId)
imageInSession.value[sessionId].push(obj)
}
const clearImageInSession = (sessionId) => {
if (imageInSession.value[sessionId]) {
imageInSession.value[sessionId] = []
}
}
const imageTrans = (content, maxWidth = 360, maxHeight = 180) => {
@@ -60,7 +61,7 @@ export const useImageStore = defineStore('anylink-image', () => {
return content
}
const loadImageInfoFromContent = async (sessionId, content) => {
const loadImageInfoFromContent = async (content) => {
const imageIds = new Set()
const matches = content.match(pattern)
if (matches && matches.length > 0) {
@@ -76,12 +77,12 @@ export const useImageStore = defineStore('anylink-image', () => {
if (imageIds.size > 0) {
const res = await mtsImageService({ objectIds: [...imageIds].join(',') })
res.data.data.forEach((item) => {
setServerImage(sessionId, item) // 缓存image数据
setImage(item) // 缓存image数据
})
}
}
const preloadImage = async (sessionId, msgRecords) => {
const preloadImage = async (msgRecords) => {
const imageIds = new Set()
msgRecords.forEach((item) => {
const content = item.content
@@ -109,7 +110,7 @@ export const useImageStore = defineStore('anylink-image', () => {
if (imageIds.size > 0) {
const res = await mtsImageService({ objectIds: [...imageIds].join(',') })
res.data.data.forEach((item) => {
setServerImage(sessionId, item)
setImage(item)
})
}
}
@@ -117,8 +118,9 @@ export const useImageStore = defineStore('anylink-image', () => {
return {
image,
imageInSession,
setLocalImage,
setServerImage,
setImage,
setImageInSession,
clearImageInSession,
imageTrans,
loadImageInfoFromContent,
preloadImage

View File

@@ -121,11 +121,11 @@ export const useMessageStore = defineStore('anylink-message', () => {
* @param {*} sessionId
* @param {*} msgRecords
*/
const preloadResource = async (sessionId, msgRecords) => {
await useImageStore().preloadImage(sessionId, msgRecords)
await useAudioStore().preloadAudio(sessionId, msgRecords)
await useVideoStore().preloadVideo(sessionId, msgRecords)
await useDocumentStore().preloadDocument(sessionId, msgRecords)
const preloadResource = async (msgRecords) => {
await useImageStore().preloadImage(msgRecords)
await useAudioStore().preloadAudio(msgRecords)
await useVideoStore().preloadVideo(msgRecords)
await useDocumentStore().preloadDocument(msgRecords)
}
/**
@@ -239,7 +239,7 @@ export const useMessageStore = defineStore('anylink-message', () => {
addSession(res.data.data[item].session)
const msgList = res.data.data[item].msgList
if (msgList) {
await preloadResource(item, msgList)
await preloadResource(msgList)
addMsgRecords(item, msgList)
updateMsgIdSort(item)
}

View File

@@ -14,11 +14,11 @@ export const useVideoStore = defineStore('anylink-video', () => {
*/
const video = ref({})
const setVideo = (sessionId, obj) => {
const setVideo = (obj) => {
video.value[obj.objectId] = obj
}
const preloadVideo = async (sessionId, msgRecords) => {
const preloadVideo = async (msgRecords) => {
const videoIds = new Set()
msgRecords.forEach((item) => {
const content = item.content
@@ -34,7 +34,7 @@ export const useVideoStore = defineStore('anylink-video', () => {
if (videoIds.size > 0) {
const res = await mtsVideoService({ objectIds: [...videoIds].join(',') })
res.data.data.forEach((item) => {
setVideo(sessionId, item)
setVideo(item)
})
}
}

View File

@@ -25,7 +25,8 @@ import {
useMessageStore,
useUserCardStore,
useGroupCardStore,
useGroupStore
useGroupStore,
useImageStore
} from '@/stores'
import backgroupImage from '@/assets/svg/messagebx_bg.svg'
import {
@@ -57,6 +58,7 @@ const messageData = useMessageStore()
const userCardData = useUserCardStore()
const groupCardData = useGroupCardStore()
const groupData = useGroupStore()
const imageData = useImageStore()
const sessionListRef = ref()
const asideWidth = ref(0)
@@ -180,6 +182,7 @@ const initSession = (sessionId) => {
}
isShowRecorder.value = false // 麦克风输入状态重置
inputRecorderRef.value?.cancelSend() // 取消音频发送
imageData.clearImageInSession(sessionId) // 清除待渲染的图片队列
}
/**
@@ -326,9 +329,9 @@ const sessionListSorted = computed(() => {
if (!b_msgIds_len) return -1
const b_lastMsg = messageData.getMsg(b.sessionId, b_msgIds[b_msgIds_len - 1])
const bTime = new Date(b_lastMsg.msgTime).getTime()
const aTIme = new Date(a_lastMsg.msgTime).getTime()
if (bTime !== aTIme) {
return bTime - aTIme
const aTime = new Date(a_lastMsg.msgTime).getTime()
if (bTime !== aTime) {
return bTime - aTime
}
}
}
@@ -416,7 +419,7 @@ const pullMsg = async (endMsgId = null) => {
const res = await msgChatPullMsgService(params)
const msgCount = res.data.data.count
if (msgCount > 0) {
await messageData.preloadResource(sessionId, res.data.data.msgList)
await messageData.preloadResource(res.data.data.msgList)
messageData.addMsgRecords(sessionId, res.data.data.msgList)
messageData.updateMsgIdSort(sessionId)
}
@@ -1191,7 +1194,6 @@ const onShowRecorder = () => {
<el-container v-if="isShowRecorder">
<InputRecorder
ref="inputRecorderRef"
:sessionId="selectedSessionId"
@exit="isShowRecorder = false"
@saveLocalMsg="handleLocalMsg"
@sendMessage="handleSendMessage"

View File

@@ -2,10 +2,13 @@
import { computed } from 'vue'
import { ElImage } from 'element-plus'
import { formatFileSize } from '@/js/utils/common'
import { useImageStore } from '@/stores'
const props = defineProps(['url', 'imgId', 'srcList', 'initialIndex', 'fileName', 'size'])
const props = defineProps(['sessionId', 'imgId', 'isForMix'])
const emits = defineEmits(['load'])
const imageData = useImageStore()
const onLoad = (e) => {
const img = e.target
const maxWidth = 360
@@ -24,27 +27,57 @@ const onLoad = (e) => {
emits('load') //向父组件暴露load事件
}
const url = computed(() => {
return imageData.image[props.imgId]?.thumbUrl
})
const imageInSessionSort = computed(() => {
const imageList = imageData.imageInSession[props.sessionId]
return imageList.sort((a, b) => {
const bTime = new Date(b.createdTime).getTime()
const aTime = new Date(a.createdTime).getTime()
return aTime - bTime
})
})
const srcList = computed(() => {
return imageInSessionSort.value.map((item) => item.originUrl)
})
const initialIndex = computed(() => {
const imgIdList = imageInSessionSort.value.map((item) => item.objectId.toString())
return imgIdList.indexOf(props.imgId.toString())
})
const fileName = computed(() => {
return props.isForMix ? '' : imageData.image[props.imgId].fileName
})
const size = computed(() => {
return props.isForMix ? '' : imageData.image[props.imgId].size
})
const formatSize = computed(() => {
return formatFileSize(props.size)
return formatFileSize(size.value)
})
</script>
<template>
<div class="image-msg-wrapper">
<el-image
:src="props.url"
:src="url"
:alt="props.imgId"
:preview-src-list="props.srcList"
:initial-index="props.initialIndex"
:preview-src-list="srcList"
:initial-index="initialIndex"
:infinite="false"
:lazy="false"
fit="contain"
@load="onLoad"
>
</el-image>
<div v-if="props.fileName || props.size > 0" class="info">
<span class="name item text-ellipsis" :title="props.fileName">
{{ props.fileName || '' }}
<div v-if="fileName || size > 0" class="info">
<span class="name item text-ellipsis" :title="fileName">
{{ fileName || '' }}
</span>
<span class="size item text-ellipsis" :title="formatSize">
{{ formatSize }}

View File

@@ -24,7 +24,7 @@ const getQuill = () => {
onMounted(async () => {
// 给组件增加滚动条样式
document.querySelector('.ql-editor').classList.add('my-scrollbar')
await imageData.loadImageInfoFromContent(props.sessionId, props.draft)
await imageData.loadImageInfoFromContent(props.draft)
formatContent(props.draft)
getQuill().on('composition-start', () => {
// 当用户使用拼音输入法开始输入汉字时,这个事件就会被触发
@@ -43,7 +43,7 @@ onBeforeUnmount(async () => {
someUploadedFailFn: () => {},
allUploadedSuccessFn: () => {}
}
const contentObj = parseContent(props.sessionId, callbacks)
const contentObj = parseContent(callbacks)
const fn = (content) => {
// 草稿若发生变动,则触发存储
@@ -72,7 +72,7 @@ onUnmounted(() => {
}
})
const parseContent = (sessionId, callbacks) => {
const parseContent = (callbacks) => {
const delta = getQuill().getContents()
let contentFromLocal = new Array(delta.ops.length).fill('')
let contentFromServer = new Array(delta.ops.length).fill('')
@@ -114,19 +114,20 @@ const parseContent = (sessionId, callbacks) => {
const tempObjectId = new Date().getTime()
// 发送的时候设置本地缓存(非服务端数据),用于立即渲染
const localSrc = URL.createObjectURL(file)
imageData.setLocalImage({
imageData.setImage({
objectId: tempObjectId,
originUrl: localSrc,
thumbUrl: localSrc,
fileName: file.name,
size: file.size
size: file.size,
createdTime: new Date()
})
contentFromLocal[index] = `{${tempObjectId}}`
//上传图片至服务端
mtsUploadService({ file: file, storeType: 1 })
.then((res) => {
imageData.setServerImage(sessionId, res.data.data) // 缓存image数据
imageData.setImage(res.data.data) // 缓存image数据
uploadSuccessCount++
contentFromServer[index] = `{${res.data.data.objectId}}`
callbacks.someOneUploadedSuccessFn()
@@ -166,7 +167,7 @@ watch(
someUploadedFailFn: () => {},
allUploadedSuccessFn: () => {}
}
const contentObj = parseContent(oldSessionId, callbacks)
const contentObj = parseContent(callbacks)
const fn = (content) => {
// 草稿若发生变动,则触发存储
@@ -205,7 +206,7 @@ const handleEnter = async () => {
allUploadedSuccessFn: () => {}
}
const contentObj = parseContent(props.sessionId, callbacks)
const contentObj = parseContent(callbacks)
const content = contentObj.contentFromLocal.join('').trim()
if (!content) {

View File

@@ -7,7 +7,6 @@ import { mtsUploadService } from '@/api/mts'
import { v4 as uuidv4 } from 'uuid'
import { msgContentType, msgFileUploadStatus } from '@/const/msgConst'
const props = defineProps(['sessionId'])
const emit = defineEmits(['exit', 'sendMessage', 'saveLocalMsg'])
const audioData = useAudioStore()
@@ -151,7 +150,7 @@ const uploadRecord = () => {
const duration = Math.floor(recordDuration / 1000)
const localSrc = URL.createObjectURL(file)
const tempObjectId = new Date().getTime()
audioData.setAudio(props.sessionId, {
audioData.setAudio({
objectId: tempObjectId,
duration: duration,
url: localSrc,
@@ -172,7 +171,7 @@ const uploadRecord = () => {
mtsUploadService({ file, storeType: 1, duration: duration })
.then((res) => {
if (res.data.code === 0) {
audioData.setAudio(props.sessionId, res.data.data) // 缓存服务端响应的audio数据
audioData.setAudio(res.data.data) // 缓存服务端响应的audio数据
msg.uploadStatus = msgFileUploadStatus.UPLOAD_SUCCESS
msg.uploadProgress = 100
msg.content = JSON.stringify({

View File

@@ -81,16 +81,17 @@ const setLocalData = (contentType, file) => {
const localSrc = URL.createObjectURL(file.raw)
switch (contentType) {
case msgContentType.IMAGE:
imageData.setLocalImage({
imageData.setImage({
objectId: file.uid,
originUrl: localSrc,
thumbUrl: localSrc,
fileName: file.name,
size: file.raw.size
size: file.raw.size,
createdTime: new Date()
})
break
case msgContentType.AUDIO:
audioData.setAudio(props.sessionId, {
audioData.setAudio({
objectId: file.uid,
url: localSrc,
fileName: file.name,
@@ -98,7 +99,7 @@ const setLocalData = (contentType, file) => {
})
break
case msgContentType.VIDEO:
videoData.setVideo(props.sessionId, {
videoData.setVideo({
objectId: file.uid,
url: localSrc,
fileName: file.name,
@@ -107,7 +108,7 @@ const setLocalData = (contentType, file) => {
break
case msgContentType.DOCUMENT:
default:
documentData.setDocument(props.sessionId, {
documentData.setDocument({
objectId: file.uid,
documentType: file.raw.type,
url: localSrc,
@@ -125,17 +126,17 @@ const setLocalData = (contentType, file) => {
const setStoreData = (contentType, data) => {
switch (contentType) {
case msgContentType.IMAGE:
imageData.setServerImage(props.sessionId, data)
imageData.setImage(data)
break
case msgContentType.AUDIO:
audioData.setAudio(props.sessionId, data)
audioData.setAudio(data)
break
case msgContentType.VIDEO:
videoData.setVideo(props.sessionId, data)
videoData.setVideo(data)
break
case msgContentType.DOCUMENT:
default:
documentData.setDocument(props.sessionId, data)
documentData.setDocument(data)
}
}

View File

@@ -168,18 +168,13 @@ const renderVideo = (content) => {
const renderImage = (content, isForMix = false) => {
const imgId = content
const url = imageData.image[imgId]?.thumbUrl
if (url) {
const imgIdList = imageData.imageInSession[props.sessionId] || []
const imgIdListSorted = imgIdList.includes(imgId) ? imgIdList.sort((a, b) => a - b) : [imgId]
const srcList = imgIdListSorted.map((item) => imageData.image[item].originUrl)
if (imageData.image[imgId]) {
// 只要这里渲染就收集该session下的所有image用于preview-src-list
imageData.setImageInSession(props.sessionId, imageData.image[imgId])
return h(ImageMsgBox, {
url,
sessionId: props.sessionId,
imgId,
srcList,
initialIndex: imgIdListSorted.indexOf(imgId),
fileName: isForMix ? '' : imageData.image[imgId].fileName,
size: isForMix ? '' : imageData.image[imgId].size,
isForMix,
onLoad: () => {
emit('loadFinished')
}