From 7113d7b5507b2cb95f16f3b51fb2e1af51740f89 Mon Sep 17 00:00:00 2001 From: La123123 <617330105@qq.com> Date: Mon, 20 Apr 2026 11:05:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=8D=E5=88=B6=E7=B2=98=E8=B4=B4=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=9B=BE=E7=89=87=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- im-uniapp/pages/chat/chat-box.vue | 326 ++++++++++++++++++------------ 1 file changed, 201 insertions(+), 125 deletions(-) diff --git a/im-uniapp/pages/chat/chat-box.vue b/im-uniapp/pages/chat/chat-box.vue index 84aa86e..405bf7e 100644 --- a/im-uniapp/pages/chat/chat-box.vue +++ b/im-uniapp/pages/chat/chat-box.vue @@ -270,6 +270,8 @@ export default { scrollViewHeight: 0, maxTmpId: 0, isFileOpen: false, + isPasting: false, // 防止重复触发粘贴事件 + hasPasteListener: false, // 防止重复添加事件监听器 }; }, methods: { @@ -1081,11 +1083,24 @@ export default { .context((res) => { this.editorCtx = res.context; // #ifdef H5 - // 只使用document监听粘贴事件,避免重复触发 - setTimeout(() => { - document.addEventListener('paste', this.onDocumentPaste); - console.log('已添加粘贴事件监听到document'); - }, 100); + // 使用全局变量确保只添加一次监听器 + if (!window.__pasteHandlerAdded__) { + window.__pasteHandlerAdded__ = true; + // 初始化全局变量 + if (window.__isPasting__ === undefined) { + window.__isPasting__ = false; + } + console.log('[chat-box] 首次添加粘贴事件监听器'); + + // 移除所有可能的旧监听器 + if (this.pasteHandler) { + document.removeEventListener('paste', this.pasteHandler); + } + + // 使用bind绑定this,确保this指向正确 + this.pasteHandler = this.onDocumentPaste.bind(this); + document.addEventListener('paste', this.pasteHandler); + } // #endif }) .exec(); @@ -1099,8 +1114,11 @@ export default { onEditorBlur(e) { this.isFocus = false; }, - onDocumentPaste(e) { + async onDocumentPaste(e) { // #ifdef H5 + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] 粘贴事件触发, isPasting: ${window.__isPasting__}`); + // 检查是否在editor中粘贴 const activeElement = document.activeElement; const isInEditor = activeElement && ( @@ -1109,25 +1127,37 @@ export default { ); if (!isInEditor) { + console.log(`[${timestamp}] 不在editor中,忽略`); + return; + } + + // 使用全局变量防止重复触发 + if (window.__isPasting__) { + console.log(`[${timestamp}] 全局isPasting为true,忽略重复触发`); + e.preventDefault(); + e.stopPropagation(); return; } - console.log('文档粘贴事件触发', e); const items = (e.clipboardData || e.originalEvent.clipboardData).items; - console.log('剪贴板项', items); + let hasImage = false; + for (let i = 0; i < items.length; i++) { const item = items[i]; - console.log('剪贴板项', i, item); if (item.kind === 'file' && item.type.startsWith('image/')) { + console.log(`[${timestamp}] 检测到图片,开始处理`); e.preventDefault(); e.stopPropagation(); - const file = item.getAsFile(); - console.log('获取到的图片文件', file); - // 将文件转换为临时路径 - const reader = new FileReader(); - reader.onload = (event) => { - const base64 = event.target.result; - console.log('转换后的base64', base64); + hasImage = true; + window.__isPasting__ = true; + + try { + const file = item.getAsFile(); + + // 将文件转换为base64 + const base64 = await this.fileToBase64(file); + console.log(`[${timestamp}] base64转换完成,长度: ${base64.length}`); + // 创建临时文件对象 const tempFile = { path: base64, @@ -1135,136 +1165,180 @@ export default { size: file.size, name: file.name || 'pasted-image.png' }; + // 调用现有的图片上传逻辑 - this.handlePasteImage(tempFile); - }; - reader.readAsDataURL(file); + await this.handlePasteImage(tempFile); + } catch (error) { + console.log(`[${timestamp}] 处理粘贴图片失败:`, error); + } finally { + window.__isPasting__ = false; + } + break; } } + + // 如果没有图片,立即重置标志 + if (!hasImage) { + console.log(`[${timestamp}] 没有检测到图片`); + this.isPasting = false; + } // #endif }, - handlePasteImage(file) { - console.log('handlePasteImage被调用', file); - // 检查是否被封禁 - if (this.isBanned) { - this.showBannedTip(); + + async fileToBase64(file) { + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] fileToBase64被调用,文件大小:`, file.size); + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = (event) => { + console.log(`[${timestamp}] fileToBase64 onload,结果长度:`, event.target.result.length); + resolve(event.target.result); + }; + reader.onerror = (error) => { + console.log(`[${timestamp}] fileToBase64 onerror:`, error); + reject(error); + }; + reader.readAsDataURL(file); + }); + }, + + async handlePasteImage(file) { + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] handlePasteImage被调用, isPasting: ${window.__isPasting__}, chat:`, this.chat); + + // 检查chat是否存在 + if (!this.chat) { + console.log(`[${timestamp}] handlePasteImage: chat为空,忽略`); return; } - let data = { - originUrl: file.path, - thumbUrl: file.path, - }; - let msgInfo = { - tmpId: this.generateId(), - fileId: file.uid, - sendId: this.mine.id, - content: JSON.stringify(data), - sendTime: new Date().getTime(), - selfSend: true, - type: this.$enums.MESSAGE_TYPE.IMAGE, - readedCount: 0, - status: this.$enums.MESSAGE_STATUS.SENDING, - }; - console.log('创建的消息信息', msgInfo); - // 填充对方id - this.fillTargetId(msgInfo, this.chat.targetId); - // 插入消息 - this.chatStore.insertMessage(msgInfo, this.chat); - // 会话置顶 - this.moveChatToTop(); - // 借助file对象保存 - file.msgInfo = msgInfo; - file.chat = this.chat; - // 更新图片宽高 - let chat = this.chat; - this.getImageSize(file).then((size) => { - console.log('获取到图片尺寸', size); + + try { + // 检查是否被封禁 + if (this.isBanned) { + this.showBannedTip(); + return; + } + + let data = { + originUrl: file.path, + thumbUrl: file.path, + }; + + let msgInfo = { + tmpId: this.generateId(), + fileId: file.uid, + sendId: this.mine.id, + content: JSON.stringify(data), + sendTime: new Date().getTime(), + selfSend: true, + type: this.$enums.MESSAGE_TYPE.IMAGE, + readedCount: 0, + status: this.$enums.MESSAGE_STATUS.SENDING, + }; + + // 填充对方id + this.fillTargetId(msgInfo, this.chat.targetId); + + // 插入消息 + this.chatStore.insertMessage(msgInfo, this.chat); + + // 会话置顶 + this.moveChatToTop(); + + // 借助file对象保存 + file.msgInfo = msgInfo; + file.chat = this.chat; + + // 更新图片宽高 + const size = await this.getImageSize(file); msgInfo = JSON.parse(JSON.stringify(msgInfo)); data.width = size.width; data.height = size.height; msgInfo.content = JSON.stringify(data); - this.chatStore.updateMessage(msgInfo, chat); + this.chatStore.updateMessage(msgInfo, this.chat); this.scrollToBottom(); - }); - // 上传图片到服务器 - this.uploadPastedImage(file); + + // 上传图片到服务器 + await this.uploadPastedImage(file); + + } catch (error) { + console.log(`[${timestamp}] 处理粘贴图片失败:`, error); + this.isPasting = false; + } }, - uploadPastedImage(file) { - console.log('uploadPastedImage被调用', file); + + async uploadPastedImage(file) { + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] uploadPastedImage被调用`); + // #ifdef H5 - // 将base64转换为Blob - const base64Data = file.path.split(',')[1]; - console.log('base64数据长度', base64Data.length); - const byteCharacters = atob(base64Data); - const byteArrays = []; - for (let offset = 0; offset < byteCharacters.length; offset += 512) { - const slice = byteCharacters.slice(offset, offset + 512); - const byteNumbers = new Array(slice.length); - for (let i = 0; i < slice.length; i++) { - byteNumbers[i] = slice.charCodeAt(i); - } - const byteArray = new Uint8Array(byteNumbers); - byteArrays.push(byteArray); - } - const blob = new Blob(byteArrays, { type: 'image/png' }); - console.log('创建的Blob对象', blob); - - // 创建FormData - const formData = new FormData(); - formData.append('file', blob, file.name); - console.log('创建的FormData', formData); - - // 上传图片 - console.log('开始上传图片'); - uni.uploadFile({ - url: UNI_APP.BASE_URL + `/image/upload?isPermanent=false&thumbSize=50`, - filePath: file.path, - name: 'file', - success: (res) => { - console.log('上传响应', res); - const data = JSON.parse(res.data); - if (data.code === 200) { - console.log('上传成功,响应数据', data.data); - // 上传成功,更新消息 - let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); - msgInfo.content = JSON.stringify(data.data); - msgInfo.receipt = this.isReceipt; - this.sendMessageRequest(msgInfo) - .then((m) => { - console.log('消息发送成功', m); - msgInfo.id = m.id; - msgInfo.status = m.status; - this.isReceipt = false; - this.chatStore.updateMessage(msgInfo, file.chat); - }) - .catch(() => { - console.log('消息发送失败'); - msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; - this.chatStore.updateMessage(msgInfo, file.chat); - }); - } else { - console.log('上传失败,响应码', data.code); - uni.showToast({ - icon: "none", - title: data.message || "上传失败", - }); - let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); - msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; - this.chatStore.updateMessage(msgInfo, file.chat); + try { + // 将base64转换为Blob + const base64Data = file.path.split(',')[1]; + const byteCharacters = atob(base64Data); + const byteArrays = []; + for (let offset = 0; offset < byteCharacters.length; offset += 512) { + const slice = byteCharacters.slice(offset, offset + 512); + const byteNumbers = new Array(slice.length); + for (let i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); } - }, - fail: (err) => { - console.error("上传图片失败", err); + const byteArray = new Uint8Array(byteNumbers); + byteArrays.push(byteArray); + } + const blob = new Blob(byteArrays, { type: 'image/png' }); + + // 创建FormData + const formData = new FormData(); + formData.append('file', blob, file.name); + + console.log(`[${timestamp}] 开始上传图片`); + + // 使用fetch上传 + const response = await fetch(UNI_APP.BASE_URL + `/image/upload?isPermanent=false&thumbSize=50`, { + method: 'POST', + body: formData + }); + + const data = await response.json(); + console.log(`[${timestamp}] 上传响应:`, data); + + if (data.code === 200) { + console.log(`[${timestamp}] 上传成功,准备发送消息`); + // 上传成功,更新消息内容 + let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); + msgInfo.content = JSON.stringify(data.data); + msgInfo.receipt = this.isReceipt; + this.chatStore.updateMessage(msgInfo, file.chat); + + // 发送消息 + const m = await this.sendMessageRequest(msgInfo); + console.log(`[${timestamp}] 消息发送成功`); + msgInfo.id = m.id; + msgInfo.status = m.status; + this.isReceipt = false; + this.chatStore.updateMessage(msgInfo, file.chat); + } else { + console.log(`[${timestamp}] 上传失败:`, data); uni.showToast({ icon: "none", - title: "上传失败", + title: data.message || "上传失败", }); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; this.chatStore.updateMessage(msgInfo, file.chat); } - }); + } catch (error) { + console.log(`[${timestamp}] 上传异常:`, error); + uni.showToast({ + icon: "none", + title: "上传失败", + }); + let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); + msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; + this.chatStore.updateMessage(msgInfo, file.chat); + } // #endif }, onAudioStateChange(state, msgInfo) { @@ -1772,8 +1846,10 @@ export default { this.unListenKeyboard(); // #ifdef H5 // 移除粘贴事件监听 - if (this.onDocumentPaste) { - document.removeEventListener('paste', this.onDocumentPaste); + if (this.hasPasteListener && this.pasteHandler) { + document.removeEventListener('paste', this.pasteHandler); + this.hasPasteListener = false; + this.pasteHandler = null; } // #endif },