feat(upload): 重构文件上传逻辑并添加全局上传任务管理
- 在dialogue store中添加globalUploadList和uploadTaskMap管理上传任务 - 修改PanelFooter.vue使用addUploadTask替代直接添加记录 - 在uploads.ts中完善上传失败处理和任务清理逻辑 - 在useTalkRecord.ts中加载完成后恢复上传任务 - 移除调试用的console.log语句
This commit is contained in:
parent
efd61b30f4
commit
871e33990a
7350
pnpm-lock.yaml
Normal file
7350
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ import {
|
|||||||
Pic,
|
Pic,
|
||||||
FolderUpload
|
FolderUpload
|
||||||
} from '@icon-park/vue-next'
|
} from '@icon-park/vue-next'
|
||||||
import {IosSend} from '@vicons/ionicons4'
|
import { IosSend } from '@vicons/ionicons4'
|
||||||
import { bus } from '@/utils/event-bus'
|
import { bus } from '@/utils/event-bus'
|
||||||
import { EditorConst } from '@/constant/event-bus'
|
import { EditorConst } from '@/constant/event-bus'
|
||||||
import { emitCall } from '@/utils/common'
|
import { emitCall } from '@/utils/common'
|
||||||
@ -33,7 +33,7 @@ const props = defineProps({
|
|||||||
const emit = defineEmits(['editor-event'])
|
const emit = defineEmits(['editor-event'])
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const dialogueStore = useDialogueStore()
|
const dialogueStore = useDialogueStore()
|
||||||
console.log('dialogueStore',dialogueStore.talk.talk_type)
|
console.log('dialogueStore', dialogueStore.talk.talk_type)
|
||||||
const editorDraftStore = useEditorDraftStore()
|
const editorDraftStore = useEditorDraftStore()
|
||||||
const editorRef = ref(null)
|
const editorRef = ref(null)
|
||||||
const content = ref('')
|
const content = ref('')
|
||||||
@ -86,27 +86,27 @@ const handleInput = (event) => {
|
|||||||
}
|
}
|
||||||
const target = editorNode;
|
const target = editorNode;
|
||||||
const editorClone = editorNode.cloneNode(true);
|
const editorClone = editorNode.cloneNode(true);
|
||||||
editorClone.querySelectorAll('.editor-quote').forEach(quote => quote.remove());
|
editorClone.querySelectorAll('.editor-quote').forEach(quote => quote.remove());
|
||||||
let rawTextContent = editorClone.textContent || '';
|
let rawTextContent = editorClone.textContent || '';
|
||||||
const emojiImages = editorClone.querySelectorAll('img.editor-emoji');
|
const emojiImages = editorClone.querySelectorAll('img.editor-emoji');
|
||||||
if (emojiImages.length > 0) {
|
if (emojiImages.length > 0) {
|
||||||
emojiImages.forEach(emoji => {
|
emojiImages.forEach(emoji => {
|
||||||
const altText = emoji.getAttribute('alt');
|
const altText = emoji.getAttribute('alt');
|
||||||
if (altText) {
|
if (altText) {
|
||||||
rawTextContent += altText;
|
rawTextContent += altText;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
editorContent.value = rawTextContent;
|
editorContent.value = rawTextContent;
|
||||||
const currentText = editorNode.textContent.trim();
|
const currentText = editorNode.textContent.trim();
|
||||||
const hasSpecialElements = editorNode.querySelector('img, .editor-file, .mention');
|
const hasSpecialElements = editorNode.querySelector('img, .editor-file, .mention');
|
||||||
if (currentText === '' && !hasSpecialElements) {
|
if (currentText === '' && !hasSpecialElements) {
|
||||||
if (editorNode.innerHTML !== '') {
|
if (editorNode.innerHTML !== '') {
|
||||||
editorNode.innerHTML = '';
|
editorNode.innerHTML = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
editorHtml.value = editorNode.innerHTML || '';
|
editorHtml.value = editorNode.innerHTML || '';
|
||||||
const currentEditorItems = parseEditorContent().items;
|
const currentEditorItems = parseEditorContent().items;
|
||||||
checkMention(target);
|
checkMention(target);
|
||||||
saveDraft();
|
saveDraft();
|
||||||
emit('editor-event', {
|
emit('editor-event', {
|
||||||
@ -114,7 +114,7 @@ const handleInput = (event) => {
|
|||||||
data: currentEditorItems.reduce((result, item) => {
|
data: currentEditorItems.reduce((result, item) => {
|
||||||
if (item.type === 1) return result + item.content;
|
if (item.type === 1) return result + item.content;
|
||||||
if (item.type === 3) return result + '[图片]';
|
if (item.type === 3) return result + '[图片]';
|
||||||
return result;
|
return result;
|
||||||
}, '')
|
}, '')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -138,7 +138,7 @@ const checkMention = (target) => {
|
|||||||
}
|
}
|
||||||
const showMentionList = () => {
|
const showMentionList = () => {
|
||||||
const query = currentMentionQuery.value.toLowerCase()
|
const query = currentMentionQuery.value.toLowerCase()
|
||||||
mentionList.value = [{ id: 0, nickname: '全体成员', avatar: defAvatar, value: '全体成员' },...props.members].filter(member => {
|
mentionList.value = [{ id: 0, nickname: '全体成员', avatar: defAvatar, value: '全体成员' }, ...props.members].filter(member => {
|
||||||
return member.value.toLowerCase().startsWith(query) && member.id !== userStore.uid
|
return member.value.toLowerCase().startsWith(query) && member.id !== userStore.uid
|
||||||
})
|
})
|
||||||
showMention.value = mentionList.value.length > 0
|
showMention.value = mentionList.value.length > 0
|
||||||
@ -435,9 +435,9 @@ const handleKeydown = (event) => {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const messageData = parseEditorContent();
|
const messageData = parseEditorContent();
|
||||||
const isEmptyMessage = messageData.items.length === 0 ||
|
const isEmptyMessage = messageData.items.length === 0 ||
|
||||||
(messageData.items.length === 1 &&
|
(messageData.items.length === 1 &&
|
||||||
messageData.items[0].type === 1 &&
|
messageData.items[0].type === 1 &&
|
||||||
!messageData.items[0].content.trimEnd());
|
!messageData.items[0].content.trimEnd());
|
||||||
if (isEmptyMessage) {
|
if (isEmptyMessage) {
|
||||||
if (editor.innerHTML.trim() !== '' && editor.innerHTML.trim() !== '<br>') {
|
if (editor.innerHTML.trim() !== '' && editor.innerHTML.trim() !== '<br>') {
|
||||||
clearEditor();
|
clearEditor();
|
||||||
@ -498,38 +498,38 @@ const sendMessage = () => {
|
|||||||
}
|
}
|
||||||
messageToSend.items.forEach(item => {
|
messageToSend.items.forEach(item => {
|
||||||
if (item.type === 1 && cleanInvisibleChars(item.content.trimEnd())) {
|
if (item.type === 1 && cleanInvisibleChars(item.content.trimEnd())) {
|
||||||
const data = {
|
const data = {
|
||||||
items: [{
|
items: [{
|
||||||
content: cleanInvisibleChars(item.content),
|
content: cleanInvisibleChars(item.content),
|
||||||
type: 1
|
type: 1
|
||||||
}],
|
}],
|
||||||
mentionUids: messageToSend.mentionUids,
|
mentionUids: messageToSend.mentionUids,
|
||||||
mentions: messageToSend.mentionUids.map(uid => {
|
mentions: messageToSend.mentionUids.map(uid => {
|
||||||
return {
|
return {
|
||||||
atid: uid,
|
atid: uid,
|
||||||
name: mentionList.value.find(member => member.id === uid)?.nickname || ''
|
name: mentionList.value.find(member => member.id === uid)?.nickname || ''
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
quoteId: messageToSend.quoteId,
|
quoteId: messageToSend.quoteId,
|
||||||
}
|
}
|
||||||
emit(
|
emit(
|
||||||
'editor-event',
|
'editor-event',
|
||||||
emitCall('text_event', data)
|
emitCall('text_event', data)
|
||||||
)
|
)
|
||||||
} else if (item.type === 3) {
|
} else if (item.type === 3) {
|
||||||
const data = {
|
const data = {
|
||||||
height: 0,
|
height: 0,
|
||||||
width: 0,
|
width: 0,
|
||||||
size: 10000,
|
size: 10000,
|
||||||
url: item.content,
|
url: item.content,
|
||||||
}
|
}
|
||||||
emit(
|
emit(
|
||||||
'editor-event',
|
'editor-event',
|
||||||
emitCall('image_event', data)
|
emitCall('image_event', data)
|
||||||
)
|
)
|
||||||
} else if (item.type === 4) {
|
} else if (item.type === 4) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
clearEditor();
|
clearEditor();
|
||||||
}
|
}
|
||||||
const parseEditorContent = () => {
|
const parseEditorContent = () => {
|
||||||
@ -776,23 +776,23 @@ const onUploadSendImg = async (event) => {
|
|||||||
console.error('Image upload failed or received invalid response:', res);
|
console.error('Image upload failed or received invalid response:', res);
|
||||||
const previewImages = editorRef.value.querySelectorAll('img[data-status="local-preview"][src^="data:image"]:not([data-uploaded-url])');
|
const previewImages = editorRef.value.querySelectorAll('img[data-status="local-preview"][src^="data:image"]:not([data-uploaded-url])');
|
||||||
if (previewImages.length > 0) {
|
if (previewImages.length > 0) {
|
||||||
const lastPreviewImage = previewImages[previewImages.length -1];
|
const lastPreviewImage = previewImages[previewImages.length - 1];
|
||||||
if(lastPreviewImage) {
|
if (lastPreviewImage) {
|
||||||
lastPreviewImage.style.border = '2px dashed red';
|
lastPreviewImage.style.border = '2px dashed red';
|
||||||
lastPreviewImage.title = 'Upload failed';
|
lastPreviewImage.title = 'Upload failed';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error during image upload process:', error);
|
console.error('Error during image upload process:', error);
|
||||||
const previewImages = editorRef.value.querySelectorAll('img[data-status="local-preview"][src^="data:image"]:not([data-uploaded-url])');
|
const previewImages = editorRef.value.querySelectorAll('img[data-status="local-preview"][src^="data:image"]:not([data-uploaded-url])');
|
||||||
if (previewImages.length > 0) {
|
if (previewImages.length > 0) {
|
||||||
const lastPreviewImage = previewImages[previewImages.length -1];
|
const lastPreviewImage = previewImages[previewImages.length - 1];
|
||||||
if(lastPreviewImage) {
|
if (lastPreviewImage) {
|
||||||
lastPreviewImage.style.border = '2px dashed red';
|
lastPreviewImage.style.border = '2px dashed red';
|
||||||
lastPreviewImage.title = 'Upload error';
|
lastPreviewImage.title = 'Upload error';
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (event.target) event.target.value = '';
|
if (event.target) event.target.value = '';
|
||||||
@ -927,17 +927,17 @@ const onSubscribeMention = async (data) => {
|
|||||||
fallbackRange.selectNodeContents(editorNode);
|
fallbackRange.selectNodeContents(editorNode);
|
||||||
fallbackRange.collapse(false);
|
fallbackRange.collapse(false);
|
||||||
const newSelection = window.getSelection();
|
const newSelection = window.getSelection();
|
||||||
if (newSelection){
|
if (newSelection) {
|
||||||
newSelection.removeAllRanges();
|
newSelection.removeAllRanges();
|
||||||
newSelection.addRange(fallbackRange);
|
newSelection.addRange(fallbackRange);
|
||||||
insertMention(data, fallbackRange);
|
insertMention(data, fallbackRange);
|
||||||
} else {
|
} else {
|
||||||
console.error("Could not get window selection to insert mention.");
|
console.error("Could not get window selection to insert mention.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleDeleteQuote = function(e) {
|
const handleDeleteQuote = function (e) {
|
||||||
if (e.key !== 'Backspace' && e.key !== 'Delete') return;
|
if (e.key !== 'Backspace' && e.key !== 'Delete') return;
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
if (selection.rangeCount === 0) return;
|
if (selection.rangeCount === 0) return;
|
||||||
const range = selection.getRangeAt(0);
|
const range = selection.getRangeAt(0);
|
||||||
@ -945,23 +945,23 @@ const handleDeleteQuote = function(e) {
|
|||||||
if (!editor) return;
|
if (!editor) return;
|
||||||
const quoteElement = editor.querySelector('.editor-quote');
|
const quoteElement = editor.querySelector('.editor-quote');
|
||||||
if (!quoteElement) {
|
if (!quoteElement) {
|
||||||
editor.removeEventListener('keydown', handleDeleteQuote);
|
editor.removeEventListener('keydown', handleDeleteQuote);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const quoteIndex = Array.from(editor.childNodes).indexOf(quoteElement);
|
const quoteIndex = Array.from(editor.childNodes).indexOf(quoteElement);
|
||||||
const isBeforeQuote = e.key === 'Backspace' &&
|
const isBeforeQuote = e.key === 'Backspace' &&
|
||||||
range.collapsed &&
|
range.collapsed &&
|
||||||
range.startContainer === editor &&
|
range.startContainer === editor &&
|
||||||
quoteIndex === range.startOffset;
|
quoteIndex === range.startOffset;
|
||||||
const isAfterQuote = e.key === 'Delete' &&
|
const isAfterQuote = e.key === 'Delete' &&
|
||||||
range.collapsed &&
|
range.collapsed &&
|
||||||
range.startContainer === editor &&
|
range.startContainer === editor &&
|
||||||
quoteIndex === range.startOffset - 1;
|
quoteIndex === range.startOffset - 1;
|
||||||
if (isBeforeQuote || isAfterQuote) {
|
if (isBeforeQuote || isAfterQuote) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
quoteElement.remove();
|
quoteElement.remove();
|
||||||
quoteData.value = null;
|
quoteData.value = null;
|
||||||
handleInput({ target: editor });
|
handleInput({ target: editor });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onSubscribeQuote = (data) => {
|
const onSubscribeQuote = (data) => {
|
||||||
@ -974,7 +974,7 @@ const onSubscribeQuote = (data) => {
|
|||||||
if (selection && selection.rangeCount > 0) {
|
if (selection && selection.rangeCount > 0) {
|
||||||
const currentRange = selection.getRangeAt(0);
|
const currentRange = selection.getRangeAt(0);
|
||||||
if (editor.contains(currentRange.commonAncestorContainer)) {
|
if (editor.contains(currentRange.commonAncestorContainer)) {
|
||||||
savedRange = currentRange.cloneRange();
|
savedRange = currentRange.cloneRange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const quoteElement = document.createElement('div');
|
const quoteElement = document.createElement('div');
|
||||||
@ -1014,11 +1014,11 @@ const onSubscribeQuote = (data) => {
|
|||||||
let nodeToPlaceCursorAfter = quoteElement;
|
let nodeToPlaceCursorAfter = quoteElement;
|
||||||
const zeroWidthSpace = document.createTextNode('\u200B');
|
const zeroWidthSpace = document.createTextNode('\u200B');
|
||||||
if (editor.lastChild === quoteElement || !quoteElement.nextSibling) {
|
if (editor.lastChild === quoteElement || !quoteElement.nextSibling) {
|
||||||
editor.appendChild(zeroWidthSpace);
|
editor.appendChild(zeroWidthSpace);
|
||||||
nodeToPlaceCursorAfter = zeroWidthSpace;
|
nodeToPlaceCursorAfter = zeroWidthSpace;
|
||||||
} else {
|
} else {
|
||||||
editor.insertBefore(zeroWidthSpace, quoteElement.nextSibling);
|
editor.insertBefore(zeroWidthSpace, quoteElement.nextSibling);
|
||||||
nodeToPlaceCursorAfter = zeroWidthSpace;
|
nodeToPlaceCursorAfter = zeroWidthSpace;
|
||||||
}
|
}
|
||||||
const handleQuoteClick = (e) => {
|
const handleQuoteClick = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -1050,30 +1050,30 @@ const onSubscribeQuote = (data) => {
|
|||||||
if (!newSelection) return;
|
if (!newSelection) return;
|
||||||
let cursorPlaced = false;
|
let cursorPlaced = false;
|
||||||
if (savedRange) {
|
if (savedRange) {
|
||||||
try {
|
try {
|
||||||
if (editor.contains(savedRange.commonAncestorContainer) && savedRange.startContainer) {
|
if (editor.contains(savedRange.commonAncestorContainer) && savedRange.startContainer) {
|
||||||
newSelection.removeAllRanges();
|
newSelection.removeAllRanges();
|
||||||
newSelection.addRange(savedRange);
|
newSelection.addRange(savedRange);
|
||||||
cursorPlaced = true;
|
cursorPlaced = true;
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!cursorPlaced) {
|
if (!cursorPlaced) {
|
||||||
const newRange = document.createRange();
|
const newRange = document.createRange();
|
||||||
if (nodeToPlaceCursorAfter && nodeToPlaceCursorAfter.parentNode === editor) {
|
if (nodeToPlaceCursorAfter && nodeToPlaceCursorAfter.parentNode === editor) {
|
||||||
newRange.setStartAfter(nodeToPlaceCursorAfter);
|
newRange.setStartAfter(nodeToPlaceCursorAfter);
|
||||||
} else if (quoteElement.parentNode === editor && quoteElement.nextSibling) {
|
} else if (quoteElement.parentNode === editor && quoteElement.nextSibling) {
|
||||||
newRange.setStartAfter(quoteElement.nextSibling);
|
newRange.setStartAfter(quoteElement.nextSibling);
|
||||||
} else if (quoteElement.parentNode === editor) {
|
} else if (quoteElement.parentNode === editor) {
|
||||||
newRange.setStartAfter(quoteElement);
|
newRange.setStartAfter(quoteElement);
|
||||||
} else {
|
} else {
|
||||||
newRange.selectNodeContents(editor);
|
newRange.selectNodeContents(editor);
|
||||||
newRange.collapse(false);
|
newRange.collapse(false);
|
||||||
}
|
}
|
||||||
newRange.collapse(true);
|
newRange.collapse(true);
|
||||||
newSelection.removeAllRanges();
|
newSelection.removeAllRanges();
|
||||||
newSelection.addRange(newRange);
|
newSelection.addRange(newRange);
|
||||||
}
|
}
|
||||||
editor.scrollTop = editor.scrollHeight;
|
editor.scrollTop = editor.scrollHeight;
|
||||||
handleInput();
|
handleInput();
|
||||||
@ -1101,11 +1101,11 @@ const saveDraft = () => {
|
|||||||
quoteElements.forEach(quote => quote.remove())
|
quoteElements.forEach(quote => quote.remove())
|
||||||
const contentToSave = tempDiv.textContent || ''
|
const contentToSave = tempDiv.textContent || ''
|
||||||
const htmlToSave = tempDiv.innerHTML || ''
|
const htmlToSave = tempDiv.innerHTML || ''
|
||||||
const currentEditor= parseEditorContent().items
|
const currentEditor = parseEditorContent().items
|
||||||
const hasContent = contentToSave.trim().length > 0 ||
|
const hasContent = contentToSave.trim().length > 0 ||
|
||||||
htmlToSave.includes('<img') ||
|
htmlToSave.includes('<img') ||
|
||||||
htmlToSave.includes('editor-file')
|
htmlToSave.includes('editor-file')
|
||||||
if (currentEditor.length>0) {
|
if (currentEditor.length > 0) {
|
||||||
editorDraftStore.items[indexName.value] = JSON.stringify({
|
editorDraftStore.items[indexName.value] = JSON.stringify({
|
||||||
content: currentEditor.reduce((result, x) => {
|
content: currentEditor.reduce((result, x) => {
|
||||||
if (x.type === 3) return result + '[图片]'
|
if (x.type === 3) return result + '[图片]'
|
||||||
@ -1188,7 +1188,7 @@ const onCodeSubmit = (data) => {
|
|||||||
emit('editor-event', {
|
emit('editor-event', {
|
||||||
event: 'code_event',
|
event: 'code_event',
|
||||||
data,
|
data,
|
||||||
callBack: () => {}
|
callBack: () => { }
|
||||||
})
|
})
|
||||||
isShowCode.value = false
|
isShowCode.value = false
|
||||||
}
|
}
|
||||||
@ -1196,7 +1196,7 @@ const onVoteSubmit = (data) => {
|
|||||||
emit('editor-event', {
|
emit('editor-event', {
|
||||||
event: 'vote_event',
|
event: 'vote_event',
|
||||||
data,
|
data,
|
||||||
callBack: () => {}
|
callBack: () => { }
|
||||||
})
|
})
|
||||||
isShowVote.value = false
|
isShowVote.value = false
|
||||||
}
|
}
|
||||||
@ -1223,15 +1223,8 @@ const handleEditorClick = (event) => {
|
|||||||
<section class="el-container is-vertical">
|
<section class="el-container is-vertical">
|
||||||
<header class="el-header toolbar bdr-t">
|
<header class="el-header toolbar bdr-t">
|
||||||
<div class="tools pr-30px">
|
<div class="tools pr-30px">
|
||||||
<n-popover
|
<n-popover placement="top-start" trigger="click" raw :show-arrow="false" :width="300" ref="emoticonRef"
|
||||||
placement="top-start"
|
style="width: 500px; height: 250px; border-radius: 10px; overflow: hidden">
|
||||||
trigger="click"
|
|
||||||
raw
|
|
||||||
:show-arrow="false"
|
|
||||||
:width="300"
|
|
||||||
ref="emoticonRef"
|
|
||||||
style="width: 500px; height: 250px; border-radius: 10px; overflow: hidden"
|
|
||||||
>
|
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<div class="item pointer">
|
<div class="item pointer">
|
||||||
<n-icon size="18" class="icon" :component="SmilingFace" />
|
<n-icon size="18" class="icon" :component="SmilingFace" />
|
||||||
@ -1240,52 +1233,30 @@ const handleEditorClick = (event) => {
|
|||||||
</template>
|
</template>
|
||||||
<MeEditorEmoticon @on-select="onEmoticonEvent" />
|
<MeEditorEmoticon @on-select="onEmoticonEvent" />
|
||||||
</n-popover>
|
</n-popover>
|
||||||
<div
|
<div class="item pointer" v-for="nav in navs" :key="nav.title" v-show="nav.show" @click="nav.click">
|
||||||
class="item pointer"
|
|
||||||
v-for="nav in navs"
|
|
||||||
:key="nav.title"
|
|
||||||
v-show="nav.show"
|
|
||||||
@click="nav.click"
|
|
||||||
>
|
|
||||||
<n-icon size="18" class="icon" :component="nav.icon" />
|
<n-icon size="18" class="icon" :component="nav.icon" />
|
||||||
<p class="tip-title">{{ nav.title }}</p>
|
<p class="tip-title">{{ nav.title }}</p>
|
||||||
</div>
|
</div>
|
||||||
<n-button class="w-80px h-30px ml-auto" type="primary" @click="sendMessage">
|
<n-button class="w-80px h-30px ml-auto" type="primary" @click="sendMessage">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon>
|
<n-icon>
|
||||||
<IosSend />
|
<IosSend />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
发送
|
发送
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main class="el-main height100">
|
<main class="el-main height100">
|
||||||
<div
|
<div ref="editorRef" class="custom-editor" contenteditable="true" :placeholder="placeholder"
|
||||||
ref="editorRef"
|
@input="handleInput" @keydown="handleKeydown" @paste="handlePaste" @focus="handleFocus" @blur="handleBlur">
|
||||||
class="custom-editor"
|
</div>
|
||||||
contenteditable="true"
|
<div v-if="showMention && dialogueStore.talk.talk_type === 2" class="mention-list py-5px"
|
||||||
:placeholder="placeholder"
|
:style="{ top: mentionPosition.top + 'px', left: mentionPosition.left + 'px' }">
|
||||||
@input="handleInput"
|
|
||||||
@keydown="handleKeydown"
|
|
||||||
@paste="handlePaste"
|
|
||||||
@focus="handleFocus"
|
|
||||||
@blur="handleBlur"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
v-if="showMention && dialogueStore.talk.talk_type === 2"
|
|
||||||
class="mention-list py-5px"
|
|
||||||
:style="{ top: mentionPosition.top + 'px', left: mentionPosition.left + 'px' }"
|
|
||||||
>
|
|
||||||
<ul class="max-h-140px w-163px overflow-auto hide-scrollbar">
|
<ul class="max-h-140px w-163px overflow-auto hide-scrollbar">
|
||||||
<li
|
<li v-for="(member, index) in mentionList" :key="member.user_id || member.id"
|
||||||
v-for="(member, index) in mentionList"
|
class="cursor-pointer px-14px h-42px" :class="{ 'bg-#EEE9F9': index === selectedMentionIndex }"
|
||||||
:key="member.user_id || member.id"
|
@mousedown.prevent="handleMentionSelectByMouse(member)" @mouseover="selectedMentionIndex = index">
|
||||||
class="cursor-pointer px-14px h-42px"
|
|
||||||
:class="{ 'bg-#EEE9F9': index === selectedMentionIndex }"
|
|
||||||
@mousedown.prevent="handleMentionSelectByMouse(member)"
|
|
||||||
@mouseover="selectedMentionIndex = index"
|
|
||||||
>
|
|
||||||
<div class="flex items-center border-b-1px border-b-solid border-b-#F8F8F8 h-full">
|
<div class="flex items-center border-b-1px border-b-solid border-b-#F8F8F8 h-full">
|
||||||
<img class="w-26px h-26px rounded-50% mr-11px" :src="member.avatar" alt="">
|
<img class="w-26px h-26px rounded-50% mr-11px" :src="member.avatar" alt="">
|
||||||
<span>{{ member.nickname }}</span>
|
<span>{{ member.nickname }}</span>
|
||||||
@ -1305,14 +1276,17 @@ const handleEditorClick = (event) => {
|
|||||||
.editor {
|
.editor {
|
||||||
--tip-bg-color: rgb(241 241 241 / 90%);
|
--tip-bg-color: rgb(241 241 241 / 90%);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
height: 38px;
|
height: 38px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
.tools {
|
.tools {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
flex: auto;
|
flex: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -1321,6 +1295,7 @@ const handleEditorClick = (event) => {
|
|||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
.tip-title {
|
.tip-title {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -1337,6 +1312,7 @@ const handleEditorClick = (event) => {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
z-index: 999999999999;
|
z-index: 999999999999;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.tip-title {
|
.tip-title {
|
||||||
display: block;
|
display: block;
|
||||||
@ -1345,6 +1321,7 @@ const handleEditorClick = (event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.editor-file) {
|
:deep(.editor-file) {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
@ -1361,6 +1338,7 @@ const handleEditorClick = (event) => {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: attr(data-size);
|
content: attr(data-size);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -1368,10 +1346,12 @@ const handleEditorClick = (event) => {
|
|||||||
color: #757575;
|
color: #757575;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #e3f2fd;
|
background-color: #e3f2fd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.editor-emoji) {
|
:deep(.editor-emoji) {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
@ -1379,6 +1359,7 @@ const handleEditorClick = (event) => {
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.editor-quote) {
|
:deep(.editor-quote) {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
@ -1394,18 +1375,22 @@ const handleEditorClick = (event) => {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
transition: background-color 0.2s ease;
|
transition: background-color 0.2s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--im-message-left-bg-hover-color, #eaeaea);
|
background-color: var(--im-message-left-bg-hover-color, #eaeaea);
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote-content-wrapper {
|
.quote-content-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote-title {
|
.quote-title {
|
||||||
color: var(--im-primary-color, #409eff);
|
color: var(--im-primary-color, #409eff);
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote-content {
|
.quote-content {
|
||||||
color: var(--im-text-color, #333);
|
color: var(--im-text-color, #333);
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
@ -1416,12 +1401,14 @@ const handleEditorClick = (event) => {
|
|||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote-image img {
|
.quote-image img {
|
||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
max-height: 60px;
|
max-height: 60px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote-close {
|
.quote-close {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
@ -1434,12 +1421,14 @@ const handleEditorClick = (event) => {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-editor {
|
.custom-editor {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -1452,44 +1441,53 @@ const handleEditorClick = (event) => {
|
|||||||
color: #333;
|
color: #333;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
&:empty:before {
|
&:empty:before {
|
||||||
content: attr(placeholder);
|
content: attr(placeholder);
|
||||||
color: #999;
|
color: #999;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 3px;
|
width: 3px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
background-color: unset;
|
background-color: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
background-color: var(--im-scrollbar-thumb);
|
background-color: var(--im-scrollbar-thumb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-editor:empty::before {
|
.custom-editor:empty::before {
|
||||||
content: attr(placeholder);
|
content: attr(placeholder);
|
||||||
color: #999;
|
color: #999;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
font-family: PingFang SC, Microsoft YaHei, 'Alibaba PuHuiTi 2.0 45' !important;
|
font-family: PingFang SC, Microsoft YaHei, 'Alibaba PuHuiTi 2.0 45' !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-editor:focus {
|
.custom-editor:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mention:hover {
|
.mention:hover {
|
||||||
background-color: #bae7ff;
|
background-color: #bae7ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-emoji {
|
.editor-emoji {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mention-list {
|
.mention-list {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
@ -1498,6 +1496,7 @@ const handleEditorClick = (event) => {
|
|||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote-card {
|
.quote-card {
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
border-left: 3px solid #1890ff;
|
border-left: 3px solid #1890ff;
|
||||||
@ -1506,18 +1505,22 @@ const handleEditorClick = (event) => {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote-content {
|
.quote-content {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote-title {
|
.quote-title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote-text {
|
.quote-text {
|
||||||
color: #666;
|
color: #666;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote-close {
|
.quote-close {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 4px;
|
top: 4px;
|
||||||
@ -1528,6 +1531,7 @@ const handleEditorClick = (event) => {
|
|||||||
color: #999;
|
color: #999;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-tip {
|
.edit-tip {
|
||||||
background: #fff7e6;
|
background: #fff7e6;
|
||||||
border: 1px solid #ffd591;
|
border: 1px solid #ffd591;
|
||||||
@ -1540,6 +1544,7 @@ const handleEditorClick = (event) => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-tip button {
|
.edit-tip button {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
@ -1548,11 +1553,13 @@ const handleEditorClick = (event) => {
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html[theme-mode='dark'] {
|
html[theme-mode='dark'] {
|
||||||
.editor {
|
.editor {
|
||||||
--tip-bg-color: #48484d;
|
--tip-bg-color: #48484d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.editor-image-wrapper.image-upload-loading::before) {
|
:deep(.editor-image-wrapper.image-upload-loading::before) {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -1568,19 +1575,23 @@ html[theme-mode='dark'] {
|
|||||||
animation: spin 0.6s linear infinite;
|
animation: spin 0.6s linear infinite;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.editor-image-wrapper.image-upload-loading img) {
|
:deep(.editor-image-wrapper.image-upload-loading img) {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
to {
|
to {
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hide-scrollbar {
|
.hide-scrollbar {
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 0;
|
width: 0;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
}
|
}
|
||||||
|
@ -227,14 +227,13 @@ class Talk extends Base {
|
|||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
console.log('输出加载1')
|
||||||
// 获取聊天面板元素节点
|
// 获取聊天面板元素节点
|
||||||
const el = document.getElementById('imChatPanel')
|
const el = document.getElementById('imChatPanel')
|
||||||
if (!el) return
|
if (!el) return
|
||||||
|
|
||||||
// 判断的滚动条是否在底部
|
// 判断的滚动条是否在底部
|
||||||
const isBottom = isScrollAtBottom(el)
|
const isBottom = isScrollAtBottom(el)
|
||||||
|
|
||||||
if (isBottom || record.user_id == this.getAccountId()) {
|
if (isBottom || record.user_id == this.getAccountId()) {
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
} else {
|
} else {
|
||||||
|
@ -167,7 +167,7 @@ export const useTalkRecord = (uid: number) => {
|
|||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const el = document.getElementById('imChatPanel')
|
const el = document.getElementById('imChatPanel')
|
||||||
|
console.log('request',request)
|
||||||
if (el) {
|
if (el) {
|
||||||
if (request.cursor == 0) {
|
if (request.cursor == 0) {
|
||||||
// el.scrollTop = el.scrollHeight
|
// el.scrollTop = el.scrollHeight
|
||||||
@ -175,6 +175,12 @@ export const useTalkRecord = (uid: number) => {
|
|||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// el.scrollTop = el.scrollHeight + 1000
|
// el.scrollTop = el.scrollHeight + 1000
|
||||||
// }, 500)
|
// }, 500)
|
||||||
|
console.log('滚动到底部')
|
||||||
|
|
||||||
|
// 在初次加载完成后恢复上传任务
|
||||||
|
// 确保在所有聊天记录加载完成后再恢复上传任务
|
||||||
|
dialogueStore.restoreUploadTasks()
|
||||||
|
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
} else {
|
} else {
|
||||||
el.scrollTop = el.scrollHeight - scrollHeight
|
el.scrollTop = el.scrollHeight - scrollHeight
|
||||||
@ -322,6 +328,8 @@ export const useTalkRecord = (uid: number) => {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// 其他情况滚动到底部
|
// 其他情况滚动到底部
|
||||||
|
// 在特殊参数模式下也需要恢复上传任务
|
||||||
|
dialogueStore.restoreUploadTasks()
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -332,6 +340,7 @@ export const useTalkRecord = (uid: number) => {
|
|||||||
|
|
||||||
loadConfig.specialParams = undefined // 普通模式清空
|
loadConfig.specialParams = undefined // 普通模式清空
|
||||||
// 原有逻辑
|
// 原有逻辑
|
||||||
|
console.log('onLoad()执行load')
|
||||||
load(params)
|
load(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,6 +378,7 @@ export const useTalkRecord = (uid: number) => {
|
|||||||
} else {
|
} else {
|
||||||
// 如果不匹配,重置为普通模式
|
// 如果不匹配,重置为普通模式
|
||||||
resetLoadConfig()
|
resetLoadConfig()
|
||||||
|
console.log('load执行2')
|
||||||
load({
|
load({
|
||||||
receiver_id: loadConfig.receiver_id,
|
receiver_id: loadConfig.receiver_id,
|
||||||
talk_type: loadConfig.talk_type,
|
talk_type: loadConfig.talk_type,
|
||||||
@ -377,6 +387,7 @@ export const useTalkRecord = (uid: number) => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 原有逻辑
|
// 原有逻辑
|
||||||
|
console.log('load执行3')
|
||||||
load({
|
load({
|
||||||
receiver_id: loadConfig.receiver_id,
|
receiver_id: loadConfig.receiver_id,
|
||||||
talk_type: loadConfig.talk_type,
|
talk_type: loadConfig.talk_type,
|
||||||
|
@ -15,7 +15,9 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
return {
|
return {
|
||||||
// 对话索引(聊天对话的唯一索引)
|
// 对话索引(聊天对话的唯一索引)
|
||||||
index_name: '',
|
index_name: '',
|
||||||
|
globalUploadList:[],
|
||||||
|
// 添加一个映射,用于快速查找每个会话的上传任务
|
||||||
|
uploadTaskMap: {}, // 格式: { "talk_type_receiver_id": [task1, task2, ...] }
|
||||||
// 对话节点
|
// 对话节点
|
||||||
talk: {
|
talk: {
|
||||||
avatar:'',
|
avatar:'',
|
||||||
@ -129,8 +131,10 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
if (data.talk_type == 2) {
|
if (data.talk_type == 2) {
|
||||||
this.updateGroupMembers()
|
this.updateGroupMembers()
|
||||||
this.getGroupInfo()
|
this.getGroupInfo()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 注意:上传任务的恢复将在聊天记录加载完成后进行
|
||||||
|
// 在useTalkRecord.ts的onLoad方法中,会在加载完聊天记录后调用restoreUploadTasks方法
|
||||||
},
|
},
|
||||||
|
|
||||||
// 更新提及列表
|
// 更新提及列表
|
||||||
@ -292,6 +296,16 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
|
|
||||||
// 更新视频上传进度
|
// 更新视频上传进度
|
||||||
updateUploadProgress(uploadId, percentage) {
|
updateUploadProgress(uploadId, percentage) {
|
||||||
|
// 更新全局列表中的进度
|
||||||
|
const globalTask = this.globalUploadList.find(item =>
|
||||||
|
item.extra && item.extra.is_uploading && item.extra.upload_id === uploadId
|
||||||
|
)
|
||||||
|
|
||||||
|
if (globalTask) {
|
||||||
|
globalTask.extra.percentage = percentage
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新当前会话记录中的进度
|
||||||
const record = this.records.find(item =>
|
const record = this.records.find(item =>
|
||||||
item.extra && item.extra.is_uploading && item.extra.upload_id === uploadId
|
item.extra && item.extra.is_uploading && item.extra.upload_id === uploadId
|
||||||
)
|
)
|
||||||
@ -301,6 +315,44 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 添加上传任务
|
||||||
|
addUploadTask(task) {
|
||||||
|
// 添加到全局列表
|
||||||
|
this.globalUploadList.push(task)
|
||||||
|
|
||||||
|
// 添加到会话映射
|
||||||
|
const sessionKey = `${task.talk_type}_${task.receiver_id}`
|
||||||
|
if (!this.uploadTaskMap[sessionKey]) {
|
||||||
|
this.uploadTaskMap[sessionKey] = []
|
||||||
|
}
|
||||||
|
this.uploadTaskMap[sessionKey].push(task)
|
||||||
|
|
||||||
|
// 同时添加到当前会话记录
|
||||||
|
this.addDialogueRecord(task)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 上传完成后移除任务
|
||||||
|
removeUploadTask(uploadId) {
|
||||||
|
// 从全局列表中找到任务
|
||||||
|
const taskIndex = this.globalUploadList.findIndex(item => item.msg_id === uploadId)
|
||||||
|
|
||||||
|
if (taskIndex >= 0) {
|
||||||
|
const task = this.globalUploadList[taskIndex]
|
||||||
|
const sessionKey = `${task.talk_type}_${task.receiver_id}`
|
||||||
|
|
||||||
|
// 从会话映射中移除
|
||||||
|
if (this.uploadTaskMap[sessionKey]) {
|
||||||
|
const mapIndex = this.uploadTaskMap[sessionKey].findIndex(item => item.msg_id === uploadId)
|
||||||
|
if (mapIndex >= 0) {
|
||||||
|
this.uploadTaskMap[sessionKey].splice(mapIndex, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从全局列表中移除
|
||||||
|
this.globalUploadList.splice(taskIndex, 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 视频上传完成后更新消息
|
// 视频上传完成后更新消息
|
||||||
completeUpload(uploadId, videoInfo) {
|
completeUpload(uploadId, videoInfo) {
|
||||||
const record = this.records.find(item =>
|
const record = this.records.find(item =>
|
||||||
@ -317,6 +369,23 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
// 更新会话信息
|
// 更新会话信息
|
||||||
updateDialogueTalk(params){
|
updateDialogueTalk(params){
|
||||||
Object.assign(this.talk, params)
|
Object.assign(this.talk, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 恢复当前会话的上传任务
|
||||||
|
restoreUploadTasks() {
|
||||||
|
// 获取当前会话的sessionKey
|
||||||
|
const sessionKey = `${this.talk.talk_type}_${this.talk.receiver_id}`
|
||||||
|
|
||||||
|
// 检查是否有需要恢复的上传任务
|
||||||
|
if (this.uploadTaskMap[sessionKey] && this.uploadTaskMap[sessionKey].length > 0) {
|
||||||
|
// 按照插入顺序排序上传任务
|
||||||
|
const tasks = [...this.uploadTaskMap[sessionKey]].sort((a, b) => a.insert_sequence - b.insert_sequence)
|
||||||
|
|
||||||
|
// 将上传任务添加到当前会话记录中
|
||||||
|
tasks.forEach(task => {
|
||||||
|
this.addDialogueRecord(task)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ServeFindFileSplitInfo, ServeFileSubareaUpload } from '@/api/upload'
|
// import { message } from 'naive-ui'
|
||||||
import { ServeSendTalkFile } from '@/api/chat'
|
import {
|
||||||
import { uploadImg } from '@/api/upload'
|
ServeSendTalkFile
|
||||||
|
} from '@/api/chat'
|
||||||
|
import {
|
||||||
|
uploadImg,
|
||||||
|
ServeFindFileSplitInfo,
|
||||||
|
ServeFileSubareaUpload
|
||||||
|
} from '@/api/upload'
|
||||||
import {
|
import {
|
||||||
useDialogueStore
|
useDialogueStore
|
||||||
} from '@/store'
|
} from '@/store'
|
||||||
@ -140,12 +146,12 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
this.triggerUpload(upload_id, clientUploadId)
|
this.triggerUpload(upload_id, clientUploadId)
|
||||||
} else {
|
} else {
|
||||||
message.error(res.message)
|
message.error(res.message)
|
||||||
onProgress(-1) // 通知上传失败
|
this.handleUploadError(upload_id, clientUploadId)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("初始化分片上传失败:", error);
|
console.error("初始化分片上传失败:", error);
|
||||||
message.error("初始化上传失败,请重试")
|
message.error("初始化上传失败,请重试")
|
||||||
onProgress(-1)
|
this.handleUploadError(upload_id, clientUploadId)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -201,26 +207,20 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
this.triggerUpload(uploadId, clientUploadId)
|
this.triggerUpload(uploadId, clientUploadId)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updatedItem.onProgress(-1)
|
|
||||||
// 上传失败处理
|
// 上传失败处理
|
||||||
console.error(`分片上传失败,错误码: ${res.code},错误信息: ${res.message || '未知错误'}`);
|
console.error(`分片上传失败,错误码: ${res.code},错误信息: ${res.message || '未知错误'}`);
|
||||||
updatedItem.status = 3
|
this.handleUploadError(uploadId, clientUploadId || '')
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
updatedItem.onProgress(-1)
|
|
||||||
console.error("分片上传错误:", error);
|
console.error("分片上传错误:", error);
|
||||||
|
|
||||||
// 获取最新的项目状态
|
// 获取最新的项目状态
|
||||||
// 这里不应该重新定义变量,而是使用已有的updatedItem
|
|
||||||
// const updatedItem = this.findItem(uploadId)
|
|
||||||
if (!updatedItem) return
|
if (!updatedItem) return
|
||||||
|
|
||||||
// 如果是暂停导致的错误,不改变状态
|
// 如果是暂停导致的错误,不改变状态
|
||||||
if (updatedItem.is_paused) return
|
if (updatedItem.is_paused) return
|
||||||
|
|
||||||
updatedItem.status = 3
|
this.handleUploadError(uploadId, clientUploadId || '')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -244,6 +244,10 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
talk_type: item.talk_type
|
talk_type: item.talk_type
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 从DialogueStore中移除上传任务
|
||||||
|
const dialogueStore = useDialogueStore()
|
||||||
|
dialogueStore.removeUploadTask(clientUploadId)
|
||||||
|
|
||||||
if (item.onComplete) {
|
if (item.onComplete) {
|
||||||
item.onComplete(item)
|
item.onComplete(item)
|
||||||
}
|
}
|
||||||
@ -291,5 +295,21 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
// 从上传列表中移除旧的上传项
|
// 从上传列表中移除旧的上传项
|
||||||
this.items = this.items.filter(i => i.client_upload_id !== clientUploadId)
|
this.items = this.items.filter(i => i.client_upload_id !== clientUploadId)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 上传失败处理
|
||||||
|
async handleUploadError(uploadId: string, clientUploadId: string) {
|
||||||
|
const item = this.findItem(uploadId)
|
||||||
|
if (!item) return
|
||||||
|
|
||||||
|
item.status = 3 // 设置为上传失败状态
|
||||||
|
|
||||||
|
// 从DialogueStore中移除上传任务
|
||||||
|
const dialogueStore = useDialogueStore()
|
||||||
|
dialogueStore.removeUploadTask(clientUploadId)
|
||||||
|
|
||||||
|
if (item.onProgress) {
|
||||||
|
item.onProgress(-1) // 通知上传失败
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -54,7 +54,6 @@ request.interceptors.request.use((config) => {
|
|||||||
|
|
||||||
// 响应拦截器
|
// 响应拦截器
|
||||||
request.interceptors.response.use((response) => {
|
request.interceptors.response.use((response) => {
|
||||||
console.log('response.data.status',response.data.status)
|
|
||||||
if(response.data.code !==200&&response.data.status!==0){
|
if(response.data.code !==200&&response.data.status!==0){
|
||||||
window['$message'].warning(response.data.msg)
|
window['$message'].warning(response.data.msg)
|
||||||
}
|
}
|
||||||
|
@ -592,8 +592,6 @@ const indexName = computed(() => dialogueStore.index_name)
|
|||||||
|
|
||||||
// 切换会话
|
// 切换会话
|
||||||
const onTabTalk = (item: ISession, follow = false) => {
|
const onTabTalk = (item: ISession, follow = false) => {
|
||||||
console.log('onTabTalk')
|
|
||||||
|
|
||||||
if (item.index_name === indexName.value) return
|
if (item.index_name === indexName.value) return
|
||||||
|
|
||||||
searchKeyword.value = ''
|
searchKeyword.value = ''
|
||||||
@ -638,7 +636,7 @@ const onReload = () => {
|
|||||||
// 初始化加载
|
// 初始化加载
|
||||||
const onInitialize = () => {
|
const onInitialize = () => {
|
||||||
let index_name = getCacheIndexName()
|
let index_name = getCacheIndexName()
|
||||||
|
console.log('index_name',index_name)
|
||||||
index_name && onTabTalk(talkStore.findItem(index_name), true)
|
index_name && onTabTalk(talkStore.findItem(index_name), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +393,7 @@ watch(
|
|||||||
}, 3000)
|
}, 3000)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
console.log('执行逻辑')
|
||||||
onLoad(
|
onLoad(
|
||||||
{
|
{
|
||||||
receiver_id: newProps.receiver_id,
|
receiver_id: newProps.receiver_id,
|
||||||
@ -403,7 +403,7 @@ watch(
|
|||||||
specialParams ? { specifiedMsg: specialParams } : undefined
|
specialParams ? { specifiedMsg: specialParams } : undefined
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: true }
|
{ deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
// onMounted(() => {
|
// onMounted(() => {
|
||||||
@ -589,12 +589,25 @@ const handleIntersection = (entries) => {
|
|||||||
watch(
|
watch(
|
||||||
() => records.value,
|
() => records.value,
|
||||||
() => {
|
() => {
|
||||||
|
console.log()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 断开旧的观察者
|
// 断开旧的观察者
|
||||||
if (observer) {
|
if (observer) {
|
||||||
observer.disconnect()
|
observer.disconnect()
|
||||||
}
|
}
|
||||||
|
// 创建原数组的副本进行遍历
|
||||||
|
// const recordsCopy = [...dialogueStore.records];
|
||||||
|
// for (const [y, iy] of dialogueStore.globalUploadList.entries()) {
|
||||||
|
// console.log('y',y)
|
||||||
|
// console.log('iy',iy)
|
||||||
|
// for (const [x, ix] of recordsCopy.entries()) {
|
||||||
|
// if(x.msg_id === y.pre_msg){
|
||||||
|
// // 注意:这里的ix是原数组的索引,需要考虑已插入元素的偏移
|
||||||
|
// dialogueStore.records.splice(ix + 1 + (dialogueStore.records.length - recordsCopy.length), 0, y);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// console.log('dialogueStore.records',dialogueStore.records)
|
||||||
// 重新初始化观察者
|
// 重新初始化观察者
|
||||||
const options = {
|
const options = {
|
||||||
root: null,
|
root: null,
|
||||||
|
@ -115,6 +115,9 @@ const onSendVideoEvent = async ({ data }) => {
|
|||||||
// 创建临时消息记录
|
// 创建临时消息记录
|
||||||
const tempMessage = {
|
const tempMessage = {
|
||||||
msg_id: uploadId,
|
msg_id: uploadId,
|
||||||
|
insert_sequence: dialogueStore.records.length > 0
|
||||||
|
? dialogueStore.records[dialogueStore.records.length-1].sequence
|
||||||
|
: 0,
|
||||||
sequence: Date.now(),
|
sequence: Date.now(),
|
||||||
talk_type: props.talk_type,
|
talk_type: props.talk_type,
|
||||||
msg_type: 5, // 视频消息类型
|
msg_type: 5, // 视频消息类型
|
||||||
@ -137,8 +140,8 @@ const onSendVideoEvent = async ({ data }) => {
|
|||||||
float: 'right' // 我发送的消息显示在右侧
|
float: 'right' // 我发送的消息显示在右侧
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接添加到对话记录中
|
// 使用新的方法添加上传任务
|
||||||
dialogueStore.addDialogueRecord(tempMessage)
|
dialogueStore.addUploadTask(tempMessage)
|
||||||
nextTick(()=>{
|
nextTick(()=>{
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
})
|
})
|
||||||
@ -151,8 +154,8 @@ const onSendVideoEvent = async ({ data }) => {
|
|||||||
dialogueStore.updateUploadProgress(uploadId, percentage)
|
dialogueStore.updateUploadProgress(uploadId, percentage)
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
dialogueStore.batchDelDialogueRecord([uploadId])
|
// 上传完成后,上传任务已经被removeUploadTask方法移除
|
||||||
|
// 不需要再次从globalUploadList中移除
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -171,6 +174,9 @@ const onSendFileEvent = ({ data }) => {
|
|||||||
const clientUploadId = `file-${Date.now()}-${Math.floor(Math.random() * 1000)}`
|
const clientUploadId = `file-${Date.now()}-${Math.floor(Math.random() * 1000)}`
|
||||||
const tempMessage = {
|
const tempMessage = {
|
||||||
msg_id: clientUploadId,
|
msg_id: clientUploadId,
|
||||||
|
insert_sequence: dialogueStore.records.length > 0
|
||||||
|
? dialogueStore.records[dialogueStore.records.length-1].sequence
|
||||||
|
: 0,
|
||||||
sequence: Date.now(),
|
sequence: Date.now(),
|
||||||
talk_type: props.talk_type,
|
talk_type: props.talk_type,
|
||||||
msg_type: 6,
|
msg_type: 6,
|
||||||
@ -192,7 +198,7 @@ const onSendFileEvent = ({ data }) => {
|
|||||||
},
|
},
|
||||||
float: 'right'
|
float: 'right'
|
||||||
}
|
}
|
||||||
dialogueStore.addDialogueRecord(tempMessage)
|
dialogueStore.addUploadTask(tempMessage)
|
||||||
nextTick(()=>{
|
nextTick(()=>{
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
})
|
})
|
||||||
@ -201,8 +207,8 @@ const onSendFileEvent = ({ data }) => {
|
|||||||
dialogueStore.updateUploadProgress(clientUploadId, percentage)
|
dialogueStore.updateUploadProgress(clientUploadId, percentage)
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
dialogueStore.batchDelDialogueRecord([clientUploadId])
|
// 上传完成后,上传任务已经被removeUploadTask方法移除
|
||||||
|
// 不需要再次从records中删除
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -46,9 +46,9 @@ export default defineConfig(({ mode }) => {
|
|||||||
vueJsx({}),
|
vueJsx({}),
|
||||||
compressPlugin(),
|
compressPlugin(),
|
||||||
UnoCSS(),
|
UnoCSS(),
|
||||||
// vueDevTools({
|
vueDevTools({
|
||||||
// launchEditor: 'trae',
|
launchEditor: 'trae',
|
||||||
// })
|
})
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
__APP_ENV__: env.APP_ENV
|
__APP_ENV__: env.APP_ENV
|
||||||
|
Loading…
Reference in New Issue
Block a user