wewer
This commit is contained in:
parent
5240206f55
commit
032094652a
@ -27,6 +27,16 @@
|
||||
|
||||
<div class="chat-footer">
|
||||
<div class="footer-left" style="">
|
||||
<!-- 附件预览区(粘贴的图片先进入此处,点击发送后再发送) -->
|
||||
<div v-if="attachments.length" class="attach-preview-row" style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:4px 8px;">
|
||||
<div v-for="(att, idx) in attachments" :key="att._id" class="attach-item"
|
||||
style="position:relative;width:64px;height:64px;border:1px solid var(--el-border-color);border-radius:6px;overflow:hidden;display:flex;align-items:center;justify-content:center;background:#fff;">
|
||||
<img :src="att.previewUrl" alt="preview" style="max-width:100%;max-height:100%;" />
|
||||
<span @click="removeAttachment(idx)"
|
||||
style="position:absolute;top:2px;right:2px;background:rgba(0,0,0,.55);color:#fff;border-radius:10px;line-height:16px;height:16px;width:16px;text-align:center;cursor:pointer;font-size:12px;">×</span>
|
||||
</div>
|
||||
<el-link type="danger" @click="clearAttachments">清空</el-link>
|
||||
</div>
|
||||
<div class="upload-row"
|
||||
style="display:flex; align-items:center; gap:12px;margin-left: 6px;position: relative; top: 10px;">
|
||||
<el-icon style="font-size:24px;cursor:pointer" @click="triggerImageInput">
|
||||
@ -34,7 +44,7 @@
|
||||
<use xlink:href="#icon-tupian1"></use>
|
||||
</svg>
|
||||
</el-icon>
|
||||
<input ref="imageInput" type="file" accept="image/*" style="display:none"
|
||||
<input ref="imageInput" type="file" accept="image/*" multiple style="display:none"
|
||||
@change="handleImageChange" />
|
||||
<V3Emoji @click-emoji="onEmojiSelect" :recent="true"></V3Emoji>
|
||||
</div>
|
||||
@ -43,7 +53,7 @@
|
||||
</div>
|
||||
<!-- 发送按钮 -->
|
||||
<div class="send-button-container" style="margin-left: 12px;">
|
||||
<el-button type="primary" @click="send" :disabled="!draft.trim() || isSending || isUploading"
|
||||
<el-button type="primary" @click="send" :disabled="(!draft.trim() && attachments.length === 0) || isSending || isUploading"
|
||||
:loading="isSending" style="height: 60px;">
|
||||
{{ isSending ? '发送中...' : '发送' }}
|
||||
</el-button>
|
||||
@ -81,6 +91,8 @@ const draft = ref('')
|
||||
const imageInput = ref(null)
|
||||
const chatBody = ref(null)
|
||||
const isLoadingMessages = ref(false)
|
||||
// 附件(待发送的粘贴图片)
|
||||
const attachments = ref([])
|
||||
|
||||
// 发送者信息
|
||||
const sendeInfo = reactive({
|
||||
@ -476,7 +488,7 @@ const loadMoreMessages = () => {
|
||||
// 发送消息
|
||||
const send = async () => {
|
||||
const content = draft.value.trim()
|
||||
if (!content || isSending.value || isUploading.value) return
|
||||
if ((!content && attachments.value.length === 0) || isSending.value || isUploading.value) return
|
||||
|
||||
// 防抖检查
|
||||
const now = Date.now()
|
||||
@ -485,73 +497,98 @@ const send = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否有相同内容的发送中消息
|
||||
const hasSendingMessage = messages.value.some(msg =>
|
||||
msg._sending &&
|
||||
msg.sender_id === '888888' &&
|
||||
msg.msg_type === 1 &&
|
||||
typeof msg.content === 'object' &&
|
||||
msg.content.message === content &&
|
||||
(now - (msg._timestamp || 0)) < 5000
|
||||
)
|
||||
// 如果有文本,按原逻辑发送文本
|
||||
if (content) {
|
||||
// 检查是否有相同内容的发送中消息
|
||||
const hasSendingMessage = messages.value.some(msg =>
|
||||
msg._sending &&
|
||||
msg.sender_id === '888888' &&
|
||||
msg.msg_type === 1 &&
|
||||
typeof msg.content === 'object' &&
|
||||
msg.content.message === content &&
|
||||
(now - (msg._timestamp || 0)) < 5000
|
||||
)
|
||||
|
||||
if (hasSendingMessage) {
|
||||
// console.log('存在相同内容的发送中消息,已忽略')
|
||||
return
|
||||
if (hasSendingMessage) {
|
||||
// console.log('存在相同内容的发送中消息,已忽略')
|
||||
} else {
|
||||
isSending.value = true
|
||||
lastSendTime.value = now
|
||||
|
||||
// 生成临时消息ID,使用特殊前缀标识
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
const msg = {
|
||||
_id: tempId,
|
||||
_sending: true,
|
||||
_failed: false,
|
||||
_isTemp: true, // 标记为临时消息
|
||||
sender_id: '888888',
|
||||
sender_name: '平台',
|
||||
msg_type: 1,
|
||||
content: { message: content },
|
||||
send_time: '刚刚',
|
||||
_timestamp: now,
|
||||
_tempContent: content // 保存原始内容用于匹配
|
||||
}
|
||||
|
||||
messages.value.push(msg)
|
||||
draft.value = ''
|
||||
|
||||
// 滚动到底部
|
||||
setTimeout(() => scrollToBottom(), 100)
|
||||
isAutoScroll.value = true
|
||||
|
||||
// 发送到服务器
|
||||
send_message({
|
||||
app_id: route.query.app_id,
|
||||
content: JSON.stringify({
|
||||
message: content
|
||||
}),
|
||||
msg_type: 1,
|
||||
to_user_id: activeUser.value.sender_id
|
||||
}).then((response) => {
|
||||
msg._sending = false
|
||||
msg._failed = false
|
||||
// 发送成功后,立即轮询获取最新消息以获取真实ID
|
||||
setTimeout(() => {
|
||||
getMessages(false, 1) // 轮询最新消息
|
||||
}, 500)
|
||||
}).catch((error) => {
|
||||
msg._sending = false
|
||||
msg._failed = true
|
||||
ElMessage({ type: 'error', message: '消息发送失败' })
|
||||
}).finally(() => {
|
||||
// 重置发送状态
|
||||
isSending.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
isSending.value = true
|
||||
lastSendTime.value = now
|
||||
// 如果存在待发送附件,逐个上传发送
|
||||
if (attachments.value.length > 0) {
|
||||
const filesToSend = attachments.value.map(a => a.file)
|
||||
// 先清空附件预览并回收URL
|
||||
attachments.value.forEach(a => {
|
||||
if (a.previewUrl && a.previewUrl.startsWith('blob:')) {
|
||||
URL.revokeObjectURL(a.previewUrl)
|
||||
}
|
||||
})
|
||||
attachments.value = []
|
||||
|
||||
// 生成临时消息ID,使用特殊前缀标识
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
const msg = {
|
||||
_id: tempId,
|
||||
_sending: true,
|
||||
_failed: false,
|
||||
_isTemp: true, // 标记为临时消息
|
||||
sender_id: '888888',
|
||||
sender_name: '平台',
|
||||
msg_type: 1,
|
||||
content: { message: content },
|
||||
send_time: '刚刚',
|
||||
_timestamp: now,
|
||||
_tempContent: content // 保存原始内容用于匹配
|
||||
// 串行发送
|
||||
const queue = filesToSend.slice()
|
||||
const processNext = () => {
|
||||
if (queue.length === 0) return
|
||||
if (isUploading.value) {
|
||||
setTimeout(processNext, 150)
|
||||
return
|
||||
}
|
||||
const f = queue.shift()
|
||||
uploadImageFile(f)
|
||||
setTimeout(processNext, 150)
|
||||
}
|
||||
processNext()
|
||||
}
|
||||
|
||||
messages.value.push(msg)
|
||||
draft.value = ''
|
||||
|
||||
// 滚动到底部
|
||||
setTimeout(() => scrollToBottom(), 100)
|
||||
isAutoScroll.value = true
|
||||
|
||||
// 发送到服务器
|
||||
send_message({
|
||||
app_id: route.query.app_id,
|
||||
content: JSON.stringify({
|
||||
message: content
|
||||
}),
|
||||
msg_type: 1,
|
||||
to_user_id: activeUser.value.sender_id
|
||||
}).then((response) => {
|
||||
msg._sending = false
|
||||
msg._failed = false
|
||||
// console.log('消息发送成功', response)
|
||||
|
||||
// 发送成功后,立即轮询获取最新消息以获取真实ID
|
||||
setTimeout(() => {
|
||||
getMessages(false, 1) // 轮询最新消息
|
||||
}, 500)
|
||||
}).catch((error) => {
|
||||
msg._sending = false
|
||||
msg._failed = true
|
||||
// console.error('消息发送失败:', error)
|
||||
ElMessage({ type: 'error', message: '消息发送失败' })
|
||||
}).finally(() => {
|
||||
// 重置发送状态
|
||||
isSending.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 键盘事件处理
|
||||
@ -676,9 +713,12 @@ const uploadImageFile = (file) => {
|
||||
}
|
||||
|
||||
const handleImageChange = (e) => {
|
||||
const file = e.target.files[0]
|
||||
if (file) {
|
||||
uploadImageFile(file)
|
||||
const files = Array.from(e.target.files || [])
|
||||
if (files.length) {
|
||||
// 加入附件预览区,等待手动点击发送
|
||||
const imageFiles = files.filter(f => f && f.type && f.type.startsWith('image/'))
|
||||
if (imageFiles.length === 0) ElMessage({ type: 'error', message: '只能上传图片文件' })
|
||||
addAttachmentFiles(imageFiles)
|
||||
}
|
||||
// 清空input值,以便下次选择同一文件时也能触发change事件
|
||||
e.target.value = ''
|
||||
@ -712,18 +752,48 @@ const handlePaste = (e) => {
|
||||
// 阻止默认插入到文本框,走图片发送逻辑
|
||||
e.preventDefault()
|
||||
|
||||
// 如果有多张,只处理第一张并提示
|
||||
if (imageFiles.length > 1) {
|
||||
ElMessage({ type: 'info', message: '一次仅发送第一张图片' })
|
||||
}
|
||||
|
||||
// 使用现有上传发送流程
|
||||
uploadImageFile(imageFiles[0])
|
||||
// 将粘贴的图片添加到附件区,等待用户手动点击发送
|
||||
addAttachmentFiles(imageFiles)
|
||||
} catch (err) {
|
||||
// 失败时不影响默认粘贴行为
|
||||
}
|
||||
}
|
||||
|
||||
// 附件辅助:添加、移除、清空
|
||||
const addAttachmentFiles = (files) => {
|
||||
const list = Array.from(files || [])
|
||||
list.forEach((file) => {
|
||||
if (!file || !file.type || !file.type.startsWith('image/')) return
|
||||
if (file.size > 10 * 1024 * 1024) {
|
||||
ElMessage({ type: 'error', message: '图片大小不能超过10MB' })
|
||||
return
|
||||
}
|
||||
const previewUrl = URL.createObjectURL(file)
|
||||
attachments.value.push({
|
||||
_id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
||||
file,
|
||||
previewUrl
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const removeAttachment = (index) => {
|
||||
const item = attachments.value[index]
|
||||
if (item && item.previewUrl && item.previewUrl.startsWith('blob:')) {
|
||||
URL.revokeObjectURL(item.previewUrl)
|
||||
}
|
||||
attachments.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const clearAttachments = () => {
|
||||
attachments.value.forEach(a => {
|
||||
if (a.previewUrl && a.previewUrl.startsWith('blob:')) {
|
||||
URL.revokeObjectURL(a.previewUrl)
|
||||
}
|
||||
})
|
||||
attachments.value = []
|
||||
}
|
||||
|
||||
// 拖拽事件处理
|
||||
const handleDragEnter = (e) => {
|
||||
e.preventDefault()
|
||||
@ -781,10 +851,8 @@ const handleDrop = (e) => {
|
||||
return
|
||||
}
|
||||
|
||||
// 只处理第一个图片文件
|
||||
if (imageFiles.length > 0) {
|
||||
uploadImageFile(imageFiles[0])
|
||||
}
|
||||
// 加入附件预览区,等待手动点击发送
|
||||
addAttachmentFiles(imageFiles)
|
||||
}
|
||||
|
||||
// 重发消息
|
||||
@ -1244,4 +1312,8 @@ watch(messages, async (newVal, oldVal) => {
|
||||
display: flex;
|
||||
align-items: center
|
||||
}
|
||||
.attach-preview-row{
|
||||
position: absolute;
|
||||
bottom: 90px;
|
||||
}
|
||||
</style>
|
||||
Loading…
x
Reference in New Issue
Block a user