This commit is contained in:
左哥 2025-11-11 23:14:54 +08:00
parent 5240206f55
commit 032094652a

View File

@ -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>