diff --git a/web/src/components/chat/InputArea.tsx b/web/src/components/chat/InputArea.tsx new file mode 100644 index 00000000..d37838f9 --- /dev/null +++ b/web/src/components/chat/InputArea.tsx @@ -0,0 +1,252 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { Send, Paperclip, Square, X, Loader2, Sparkles } from 'lucide-react'; + +interface InputAreaProps { + onSendMessage: (text: string, attachments?: { name: string; size: string; type: string }[]) => void; + isThinking: boolean; + isStreaming: boolean; + onStopStreaming?: () => void; +} + +export const InputArea: React.FC = ({ + onSendMessage, + isThinking, + isStreaming, + onStopStreaming, +}) => { + const [text, setText] = useState(''); + const [attachments, setAttachments] = useState<{ name: string; size: string; type: string }[]>([]); + const textareaRef = useRef(null); + const fileInputRef = useRef(null); + + // 自动撑高 + useEffect(() => { + if (textareaRef.current) { + textareaRef.current.style.height = 'auto'; + textareaRef.current.style.height = `${Math.min(160, Math.max(42, textareaRef.current.scrollHeight))}px`; + } + }, [text]); + + const handleSend = () => { + if (isThinking || isStreaming) { + if (isStreaming && onStopStreaming) { + onStopStreaming(); + } + return; + } + + if (!text.trim() && attachments.length === 0) { + return; + } + + onSendMessage(text.trim(), attachments); + setText(''); + setAttachments([]); + if (textareaRef.current) { + textareaRef.current.style.height = '42px'; + } + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; + + const handleFileChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files.length > 0) { + const file = e.target.files[0]; + const sizeStr = (file.size / 1024).toFixed(0) + ' KB'; + setAttachments((prev) => [ + ...prev, + { + name: file.name, + size: sizeStr, + type: file.type || 'document', + }, + ]); + e.target.value = ''; + } + }; + + const removeAttachment = (index: number) => { + setAttachments((prev) => prev.filter((_, i) => i !== index)); + }; + + return ( +
+ {/* 隐藏的原生文件上传 */} + + + {/* 已挂载文件芯片 */} + {attachments.length > 0 && ( +
+ {attachments.map((att, i) => ( +
+ 📎 {att.name} + +
+ ))} +
+ )} + + {/* 智能体状态提示 */} + {isThinking && ( +
+ + 智能体协同小队正在调取法规并推演回复... +
+ )} + + {/* 输入框与按钮条 */} +
+ {/* 附件按钮 */} + + + {/* 多行输入框 */} +