网页版支持粘贴图片发送、聊天图片保存到本地

老板反馈:"没有复制粘贴图片功能,只能保存在电脑里面发,图片也没有保存功能"

- 聊天输入框新增粘贴图片检测:剪贴板里有图片(截图工具/复制图片)时直接走发图逻辑,
  不再要求先存到电脑里再用文件选择器上传;普通文本粘贴不受影响
- "查看大图"预览里新增"保存图片"下载按钮,取消息自带的原始文件名,
  取不到时按mime类型生成默认文件名

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-29 02:42:21 +08:00
co-authored by Claude Sonnet 5
parent 9abae5030b
commit 3d48abdf73
2 changed files with 58 additions and 2 deletions
@@ -936,6 +936,29 @@ export function ConversationView() {
}
};
/** User report (老板, 2026-09-29): "没有复制粘贴图片功能,只能保存在电脑里面发" -- e.g. a screenshot
* tool, or "复制图片" from a browser/chat app, only ever puts raw pixel data on the clipboard, never
* a File the hidden `<input type="file">` above can see; without this, getting that image into a
* message required saving it to disk first and picking it via handlePickImage. `item.getAsFile()`
* returns a real File, indistinguishable from one chosen through the file picker, so this reuses
* the exact same send path. Only preventDefault once an actual image item is found -- an ordinary
* text paste (no image on the clipboard at all) is left completely untouched. */
const handleComposerPaste = (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
const items = e.clipboardData?.items;
if (!items) return;
for (let i = 0; i < items.length; i += 1) {
const item = items[i];
if (item.kind === 'file' && item.type.startsWith('image/')) {
const file = item.getAsFile();
if (!file) continue;
e.preventDefault();
const reply = consumePendingReply();
sendImage(topic, file, reply).catch((err) => showError(err, '图片发送失败'));
return;
}
}
};
return (
<div className="flex h-full w-full flex-col bg-surface-chat">
<header className="flex h-15 flex-none items-center justify-between border-b border-border-panel bg-white px-5">
@@ -1214,6 +1237,7 @@ export function ConversationView() {
value={draft}
onChange={handleDraftChange}
onKeyDown={handleKeyDown}
onPaste={handleComposerPaste}
placeholder="输入消息,Enter 发送,Shift+Enter 换行"
className="min-h-[72px] flex-1 resize-none bg-transparent text-[15px] leading-normal outline-none placeholder:text-text-placeholder-1"
/>