发送时立即渲染本地消息

This commit is contained in:
bob
2025-04-08 20:28:22 +08:00
parent bdb221f123
commit b39e38de4a
10 changed files with 468 additions and 151 deletions

View File

@@ -29,3 +29,11 @@ export const msgSendStatus = {
OK: 'ok', // 发送成功
FAILED: 'failed' // 发送失败
}
// 消息中文件的上传状态
export const msgFileUploadStatus = {
UPLOAD_DEFAULT: 0, // 默认状态,不上传
UPLOADING: 1, // 上传中
UPLOAD_SUCCESS: 2, // 上传成功
UPLOAD_FAILED: 3 // 上传失败
}

89
src/models/message.js Normal file
View File

@@ -0,0 +1,89 @@
import { msgSendStatus } from '@/const/msgConst'
/**
* 消息渲染及缓存时用到实体类,收录所有可能用到的属性,目前作为参考用,并未实际调用
*/
class Message {
/**
* 会话内唯一消息Id
*/
msgId
/**
* 消息序列号
*/
seq
/**
* 消息所属的会话ID
*/
sessionId
/**
* 消息发送ID
*/
fromId
/**
* 消息类型
*/
msgType
/**
* 消息内容
*/
content
/**
* 消息状态:发送中,发送成功,发送失败
*/
status
/**
* 接收消息的时间
*/
msgTime
/**
* 消息发送的时间,发送消息时才需要填
*/
sendTime
/**
* 消息中文件的上传状态
*/
uploadStatus
/**
* 消息中文件的上传进度
*/
uploadProgress
constructor(
sessionId,
fromId,
msgType,
content,
msgTime,
sendTime = undefined,
msgId = undefined,
seq = undefined,
status = msgSendStatus.PENDING,
uploadStatus = undefined,
uploadProgress = undefined
) {
this.msgId = msgId
this.seq = seq
this.sessionId = sessionId
this.fromId = fromId
this.msgType = msgType
this.content = content
this.status = status
this.msgTime = msgTime
this.sendTime = sendTime
this.uploadStatus = uploadStatus
this.uploadProgress = uploadProgress
}
}
export { Message }

View File

@@ -21,7 +21,15 @@ export const useImageStore = defineStore('anylink-image', () => {
*/
const imageInSession = ref({})
const setImage = (sessionId, obj) => {
/**
* 本地图片只是临时的不用放进imageInSession
* @param {*} obj
*/
const setLocalImage = (obj) => {
image.value[obj.objectId] = obj
}
const setServerImage = (sessionId, obj) => {
image.value[obj.objectId] = obj
if (!imageInSession.value[sessionId]) {
imageInSession.value[sessionId] = []
@@ -29,7 +37,7 @@ export const useImageStore = defineStore('anylink-image', () => {
imageInSession.value[sessionId].push(obj.objectId)
}
const imageTrans = (content, maxWidth = 400, maxHeight = 300) => {
const imageTrans = (content, maxWidth = 360, maxHeight = 180) => {
const matches = content.match(pattern)
if (!matches || matches.length === 0) {
return content
@@ -68,7 +76,7 @@ export const useImageStore = defineStore('anylink-image', () => {
if (imageIds.size > 0) {
const res = await mtsImageService({ objectIds: [...imageIds].join(',') })
res.data.data.forEach((item) => {
setImage(sessionId, item) // 缓存image数据
setServerImage(sessionId, item) // 缓存image数据
})
}
}
@@ -101,7 +109,7 @@ export const useImageStore = defineStore('anylink-image', () => {
if (imageIds.size > 0) {
const res = await mtsImageService({ objectIds: [...imageIds].join(',') })
res.data.data.forEach((item) => {
setImage(sessionId, item)
setServerImage(sessionId, item)
})
}
}
@@ -109,7 +117,8 @@ export const useImageStore = defineStore('anylink-image', () => {
return {
image,
imageInSession,
setImage,
setLocalImage,
setServerImage,
imageTrans,
loadImageInfoFromContent,
preloadImage

View File

@@ -117,7 +117,7 @@ export const useMessageStore = defineStore('anylink-message', () => {
}
/**
* 对话列表中加入新的消息数组
* 对话列表中加入新的消息数组(预加载资源)
* @param {*} sessionId 会话id
* @param {*} msgRecords 新的消息数组
*/
@@ -128,6 +128,15 @@ export const useMessageStore = defineStore('anylink-message', () => {
await useVideoStore().preloadVideo(sessionId, msgRecords)
await useDocumentStore().preloadDocument(sessionId, msgRecords)
addMsgRecordsWithOutPreLoad(sessionId, msgRecords)
}
/**
* 对话列表中加入新的消息数组
* @param {*} sessionId 会话id
* @param {*} msgRecords 新的消息数组
*/
const addMsgRecordsWithOutPreLoad = (sessionId, msgRecords) => {
if (!msgRecords?.length) return
msgRecords.forEach((item) => {
if (!msgRecordsList.value[sessionId]) {
@@ -272,6 +281,7 @@ export const useMessageStore = defineStore('anylink-message', () => {
msgRecordsList,
msgIdSortArray,
addMsgRecords,
addMsgRecordsWithOutPreLoad,
removeMsgRecord,
getMsg,

View File

@@ -9,6 +9,7 @@ import {
ArrowDownBold,
ArrowUp
} from '@element-plus/icons-vue'
import { v4 as uuidv4 } from 'uuid'
import DragLine from '@/components/common/DragLine.vue'
import SearchBox from '@/components/search/SearchBox.vue'
import AddButton from '@/components/common/AddButton.vue'
@@ -478,7 +479,7 @@ const handleRead = () => {
}
}
const handleSendMessage = (content, resendSeq = '') => {
const handleSendMessage = (msg) => {
if (isNotInGroup.value) {
ElMessage.warning('您已离开该群或群已被解散')
return
@@ -490,14 +491,16 @@ const handleSendMessage = (content, resendSeq = '') => {
if (inputToolBarRef.value) inputToolBarRef.value.closeWindow()
const msg = {
sessionId: selectedSessionId.value,
fromId: myAccount.value,
msgType: selectedSession.value.sessionType,
content: content,
status: msgSendStatus.PENDING,
msgTime: new Date(),
sendTime: new Date()
if (typeof msg === 'string') {
msg = {
sessionId: selectedSessionId.value,
fromId: myAccount.value,
msgType: selectedSession.value.sessionType,
content: msg,
status: msgSendStatus.PENDING,
msgTime: new Date(),
sendTime: new Date()
}
}
const resendInterval = 2000 //2秒
@@ -556,8 +559,8 @@ const handleSendMessage = (content, resendSeq = '') => {
msg.sessionId,
showId.value,
selectedSession.value.sessionType,
content,
resendSeq,
msg.content,
msg.seq,
before,
after
)
@@ -567,8 +570,12 @@ const handleSendMessage = (content, resendSeq = '') => {
locateSession(msg.sessionId)
}
const handleResendMessage = ({ content, seq }) => {
handleSendMessage(content, seq)
const handleResendMessage = (msg) => {
// 重发消息时更新这三个属性,其他不变
msg.status = msgSendStatus.PENDING
msg.msgTime = new Date()
msg.sendTime = new Date()
handleSendMessage(msg)
}
const onLoadMore = async () => {
@@ -982,24 +989,27 @@ const onSendEmoji = (key) => {
inputEditorRef.value.addEmoji(key)
}
const onSendImage = ({ objectId }) => {
handleSendMessage(JSON.stringify({ type: msgContentType.IMAGE, value: objectId }))
}
const onSendAudio = ({ objectId }) => {
handleSendMessage(JSON.stringify({ type: msgContentType.AUDIO, value: objectId }))
}
const onSendRecording = ({ objectId }) => {
handleSendMessage(JSON.stringify({ type: msgContentType.RECORDING, value: objectId }))
}
const onSendVideo = ({ objectId }) => {
handleSendMessage(JSON.stringify({ type: msgContentType.VIDEO, value: objectId }))
}
const onSendDocument = ({ objectId }) => {
handleSendMessage(JSON.stringify({ type: msgContentType.DOCUMENT, value: objectId }))
/**
* 发送时先添加本地消息,可以立即渲染
*/
const handleLocalMsg = ({ content, contentType, objectId, fn }) => {
const seq = uuidv4()
const msg = {
msgId: seq,
seq: seq,
sessionId: selectedSessionId.value,
fromId: myAccount.value,
msgType: selectedSession.value.sessionType,
content:
contentType === msgContentType.MIX
? content
: JSON.stringify({ type: contentType, value: objectId }),
status: msgSendStatus.PENDING,
msgTime: new Date(),
sendTime: new Date()
}
messageData.addMsgRecordsWithOutPreLoad(msg.sessionId, [msg])
fn(msg)
}
const inputRecorderRef = ref(null)
@@ -1193,7 +1203,8 @@ const onShowRecorder = () => {
ref="inputRecorderRef"
:sessionId="selectedSessionId"
@exit="isShowRecorder = false"
@sendRecording="onSendRecording"
@saveLocalMsg="handleLocalMsg"
@sendMessage="handleSendMessage"
></InputRecorder>
</el-container>
<el-container v-else class="input-box-container">
@@ -1203,11 +1214,9 @@ const onShowRecorder = () => {
:sessionId="selectedSessionId"
:isShowToolSet="!isNotInGroup"
@sendEmoji="onSendEmoji"
@sendImage="onSendImage"
@sendAudio="onSendAudio"
@sendVideo="onSendVideo"
@sendDocument="onSendDocument"
@showRecorder="onShowRecorder"
@saveLocalMsg="handleLocalMsg"
@sendMessage="handleSendMessage"
></InputToolBar>
</el-header>
<el-main class="input-box-main">
@@ -1230,6 +1239,7 @@ const onShowRecorder = () => {
ref="inputEditorRef"
:sessionId="selectedSessionId"
:draft="selectedSession.draft || ''"
@saveLocalMsg="handleLocalMsg"
@sendMessage="handleSendMessage"
></InputEditor>
</el-main>

View File

@@ -8,20 +8,16 @@ const emits = defineEmits(['load'])
const onLoad = (e) => {
const img = e.target
const ratio = img.naturalWidth / img.naturalHeight
const maxRatio = 300 / 200 // 最大宽高比
const maxWidth = 360
const maxHeight = 180
// 如果图片尺寸在限制范围内,保持原始尺寸
if (img.naturalWidth <= 300 && img.naturalHeight <= 200) {
img.style.width = img.naturalWidth + 'px'
img.style.height = img.naturalHeight + 'px'
} else if (ratio > maxRatio) {
if (img.naturalWidth / img.naturalHeight > maxWidth / maxHeight) {
// 如果图片更宽,以宽度为基准
img.style.width = '300px'
img.style.width = maxWidth + 'px'
img.style.height = 'auto'
} else {
// 如果图片更高,以高度为基准
img.style.height = '200px'
img.style.height = maxHeight + 'px'
img.style.width = 'auto'
}
@@ -63,8 +59,6 @@ const formatSize = computed(() => {
position: relative;
.el-image {
max-width: 300px;
max-height: 200px;
width: auto;
height: auto;

View File

@@ -4,14 +4,14 @@ import '@vueup/vue-quill/dist/vue-quill.snow.css'
import { onMounted, onUnmounted, onBeforeUnmount, ref, watch } from 'vue'
import { v4 as uuidv4 } from 'uuid'
import { useMessageStore, useImageStore } from '@/stores'
import { ElMessage, ElLoading } from 'element-plus'
import { ElMessage } from 'element-plus'
import { emojiTrans, getEmojiHtml } from '@/js/utils/emojis'
import { base64ToFile } from '@/js/utils/common'
import { mtsUploadService } from '@/api/mts'
import { el_loading_options } from '@/const/commonConst'
import { msgContentType, msgFileUploadStatus } from '@/const/msgConst'
const props = defineProps(['sessionId', 'draft'])
const emit = defineEmits(['sendMessage'])
const emit = defineEmits(['saveLocalMsg', 'sendMessage'])
const messageData = useMessageStore()
const imageData = useImageStore()
@@ -37,15 +37,36 @@ onMounted(async () => {
})
onBeforeUnmount(async () => {
let content = await getContent()
// 草稿若没发生变动,则不触发存储
const contentObj = parseContent()
const draft = messageData.sessionList[props.sessionId]?.draft
const content = contentObj.contentFromLocal.join('').trim()
// 草稿若发生变动,则触发存储
if (content && draft && content !== draft) {
messageData.updateSession({
sessionId: props.sessionId,
draft: content
})
}
// 有图片需要上传再保存一次draft
if (contentObj.needUploadCount.value > 0) {
const stopWatch = watch(
() => contentObj.uploadedTotalCount.value,
() => {
if (contentObj.needUploadCount.value === contentObj.uploadedTotalCount.value) {
// 满足第一个相等条件就停止监视
stopWatch()
if (contentObj.uploadSuccessCount.value === contentObj.needUploadCount.value) {
// 满足第二个相等条件才保存草稿
messageData.updateSession({
sessionId: props.sessionId,
draft: contentObj.contentFromServer.join('').trim()
})
}
}
}
)
}
})
onUnmounted(() => {
@@ -58,54 +79,114 @@ onUnmounted(() => {
}
})
const getContent = async () => {
const parseContent = (sessionId = props.sessionId) => {
const delta = getQuill().getContents()
let content = ''
let contentFromLocal = new Array(delta.ops.length).fill('')
let contentFromServer = new Array(delta.ops.length).fill('')
let needUploadCount = ref(0) // 需要上传的图片个数
let uploadedTotalCount = ref(0) // 已发上传请求的图片个数,包括上传成功和失败
let uploadSuccessCount = ref(0) // 已经上传成功的图片个数
for (let index = 0; index < delta.ops.length; index++) {
const op = delta.ops[index]
const insert = op.insert
if (insert && typeof insert === 'string') {
// 文本
content = content + insert
contentFromLocal[index] = insert
contentFromServer[index] = insert
} else if (insert && insert.image) {
const alt = op.attributes?.alt
if (alt && alt.startsWith('[') && alt.endsWith(']')) {
// 表情
content = content + alt
// 表情id
contentFromLocal[index] = alt
contentFromServer[index] = alt
} else if (alt && alt.startsWith('{') && alt.endsWith('}')) {
// 图片
content = content + alt
// 图片id
contentFromLocal[index] = alt
contentFromServer[index] = alt
} else if (insert.image.startsWith('data:') && insert.image.includes('base64')) {
// base64编码的图片
needUploadCount.value++
const file = base64ToFile(insert.image, uuidv4()) // base64转file
el_loading_options.text = '图片上传中...' //上传中加一个loading效果
const loadingInstance = ElLoading.service(el_loading_options)
try {
const res = await mtsUploadService({ file: file, storeType: 1 }) //上传图片至服务端
imageData.setImage(props.sessionId, res.data.data) // 缓存image数据
content = content + `{${res.data.data.objectId}}`
} finally {
loadingInstance.close()
}
const tempObjectId = new Date().getTime()
// 发送的时候设置本地缓存(非服务端数据),用于立即渲染
const localSrc = URL.createObjectURL(file)
imageData.setLocalImage({
objectId: tempObjectId,
originUrl: localSrc,
thumbUrl: localSrc,
fileName: file.name,
size: file.size
})
contentFromLocal[index] = `{${tempObjectId}}`
//上传图片至服务端
mtsUploadService({ file: file, storeType: 1 })
.then((res) => {
imageData.setServerImage(sessionId, res.data.data) // 缓存image数据
uploadSuccessCount.value++
contentFromServer[index] = `{${res.data.data.objectId}}`
// TODO 这里要判断是最后一个上传的图片
// 这里用异步有个问题后面请求上传的图片传的块在content中就会跑到前面去图片的顺序会错乱
// 可以把content设成一个数组按照index下标给每个数组元素设置防止乱序
// 这样也可以watch每个元素如果都填满就sendMessage
})
.finally(() => {
uploadedTotalCount.value++
})
} else {
// 当文本处理
contentFromLocal[index] = insert
contentFromServer[index] = insert
}
}
}
return content.trim()
return {
needUploadCount: needUploadCount,
uploadedTotalCount: uploadedTotalCount,
uploadSuccessCount: uploadSuccessCount,
contentFromLocal: contentFromLocal,
contentFromServer: contentFromServer
}
}
// 监控session发生了切换
watch(
() => props.sessionId,
async (newValue, oldValue) => {
let content = await getContent()
// 草稿若没发生变动,则不触发存储
if (oldValue && content !== messageData.sessionList[oldValue].draft) {
async (newSessionId, oldSessionId) => {
const contentObj = parseContent(oldSessionId)
const content = contentObj.contentFromLocal.join('').trim()
// 草稿若发生变动,则触发存储
if (oldSessionId && content !== messageData.sessionList[oldSessionId].draft) {
messageData.updateSession({
sessionId: oldValue,
sessionId: oldSessionId,
draft: content
})
}
formatContent(messageData.sessionList[newValue].draft || '')
// 有图片需要上传再保存一次draft
if (contentObj.needUploadCount.value > 0) {
const stopWatch = watch(
() => contentObj.uploadedTotalCount.value,
() => {
if (contentObj.needUploadCount.value === contentObj.uploadedTotalCount.value) {
// 满足第一个相等条件就停止监视
stopWatch()
if (contentObj.uploadSuccessCount.value === contentObj.needUploadCount.value) {
// 满足第二个相等条件才保存草稿
if (oldSessionId) {
messageData.updateSession({
sessionId: oldSessionId,
draft: contentObj.contentFromServer.join('').trim()
})
}
}
}
}
)
}
formatContent(messageData.sessionList[newSessionId].draft || '')
},
{ deep: true }
)
@@ -120,16 +201,56 @@ const formatContent = (content) => {
}
const handleEnter = async () => {
const content = await getContent()
const contentObj = parseContent()
const content = contentObj.contentFromLocal.join('').trim()
if (!content) {
ElMessage.warning('请勿发送空内容')
getQuill().setText('')
return
} else if (content.length > 3000) {
ElMessage.warning('发送内容请不要超过3000个字')
} else {
emit('sendMessage', content)
getQuill().setText('')
return
}
if (contentObj.needUploadCount.value === 0) {
emit('sendMessage', content)
} else {
// 发送的时候设置本地缓存(非服务端数据),用于立即渲染
let msg = {}
emit('saveLocalMsg', {
contentType: msgContentType.MIX,
content: content,
fn: (result) => {
msg = result
}
})
// 有图片需要上传
if (contentObj.needUploadCount.value > 0) {
msg.uploadStatus = msgFileUploadStatus.UPLOADING
msg.uploadProgress = 0
}
// 监视图片上传结果,图片上传完后向服务器发送消息
const stopWatch = watch(
() => contentObj.uploadedTotalCount.value,
() => {
msg.uploadProgress = Math.floor(
(contentObj.uploadSuccessCount.value / contentObj.needUploadCount.value) * 100
)
if (contentObj.uploadedTotalCount.value === contentObj.needUploadCount.value) {
stopWatch()
if (contentObj.uploadSuccessCount.value === contentObj.needUploadCount.value) {
msg.uploadStatus = msgFileUploadStatus.UPLOAD_SUCCESS
msg.content = contentObj.contentFromServer.join('').trim()
emit('sendMessage', msg)
}
} else {
msg.uploadStatus = msgFileUploadStatus.UPLOAD_FAILED
}
}
)
}
getQuill().setText('') // 编辑窗口置空
}
/**

View File

@@ -1,14 +1,14 @@
<script setup>
import { ref, onMounted, onUnmounted, watch, computed } from 'vue'
import { Microphone } from '@element-plus/icons-vue'
import { ElLoading, ElMessage } from 'element-plus'
import { ElMessage } from 'element-plus'
import { useAudioStore } from '@/stores'
import { mtsUploadService } from '@/api/mts'
import { el_loading_options } from '@/const/commonConst'
import { v4 as uuidv4 } from 'uuid'
import { msgContentType, msgFileUploadStatus } from '@/const/msgConst'
const props = defineProps(['sessionId'])
const emit = defineEmits(['exit', 'sendRecording'])
const emit = defineEmits(['exit', 'sendMessage', 'saveLocalMsg'])
const audioData = useAudioStore()
const spaceDown = ref(false) // 空格键是否被按下
@@ -144,18 +144,47 @@ const stopRecording = () => {
}
const uploadRecord = () => {
const loadingInstance = ElLoading.service(el_loading_options)
const fileName = `${uuidv4()}.${fileSuffix}`
const file = new File([recordBlob.value], fileName, { type: recordType })
mtsUploadService({ file, storeType: 1, duration: Math.floor(recordDuration / 1000) })
// 发送的时候设置本地缓存(非服务端数据),用于立即渲染
const duration = Math.floor(recordDuration / 1000)
const localSrc = URL.createObjectURL(file)
const tempObjectId = new Date().getTime()
audioData.setAudio(props.sessionId, {
objectId: tempObjectId,
duration: duration,
url: localSrc,
fileName: file.name,
size: file.size
})
let msg = {}
emit('saveLocalMsg', {
contentType: msgContentType.RECORDING,
objectId: tempObjectId,
fn: (result) => {
msg = result
}
})
msg.uploadStatus = msgFileUploadStatus.UPLOADING
msg.uploadProgress = 0
mtsUploadService({ file, storeType: 1, duration: duration })
.then((res) => {
if (res.data.code === 0) {
audioData.setAudio(props.sessionId, res.data.data) // 缓存audio数据
emit('sendRecording', res.data.data)
audioData.setAudio(props.sessionId, res.data.data) // 缓存服务端响应的audio数据
msg.uploadStatus = msgFileUploadStatus.UPLOAD_SUCCESS
msg.uploadProgress = 100
msg.content = JSON.stringify({
type: msgContentType.RECORDING,
value: res.data.data.objectId
})
emit('sendMessage', msg)
}
})
.finally(() => {
loadingInstance.close()
.catch(() => {
msg.uploadStatus = msgFileUploadStatus.UPLOAD_FAILED
ElMessage.error('上传失败')
})
}

View File

@@ -1,7 +1,7 @@
<script setup>
import { ref } from 'vue'
import { Clock, Microphone } from '@element-plus/icons-vue'
import { ElMessage, ElLoading } from 'element-plus'
import { ElMessage } from 'element-plus'
import EmojiIcon from '@/assets/svg/emoji.svg'
import FileIcon from '@/assets/svg/file.svg'
import ImageIcon from '@/assets/svg/image.svg'
@@ -17,18 +17,11 @@ import {
useVideoStore,
useDocumentStore
} from '@/stores'
import { el_loading_options } from '@/const/commonConst'
import { MsgType } from '@/proto/msg'
import { msgContentType, msgFileUploadStatus } from '@/const/msgConst'
const props = defineProps(['sessionId', 'isShowToolSet'])
const emit = defineEmits([
'sendEmoji',
'sendImage',
'sendAudio',
'sendVideo',
'sendDocument',
'showRecorder'
])
const emit = defineEmits(['sendEmoji', 'showRecorder', 'sendMessage', 'saveLocalMsg'])
const messageData = useMessageStore()
const imageData = useImageStore()
@@ -42,57 +35,110 @@ const onSelectedFile = (file) => {
return
}
if (file.raw.type && file.raw.type.startsWith('image/')) {
const loadingInstance = ElLoading.service(el_loading_options)
mtsUploadService({ file: file.raw, storeType: 1 })
.then((res) => {
if (res.data.code === 0) {
imageData.setImage(props.sessionId, res.data.data) // 缓存image数据
emit('sendImage', res.data.data)
}
let contentType = msgContentType.DOCUMENT
if (file.raw.type.startsWith('image/')) {
contentType = msgContentType.IMAGE
} else if (file.raw.type.startsWith('audio/')) {
contentType = msgContentType.AUDIO
} else if (file.raw.type.startsWith('video/')) {
contentType = msgContentType.VIDEO
}
setLocalData(contentType, file)
let msg = {}
emit('saveLocalMsg', {
contentType: contentType,
objectId: file.uid,
fn: (result) => {
msg = result
}
})
msg.uploadStatus = msgFileUploadStatus.UPLOADING
msg.uploadProgress = 0
mtsUploadService({ file: file.raw, storeType: 1 })
.then((res) => {
if (res.data.code === 0) {
setStoreData(contentType, res.data.data)
msg.uploadStatus = msgFileUploadStatus.UPLOAD_SUCCESS
msg.uploadProgress = 100
msg.content = JSON.stringify({ type: contentType, value: res.data.data.objectId })
emit('sendMessage', msg)
}
})
.catch(() => {
msg.uploadStatus = msgFileUploadStatus.UPLOAD_FAILED
ElMessage.error('上传失败')
})
}
/**
* 发送的时候设置本地缓存(非服务端数据),用于立即渲染
* @param contentType
* @param file
*/
const setLocalData = (contentType, file) => {
const localSrc = URL.createObjectURL(file.raw)
switch (contentType) {
case msgContentType.IMAGE:
imageData.setLocalImage({
objectId: file.uid,
originUrl: localSrc,
thumbUrl: localSrc,
fileName: file.name,
size: file.raw.size
})
.finally(() => {
loadingInstance.close()
break
case msgContentType.AUDIO:
audioData.setAudio(props.sessionId, {
objectId: file.uid,
url: localSrc,
fileName: file.name,
size: file.raw.size
})
} else if (file.raw.type && file.raw.type.startsWith('audio/')) {
const loadingInstance = ElLoading.service(el_loading_options)
mtsUploadService({ file: file.raw, storeType: 1 })
.then((res) => {
if (res.data.code === 0) {
audioData.setAudio(props.sessionId, res.data.data) // 缓存audio的数据
emit('sendAudio', res.data.data)
}
break
case msgContentType.VIDEO:
videoData.setVideo(props.sessionId, {
objectId: file.uid,
url: localSrc,
fileName: file.name,
size: file.raw.size
})
.finally(() => {
loadingInstance.close()
})
} else if (file.raw.type && file.raw.type.startsWith('video/')) {
const loadingInstance = ElLoading.service(el_loading_options)
mtsUploadService({ file: file.raw, storeType: 1 })
.then((res) => {
if (res.data.code === 0) {
videoData.setVideo(props.sessionId, res.data.data) // 缓存video的数据
emit('sendVideo', res.data.data)
}
})
.finally(() => {
loadingInstance.close()
})
} else {
const loadingInstance = ElLoading.service(el_loading_options)
mtsUploadService({ file: file.raw, storeType: 1 })
.then((res) => {
if (res.data.code === 0) {
documentData.setDocument(props.sessionId, res.data.data) // 缓存video的数据
emit('sendDocument', res.data.data)
}
})
.finally(() => {
loadingInstance.close()
break
case msgContentType.DOCUMENT:
default:
documentData.setDocument(props.sessionId, {
objectId: file.uid,
documentType: file.raw.type,
url: localSrc,
fileName: file.name,
size: file.raw.size
})
}
}
/**
* 服务端响应数据回来后设置store缓存
* @param contentType
* @param file
*/
const setStoreData = (contentType, data) => {
switch (contentType) {
case msgContentType.IMAGE:
imageData.setServerImage(props.sessionId, data)
break
case msgContentType.AUDIO:
audioData.setAudio(props.sessionId, data)
break
case msgContentType.VIDEO:
videoData.setVideo(props.sessionId, data)
break
case msgContentType.DOCUMENT:
default:
documentData.setDocument(props.sessionId, data)
}
}
const onSendEmoji = (key) => {
emit('sendEmoji', key)
}

View File

@@ -170,13 +170,14 @@ const renderImage = (content, ishowInfo = true) => {
const imgId = content
const url = imageData.image[imgId]?.thumbUrl
if (url) {
const imgIdList = imageData.imageInSession[props.sessionId].sort((a, b) => a - b)
const srcList = imgIdList.map((item) => imageData.image[item].originUrl)
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)
return h(ImageMsgBox, {
url,
imgId,
srcList,
initialIndex: imgIdList.indexOf(imgId),
initialIndex: imgIdListSorted.indexOf(imgId),
fileName: ishowInfo ? imageData.image[imgId].fileName : '',
size: ishowInfo ? imageData.image[imgId].size : '',
onLoad: () => {