fix: 修复消息转发、上传和编辑器引用删除功能

- 添加ChatMsgTypeForward到可转发消息类型
- 修复请求拦截器中状态码判断逻辑
- 优化视频消息上传封面获取和预览显示
- 修复上传分片错误处理和进度更新
- 重构编辑器引用删除逻辑,提升代码可维护性
- 调整图片消息样式和上传蒙版显示
This commit is contained in:
Phoenix 2025-06-11 14:47:13 +08:00
parent 57e4ba69d9
commit 45e4415cec
8 changed files with 77 additions and 67 deletions

View File

@ -1,5 +1,5 @@
<script setup>
import { ref, reactive, computed, onMounted, onBeforeUnmount, nextTick, markRaw, watch } from 'vue'
import { ref, computed, onMounted, onBeforeUnmount, nextTick, markRaw, watch } from 'vue'
import { NPopover, NIcon } from 'naive-ui'
import {
SmilingFace,
@ -1063,6 +1063,54 @@ const onSubscribeMention = async (data) => {
}
};
// handleDeleteQuote
const handleDeleteQuote = function(e) {
// 退
if (e.key !== 'Backspace' && e.key !== 'Delete') return;
const selection = window.getSelection();
if (selection.rangeCount === 0) return;
const range = selection.getRangeAt(0);
const editor = editorRef.value;
if (!editor) return;
const quoteElement = editor.querySelector('.editor-quote');
if (!quoteElement) {
//
editor.removeEventListener('keydown', handleDeleteQuote);
return;
}
//
const quoteIndex = Array.from(editor.childNodes).indexOf(quoteElement);
// 退
const isBeforeQuote = e.key === 'Backspace' &&
range.collapsed &&
range.startContainer === editor &&
quoteIndex === range.startOffset;
//
const isAfterQuote = e.key === 'Delete' &&
range.collapsed &&
range.startContainer === editor &&
quoteIndex === range.startOffset - 1;
if (isBeforeQuote || isAfterQuote) {
//
e.preventDefault();
//
quoteElement.remove();
quoteData.value = null;
//
handleInput({ target: editor });
}
};
const onSubscribeQuote = (data) => {
quoteData.value = data
@ -1135,58 +1183,8 @@ const onSubscribeQuote = (data) => {
}
})
const handleDeleteQuote = function(e) {
if (e.key !== 'Backspace' && e.key !== 'Delete') return;
const selection = window.getSelection();
if (selection.rangeCount === 0) return;
const range = selection.getRangeAt(0);
const quoteElement = editor.querySelector('.editor-quote');
if (!quoteElement) {
editor.removeEventListener('keydown', handleDeleteQuote);
return;
}
const quoteIndex = Array.from(editor.childNodes).indexOf(quoteElement);
const isBeforeQuote = e.key === 'Backspace' &&
range.collapsed &&
range.startContainer === editor &&
quoteIndex === range.startOffset;
const isAfterQuote = e.key === 'Delete' &&
range.collapsed &&
range.startContainer === editor &&
quoteIndex === range.startOffset - 1;
if (isBeforeQuote || isAfterQuote) {
e.preventDefault();
quoteElement.remove();
quoteData.value = null;
handleInput({ target: editor });
}
};
editor.addEventListener('keydown', handleDeleteQuote);
// 使handleDeleteQuote
editor.addEventListener('keydown', handleDeleteQuote);
setTimeout(() => {
@ -1955,7 +1953,6 @@ const handleEditorClick = (event) => {
/**
* 表情样式

View File

@ -38,7 +38,7 @@ const img = (src: string, width = 200) => {
<div class="image-container">
<n-image class="h-149px" :src="extra.url" />
<!-- 上传中的loading蒙版 -->
<div v-if="props.extra.is_uploading" class="loading-overlay">
<div v-if="extra.is_uploading" class="loading-overlay">
<n-spin size="large" />
</div>
</div>
@ -53,7 +53,7 @@ const img = (src: string, width = 200) => {
height:149px;
&.left {
background: var(--im-message-right-bg-color);
background: #F4F4FC;
}
.image-container {

View File

@ -1,6 +1,6 @@
<script lang="ts" setup>
import 'xgplayer/dist/index.min.css'
import { ref, nextTick, watch } from 'vue'
import { ref, nextTick, watch, computed } from 'vue'
import { NImage, NModal, NCard, NProgress, NPopconfirm } from 'naive-ui'
import { Play, Close, Pause, Right, Attention } from '@icon-park/vue-next'
import { getImageInfo } from '@/utils/functions'
@ -64,6 +64,11 @@ const updatePauseStatus = () => {
//
updatePauseStatus()
// URL
const videoSrc = computed(() => {
// 使URL
return props.extra.url || ''
})
// //
// watch(() => props.extra.percentage, (newVal: number | undefined) => {
// // UI
@ -136,7 +141,7 @@ function resumeUpload(e) {
>
<!-- <n-image :src="extra.cover" preview-disabled /> -->
<video :src="props.extra.url" :controls="false"></video>
<video :src="videoSrc" :controls="false"></video>
<!-- 上传进度时的黑色半透明蒙层 -->
<div v-if="extra.is_uploading && !uploadFailed" class="upload-mask"></div>
<!-- 上传进度显示 -->
@ -252,7 +257,7 @@ function resumeUpload(e) {
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.45);
background: rgba(0, 0, 0, 0.3); /* 降低不透明度从0.45改为0.3,让视频封面能够显示 */
z-index: 1;
border-radius: 5px;
}

View File

@ -96,6 +96,7 @@ export const MessageComponents = {
// 可转发的消息类型
export const ForwardableMessageType = [
ChatMsgTypeForward,
ChatMsgTypeText,
ChatMsgTypeCode,
ChatMsgTypeImage,

View File

@ -170,14 +170,14 @@ export const useUploadsStore = defineStore('uploads', {
// 更新状态为上传中
currentItem.status = 1
const updatedItem:any = this.findItem(uploadId)
// 上传当前分片
try {
const res = await ServeFileSubareaUpload(form)
// 获取最新的项目状态,确保仍然存在且没有被暂停
const updatedItem:any = this.findItem(uploadId)
if (res.code == 200) {
// 当前分片上传成功,增加索引
updatedItem.uploadIndex++
@ -209,10 +209,12 @@ export const useUploadsStore = defineStore('uploads', {
}
} catch (error) {
updatedItem.onProgress(-1)
console.error("分片上传错误:", error);
// 获取最新的项目状态
const updatedItem = this.findItem(uploadId)
// 这里不应该重新定义变量而是使用已有的updatedItem
// const updatedItem = this.findItem(uploadId)
if (!updatedItem) return
// 如果是暂停导致的错误,不改变状态

View File

@ -54,7 +54,8 @@ request.interceptors.request.use((config) => {
// 响应拦截器
request.interceptors.response.use((response) => {
if(response.data.code !==200){
console.log('response.data.status',response.data.status)
if(response.data.code !==200&&response.data.status!==0){
window['$message'].warning(response.data.msg)
}
return response.data

View File

@ -330,6 +330,7 @@ const onContextMenuHandle = (key: string) => {
const onRowClick = (item: ITalkRecord) => {
if (dialogueStore.isOpenMultiSelect) {
console.log('item.msg_type',item.msg_type)
if (ForwardableMessageType.includes(item.msg_type)) {
item.isCheck = !item.isCheck
} else {

View File

@ -101,10 +101,13 @@ const onSendImageEvent = ({ data, callBack }) => {
//
const onSendVideoEvent = async ({ data }) => {
//
// let resp = await getVideoImage(data)
let videoPreview = null
try {
videoPreview = await getVideoImage(data)
} catch (error) {
console.error('获取视频封面失败:', error)
}
// ID
const uploadId = `video-${Date.now()}-${Math.floor(Math.random() * 1000)}`
@ -123,7 +126,7 @@ const onSendVideoEvent = async ({ data }) => {
content: '',
created_at: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}'),
extra: {
url: '',
url: videoPreview ? URL.createObjectURL(data) : '', // 使URL
size: data.size,
is_uploading: true,
upload_id: uploadId,