diff --git a/src/pages/chat.js b/src/pages/chat.js
index c1f7ba5..89d0330 100644
--- a/src/pages/chat.js
+++ b/src/pages/chat.js
@@ -155,6 +155,8 @@ function bindEvents(page) {
// 文件上传
page.querySelector('#chat-attach-btn').addEventListener('click', () => _fileInputEl.click())
_fileInputEl.addEventListener('change', handleFileSelect)
+ // 粘贴图片(Ctrl+V)
+ _textarea.addEventListener('paste', handlePaste)
_messagesEl.addEventListener('scroll', () => {
const { scrollTop, scrollHeight, clientHeight } = _messagesEl
@@ -196,13 +198,31 @@ async function handleFileSelect(e) {
_fileInputEl.value = ''
}
+async function handlePaste(e) {
+ const items = Array.from(e.clipboardData?.items || [])
+ const imageItems = items.filter(item => item.type.startsWith('image/'))
+ if (!imageItems.length) return
+ e.preventDefault()
+ for (const item of imageItems) {
+ const file = item.getAsFile()
+ if (!file) continue
+ if (file.size > 5 * 1024 * 1024) { toast('粘贴的图片超过 5MB 限制', 'warning'); continue }
+ try {
+ const base64 = await fileToBase64(file)
+ _attachments.push({ type: 'image', mimeType: file.type || 'image/png', fileName: `paste-${Date.now()}.png`, content: base64 })
+ renderAttachments()
+ } catch (_) { toast('读取粘贴图片失败', 'error') }
+ }
+}
+
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => {
const dataUrl = reader.result
- const base64 = dataUrl.split(',')[1]
- resolve(base64)
+ const match = /^data:[^;]+;base64,(.+)$/.exec(dataUrl)
+ if (!match) { reject(new Error('无效的数据 URL')); return }
+ resolve(match[1])
}
reader.onerror = reject
reader.readAsDataURL(file)
@@ -229,6 +249,7 @@ function renderAttachments() {
renderAttachments()
})
})
+ updateSendState()
}
// ── Gateway 连接 ──
@@ -829,7 +850,7 @@ function updateSendState() {
_sendBtn.innerHTML = ''
_sendBtn.title = '停止生成'
} else {
- _sendBtn.disabled = !_textarea.value.trim()
+ _sendBtn.disabled = !_textarea.value.trim() && !_attachments.length
_sendBtn.innerHTML = ''
_sendBtn.title = '发送'
}