|
|
@ -270,6 +270,8 @@ export default { |
|
|
scrollViewHeight: 0, |
|
|
scrollViewHeight: 0, |
|
|
maxTmpId: 0, |
|
|
maxTmpId: 0, |
|
|
isFileOpen: false, |
|
|
isFileOpen: false, |
|
|
|
|
|
isPasting: false, // 防止重复触发粘贴事件 |
|
|
|
|
|
hasPasteListener: false, // 防止重复添加事件监听器 |
|
|
}; |
|
|
}; |
|
|
}, |
|
|
}, |
|
|
methods: { |
|
|
methods: { |
|
|
@ -1081,11 +1083,24 @@ export default { |
|
|
.context((res) => { |
|
|
.context((res) => { |
|
|
this.editorCtx = res.context; |
|
|
this.editorCtx = res.context; |
|
|
// #ifdef H5 |
|
|
// #ifdef H5 |
|
|
// 只使用document监听粘贴事件,避免重复触发 |
|
|
// 使用全局变量确保只添加一次监听器 |
|
|
setTimeout(() => { |
|
|
if (!window.__pasteHandlerAdded__) { |
|
|
document.addEventListener('paste', this.onDocumentPaste); |
|
|
window.__pasteHandlerAdded__ = true; |
|
|
console.log('已添加粘贴事件监听到document'); |
|
|
// 初始化全局变量 |
|
|
}, 100); |
|
|
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 |
|
|
// #endif |
|
|
}) |
|
|
}) |
|
|
.exec(); |
|
|
.exec(); |
|
|
@ -1099,8 +1114,11 @@ export default { |
|
|
onEditorBlur(e) { |
|
|
onEditorBlur(e) { |
|
|
this.isFocus = false; |
|
|
this.isFocus = false; |
|
|
}, |
|
|
}, |
|
|
onDocumentPaste(e) { |
|
|
async onDocumentPaste(e) { |
|
|
// #ifdef H5 |
|
|
// #ifdef H5 |
|
|
|
|
|
const timestamp = new Date().toISOString(); |
|
|
|
|
|
console.log(`[${timestamp}] 粘贴事件触发, isPasting: ${window.__isPasting__}`); |
|
|
|
|
|
|
|
|
// 检查是否在editor中粘贴 |
|
|
// 检查是否在editor中粘贴 |
|
|
const activeElement = document.activeElement; |
|
|
const activeElement = document.activeElement; |
|
|
const isInEditor = activeElement && ( |
|
|
const isInEditor = activeElement && ( |
|
|
@ -1109,25 +1127,37 @@ export default { |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
if (!isInEditor) { |
|
|
if (!isInEditor) { |
|
|
|
|
|
console.log(`[${timestamp}] 不在editor中,忽略`); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 使用全局变量防止重复触发 |
|
|
|
|
|
if (window.__isPasting__) { |
|
|
|
|
|
console.log(`[${timestamp}] 全局isPasting为true,忽略重复触发`); |
|
|
|
|
|
e.preventDefault(); |
|
|
|
|
|
e.stopPropagation(); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
console.log('文档粘贴事件触发', e); |
|
|
|
|
|
const items = (e.clipboardData || e.originalEvent.clipboardData).items; |
|
|
const items = (e.clipboardData || e.originalEvent.clipboardData).items; |
|
|
console.log('剪贴板项', items); |
|
|
let hasImage = false; |
|
|
|
|
|
|
|
|
for (let i = 0; i < items.length; i++) { |
|
|
for (let i = 0; i < items.length; i++) { |
|
|
const item = items[i]; |
|
|
const item = items[i]; |
|
|
console.log('剪贴板项', i, item); |
|
|
|
|
|
if (item.kind === 'file' && item.type.startsWith('image/')) { |
|
|
if (item.kind === 'file' && item.type.startsWith('image/')) { |
|
|
|
|
|
console.log(`[${timestamp}] 检测到图片,开始处理`); |
|
|
e.preventDefault(); |
|
|
e.preventDefault(); |
|
|
e.stopPropagation(); |
|
|
e.stopPropagation(); |
|
|
const file = item.getAsFile(); |
|
|
hasImage = true; |
|
|
console.log('获取到的图片文件', file); |
|
|
window.__isPasting__ = true; |
|
|
// 将文件转换为临时路径 |
|
|
|
|
|
const reader = new FileReader(); |
|
|
try { |
|
|
reader.onload = (event) => { |
|
|
const file = item.getAsFile(); |
|
|
const base64 = event.target.result; |
|
|
|
|
|
console.log('转换后的base64', base64); |
|
|
// 将文件转换为base64 |
|
|
|
|
|
const base64 = await this.fileToBase64(file); |
|
|
|
|
|
console.log(`[${timestamp}] base64转换完成,长度: ${base64.length}`); |
|
|
|
|
|
|
|
|
// 创建临时文件对象 |
|
|
// 创建临时文件对象 |
|
|
const tempFile = { |
|
|
const tempFile = { |
|
|
path: base64, |
|
|
path: base64, |
|
|
@ -1135,136 +1165,180 @@ export default { |
|
|
size: file.size, |
|
|
size: file.size, |
|
|
name: file.name || 'pasted-image.png' |
|
|
name: file.name || 'pasted-image.png' |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 调用现有的图片上传逻辑 |
|
|
// 调用现有的图片上传逻辑 |
|
|
this.handlePasteImage(tempFile); |
|
|
await this.handlePasteImage(tempFile); |
|
|
}; |
|
|
} catch (error) { |
|
|
reader.readAsDataURL(file); |
|
|
console.log(`[${timestamp}] 处理粘贴图片失败:`, error); |
|
|
|
|
|
} finally { |
|
|
|
|
|
window.__isPasting__ = false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 如果没有图片,立即重置标志 |
|
|
|
|
|
if (!hasImage) { |
|
|
|
|
|
console.log(`[${timestamp}] 没有检测到图片`); |
|
|
|
|
|
this.isPasting = false; |
|
|
|
|
|
} |
|
|
// #endif |
|
|
// #endif |
|
|
}, |
|
|
}, |
|
|
handlePasteImage(file) { |
|
|
|
|
|
console.log('handlePasteImage被调用', file); |
|
|
async fileToBase64(file) { |
|
|
// 检查是否被封禁 |
|
|
const timestamp = new Date().toISOString(); |
|
|
if (this.isBanned) { |
|
|
console.log(`[${timestamp}] fileToBase64被调用,文件大小:`, file.size); |
|
|
this.showBannedTip(); |
|
|
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; |
|
|
return; |
|
|
} |
|
|
} |
|
|
let data = { |
|
|
|
|
|
originUrl: file.path, |
|
|
try { |
|
|
thumbUrl: file.path, |
|
|
// 检查是否被封禁 |
|
|
}; |
|
|
if (this.isBanned) { |
|
|
let msgInfo = { |
|
|
this.showBannedTip(); |
|
|
tmpId: this.generateId(), |
|
|
return; |
|
|
fileId: file.uid, |
|
|
} |
|
|
sendId: this.mine.id, |
|
|
|
|
|
content: JSON.stringify(data), |
|
|
let data = { |
|
|
sendTime: new Date().getTime(), |
|
|
originUrl: file.path, |
|
|
selfSend: true, |
|
|
thumbUrl: file.path, |
|
|
type: this.$enums.MESSAGE_TYPE.IMAGE, |
|
|
}; |
|
|
readedCount: 0, |
|
|
|
|
|
status: this.$enums.MESSAGE_STATUS.SENDING, |
|
|
let msgInfo = { |
|
|
}; |
|
|
tmpId: this.generateId(), |
|
|
console.log('创建的消息信息', msgInfo); |
|
|
fileId: file.uid, |
|
|
// 填充对方id |
|
|
sendId: this.mine.id, |
|
|
this.fillTargetId(msgInfo, this.chat.targetId); |
|
|
content: JSON.stringify(data), |
|
|
// 插入消息 |
|
|
sendTime: new Date().getTime(), |
|
|
this.chatStore.insertMessage(msgInfo, this.chat); |
|
|
selfSend: true, |
|
|
// 会话置顶 |
|
|
type: this.$enums.MESSAGE_TYPE.IMAGE, |
|
|
this.moveChatToTop(); |
|
|
readedCount: 0, |
|
|
// 借助file对象保存 |
|
|
status: this.$enums.MESSAGE_STATUS.SENDING, |
|
|
file.msgInfo = msgInfo; |
|
|
}; |
|
|
file.chat = this.chat; |
|
|
|
|
|
// 更新图片宽高 |
|
|
// 填充对方id |
|
|
let chat = this.chat; |
|
|
this.fillTargetId(msgInfo, this.chat.targetId); |
|
|
this.getImageSize(file).then((size) => { |
|
|
|
|
|
console.log('获取到图片尺寸', size); |
|
|
// 插入消息 |
|
|
|
|
|
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)); |
|
|
msgInfo = JSON.parse(JSON.stringify(msgInfo)); |
|
|
data.width = size.width; |
|
|
data.width = size.width; |
|
|
data.height = size.height; |
|
|
data.height = size.height; |
|
|
msgInfo.content = JSON.stringify(data); |
|
|
msgInfo.content = JSON.stringify(data); |
|
|
this.chatStore.updateMessage(msgInfo, chat); |
|
|
this.chatStore.updateMessage(msgInfo, this.chat); |
|
|
this.scrollToBottom(); |
|
|
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 |
|
|
// #ifdef H5 |
|
|
// 将base64转换为Blob |
|
|
try { |
|
|
const base64Data = file.path.split(',')[1]; |
|
|
// 将base64转换为Blob |
|
|
console.log('base64数据长度', base64Data.length); |
|
|
const base64Data = file.path.split(',')[1]; |
|
|
const byteCharacters = atob(base64Data); |
|
|
const byteCharacters = atob(base64Data); |
|
|
const byteArrays = []; |
|
|
const byteArrays = []; |
|
|
for (let offset = 0; offset < byteCharacters.length; offset += 512) { |
|
|
for (let offset = 0; offset < byteCharacters.length; offset += 512) { |
|
|
const slice = byteCharacters.slice(offset, offset + 512); |
|
|
const slice = byteCharacters.slice(offset, offset + 512); |
|
|
const byteNumbers = new Array(slice.length); |
|
|
const byteNumbers = new Array(slice.length); |
|
|
for (let i = 0; i < slice.length; i++) { |
|
|
for (let i = 0; i < slice.length; i++) { |
|
|
byteNumbers[i] = slice.charCodeAt(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); |
|
|
|
|
|
} |
|
|
} |
|
|
}, |
|
|
const byteArray = new Uint8Array(byteNumbers); |
|
|
fail: (err) => { |
|
|
byteArrays.push(byteArray); |
|
|
console.error("上传图片失败", err); |
|
|
} |
|
|
|
|
|
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({ |
|
|
uni.showToast({ |
|
|
icon: "none", |
|
|
icon: "none", |
|
|
title: "上传失败", |
|
|
title: data.message || "上传失败", |
|
|
}); |
|
|
}); |
|
|
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); |
|
|
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); |
|
|
msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; |
|
|
msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; |
|
|
this.chatStore.updateMessage(msgInfo, file.chat); |
|
|
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 |
|
|
// #endif |
|
|
}, |
|
|
}, |
|
|
onAudioStateChange(state, msgInfo) { |
|
|
onAudioStateChange(state, msgInfo) { |
|
|
@ -1772,8 +1846,10 @@ export default { |
|
|
this.unListenKeyboard(); |
|
|
this.unListenKeyboard(); |
|
|
// #ifdef H5 |
|
|
// #ifdef H5 |
|
|
// 移除粘贴事件监听 |
|
|
// 移除粘贴事件监听 |
|
|
if (this.onDocumentPaste) { |
|
|
if (this.hasPasteListener && this.pasteHandler) { |
|
|
document.removeEventListener('paste', this.onDocumentPaste); |
|
|
document.removeEventListener('paste', this.pasteHandler); |
|
|
|
|
|
this.hasPasteListener = false; |
|
|
|
|
|
this.pasteHandler = null; |
|
|
} |
|
|
} |
|
|
// #endif |
|
|
// #endif |
|
|
}, |
|
|
}, |
|
|
|