wewer
This commit is contained in:
parent
5240206f55
commit
032094652a
@ -27,6 +27,16 @@
|
|||||||
|
|
||||||
<div class="chat-footer">
|
<div class="chat-footer">
|
||||||
<div class="footer-left" style="">
|
<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"
|
<div class="upload-row"
|
||||||
style="display:flex; align-items:center; gap:12px;margin-left: 6px;position: relative; top: 10px;">
|
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">
|
<el-icon style="font-size:24px;cursor:pointer" @click="triggerImageInput">
|
||||||
@ -34,7 +44,7 @@
|
|||||||
<use xlink:href="#icon-tupian1"></use>
|
<use xlink:href="#icon-tupian1"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</el-icon>
|
</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" />
|
@change="handleImageChange" />
|
||||||
<V3Emoji @click-emoji="onEmojiSelect" :recent="true"></V3Emoji>
|
<V3Emoji @click-emoji="onEmojiSelect" :recent="true"></V3Emoji>
|
||||||
</div>
|
</div>
|
||||||
@ -43,7 +53,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 发送按钮 -->
|
<!-- 发送按钮 -->
|
||||||
<div class="send-button-container" style="margin-left: 12px;">
|
<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;">
|
:loading="isSending" style="height: 60px;">
|
||||||
{{ isSending ? '发送中...' : '发送' }}
|
{{ isSending ? '发送中...' : '发送' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -81,6 +91,8 @@ const draft = ref('')
|
|||||||
const imageInput = ref(null)
|
const imageInput = ref(null)
|
||||||
const chatBody = ref(null)
|
const chatBody = ref(null)
|
||||||
const isLoadingMessages = ref(false)
|
const isLoadingMessages = ref(false)
|
||||||
|
// 附件(待发送的粘贴图片)
|
||||||
|
const attachments = ref([])
|
||||||
|
|
||||||
// 发送者信息
|
// 发送者信息
|
||||||
const sendeInfo = reactive({
|
const sendeInfo = reactive({
|
||||||
@ -476,7 +488,7 @@ const loadMoreMessages = () => {
|
|||||||
// 发送消息
|
// 发送消息
|
||||||
const send = async () => {
|
const send = async () => {
|
||||||
const content = draft.value.trim()
|
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()
|
const now = Date.now()
|
||||||
@ -485,6 +497,8 @@ const send = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果有文本,按原逻辑发送文本
|
||||||
|
if (content) {
|
||||||
// 检查是否有相同内容的发送中消息
|
// 检查是否有相同内容的发送中消息
|
||||||
const hasSendingMessage = messages.value.some(msg =>
|
const hasSendingMessage = messages.value.some(msg =>
|
||||||
msg._sending &&
|
msg._sending &&
|
||||||
@ -497,9 +511,7 @@ const send = async () => {
|
|||||||
|
|
||||||
if (hasSendingMessage) {
|
if (hasSendingMessage) {
|
||||||
// console.log('存在相同内容的发送中消息,已忽略')
|
// console.log('存在相同内容的发送中消息,已忽略')
|
||||||
return
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
isSending.value = true
|
isSending.value = true
|
||||||
lastSendTime.value = now
|
lastSendTime.value = now
|
||||||
|
|
||||||
@ -537,8 +549,6 @@ const send = async () => {
|
|||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
msg._sending = false
|
msg._sending = false
|
||||||
msg._failed = false
|
msg._failed = false
|
||||||
// console.log('消息发送成功', response)
|
|
||||||
|
|
||||||
// 发送成功后,立即轮询获取最新消息以获取真实ID
|
// 发送成功后,立即轮询获取最新消息以获取真实ID
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
getMessages(false, 1) // 轮询最新消息
|
getMessages(false, 1) // 轮询最新消息
|
||||||
@ -546,12 +556,39 @@ const send = async () => {
|
|||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
msg._sending = false
|
msg._sending = false
|
||||||
msg._failed = true
|
msg._failed = true
|
||||||
// console.error('消息发送失败:', error)
|
|
||||||
ElMessage({ type: 'error', message: '消息发送失败' })
|
ElMessage({ type: 'error', message: '消息发送失败' })
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
// 重置发送状态
|
// 重置发送状态
|
||||||
isSending.value = false
|
isSending.value = false
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果存在待发送附件,逐个上传发送
|
||||||
|
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 = []
|
||||||
|
|
||||||
|
// 串行发送
|
||||||
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 键盘事件处理
|
// 键盘事件处理
|
||||||
@ -676,9 +713,12 @@ const uploadImageFile = (file) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleImageChange = (e) => {
|
const handleImageChange = (e) => {
|
||||||
const file = e.target.files[0]
|
const files = Array.from(e.target.files || [])
|
||||||
if (file) {
|
if (files.length) {
|
||||||
uploadImageFile(file)
|
// 加入附件预览区,等待手动点击发送
|
||||||
|
const imageFiles = files.filter(f => f && f.type && f.type.startsWith('image/'))
|
||||||
|
if (imageFiles.length === 0) ElMessage({ type: 'error', message: '只能上传图片文件' })
|
||||||
|
addAttachmentFiles(imageFiles)
|
||||||
}
|
}
|
||||||
// 清空input值,以便下次选择同一文件时也能触发change事件
|
// 清空input值,以便下次选择同一文件时也能触发change事件
|
||||||
e.target.value = ''
|
e.target.value = ''
|
||||||
@ -712,18 +752,48 @@ const handlePaste = (e) => {
|
|||||||
// 阻止默认插入到文本框,走图片发送逻辑
|
// 阻止默认插入到文本框,走图片发送逻辑
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
// 如果有多张,只处理第一张并提示
|
// 将粘贴的图片添加到附件区,等待用户手动点击发送
|
||||||
if (imageFiles.length > 1) {
|
addAttachmentFiles(imageFiles)
|
||||||
ElMessage({ type: 'info', message: '一次仅发送第一张图片' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用现有上传发送流程
|
|
||||||
uploadImageFile(imageFiles[0])
|
|
||||||
} catch (err) {
|
} 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) => {
|
const handleDragEnter = (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@ -781,10 +851,8 @@ const handleDrop = (e) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只处理第一个图片文件
|
// 加入附件预览区,等待手动点击发送
|
||||||
if (imageFiles.length > 0) {
|
addAttachmentFiles(imageFiles)
|
||||||
uploadImageFile(imageFiles[0])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重发消息
|
// 重发消息
|
||||||
@ -1244,4 +1312,8 @@ watch(messages, async (newVal, oldVal) => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center
|
align-items: center
|
||||||
}
|
}
|
||||||
|
.attach-preview-row{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 90px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user