fix: 修复消息转发、上传和编辑器引用删除功能
- 添加ChatMsgTypeForward到可转发消息类型 - 修复请求拦截器中状态码判断逻辑 - 优化视频消息上传封面获取和预览显示 - 修复上传分片错误处理和进度更新 - 重构编辑器引用删除逻辑,提升代码可维护性 - 调整图片消息样式和上传蒙版显示
This commit is contained in:
parent
57e4ba69d9
commit
45e4415cec
@ -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) => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 表情样式
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -96,6 +96,7 @@ export const MessageComponents = {
|
||||
|
||||
// 可转发的消息类型
|
||||
export const ForwardableMessageType = [
|
||||
ChatMsgTypeForward,
|
||||
ChatMsgTypeText,
|
||||
ChatMsgTypeCode,
|
||||
ChatMsgTypeImage,
|
||||
|
@ -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
|
||||
|
||||
// 如果是暂停导致的错误,不改变状态
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user