2025-08-23 19:11:16 +08:00

139 lines
4.3 KiB
TypeScript

import React, { useRef, useState } from "react";
import { Image as ImageIcon, Send, Trash2 } from "lucide-react";
import { MessageBlock } from "./types";
import { useUploadFile } from "@/app/service/domain/service";
interface InputBarProps {
onSend: (blocks: MessageBlock[]) => void;
}
export function InputBar({ onSend }: InputBarProps) {
const [text, setText] = useState("");
const [isUploading, setIsUploading] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
const [imageUrl, setImageUrl] = useState<string | null>(null);
const { uploadFile } = useUploadFile();
const handleSend = () => {
const blocks: MessageBlock[] = [];
if (text.trim()) blocks.push({ type: "text" as const, text: text.trim() });
if (imageUrl) blocks.push({ type: "image" as const, url: imageUrl });
if (!blocks.length) return;
onSend(blocks);
setText("");
setImageUrl(null);
};
const handleFileUpload = async (file: File) => {
try {
setIsUploading(true);
const url = await uploadFile(file, (progress) => {
setUploadProgress(progress);
});
setImageUrl(url);
} catch (error) {
console.error("上传失败:", error);
// 可以添加错误提示
} finally {
setIsUploading(false);
setUploadProgress(0);
}
};
const onFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
if (!file.type.startsWith('image/')) {
alert('请选择图片文件');
return;
}
await handleFileUpload(file);
}
e.currentTarget.value = ""; // reset
};
const removeImage = () => {
setImageUrl(null);
};
return (
<div data-alt="input-bar">
{/* 图片预览 */}
{imageUrl && (
<div className="px-3 pt-3" data-alt="image-preview">
<div className="relative group w-24 h-24">
<img
src={imageUrl}
className="h-full w-full object-cover rounded-xl border border-white/10"
alt="预览图"
/>
<button
onClick={removeImage}
className="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition bg-black/60 text-white rounded-full p-1"
title="移除"
data-alt="remove-image-button"
>
<Trash2 size={14} />
</button>
</div>
</div>
)}
{/* 上传进度 */}
{isUploading && (
<div className="px-3 py-2">
<div className="w-full bg-gray-200 rounded-full h-1">
<div
className="bg-blue-500 h-1 rounded-full transition-all duration-300"
style={{ width: `${uploadProgress}%` }}
/>
</div>
</div>
)}
<div className="flex items-center gap-2 px-3 m-3 border border-gray-700 rounded-[2rem]">
{/* 图片上传 */}
<label
className={`cursor-pointer inline-flex items-center gap-2 p-2 my-2 rounded-full hover:bg-gray-700/50 text-gray-100 ${isUploading ? 'opacity-50 cursor-not-allowed' : ''}`}
data-alt="file-upload"
>
<ImageIcon size={16} />
<input
className="hidden"
type="file"
accept="image/*"
onChange={onFileChange}
disabled={isUploading}
/>
</label>
{/* 文本输入 */}
<input
className="flex-1 bg-transparent text-gray-100 px-3 py-2 outline-none placeholder:text-gray-400"
placeholder="输入文字…"
value={text}
onChange={(e) => setText(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSend();
}
}}
data-alt="text-input"
/>
{/* 发送按钮 */}
<button
onClick={handleSend}
className="inline-flex items-center gap-2 p-2 my-2 rounded-full bg-blue-500 hover:bg-blue-400 text-white shadow disabled:bg-gray-500 disabled:hover:bg-gray-500 disabled:cursor-not-allowed"
data-alt="send-button"
disabled={!text.trim() && !imageUrl}
>
<Send size={18} />
</button>
</div>
</div>
);
}