import React, { useState, useEffect, useRef } from 'react';
import { Send, Paperclip, Mic, MoreVert, Loader2, X, Smile } from 'lucide-react';
// 消息类型定义
interface Message {
id: string;
content: string;
sender: 'user' | 'ai';
timestamp: Date;
status: 'sent' | 'receiving' | 'received';
}
const AIChatWindow: React.FC = () => {
// 状态管理
const [messages, setMessages] = useState<Message[]>([
{
id: '1',
content: '您好!我是AI助手,有什么可以帮助您的吗?',
sender: 'ai',
timestamp: new Date(Date.now() - 86400000),
status: 'received'
}
]);
const [input, setInput] = useState('');
const [isTyping, setIsTyping] = useState(false);
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
// 自动滚动到最新消息
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
// 发送消息处理函数
const handleSendMessage = () => {
if (!input.trim()) return;
// 添加用户消息
const userMessage: Message = {
id: Date.now().toString(),
content: input.trim(),
sender: 'user',
timestamp: new Date(),
status: 'sent'
};
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsTyping(true);
// 模拟AI回复延迟
setTimeout(() => {
// 模拟AI回复
const aiResponses = [
'感谢您的提问!关于这个问题,我的理解是...',
'您提出的这个问题很有深度。让我为您详细解答一下...',
'根据您提供的信息,我认为最佳方案是...',
'这个问题需要从多个角度来分析。首先...',
'我明白了您的需求,我会尽力提供帮助。'
];
const randomResponse = aiResponses[Math.floor(Math.random() * aiResponses.length)];
const aiMessage: Message = {
id: (Date.now() + 1).toString(),
content: randomResponse,
sender: 'ai',
timestamp: new Date(),
status: 'received'
};
setMessages(prev => [...prev, aiMessage]);
setIsTyping(false);
}, 1500);
};
// 处理键盘回车发送
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
};
// 添加表情
const addEmoji = (emoji: string) => {
setInput(prev => prev + emoji);
setShowEmojiPicker(false);
};
// 格式化时间显示
const formatTime = (date: Date) => {
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
};
return (
<div className="flex flex-col h-full max-h-[800px] w-full max-w-md mx-auto bg-gray-50 rounded-xl shadow-lg overflow-hidden border border-gray-200">
{/* 聊天窗口头部 */}
<div className="bg-gradient-to-r from-blue-600 to-indigo-600 text-white p-4 shadow-md">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<div className="w-10 h-10 rounded-full bg-white/20 flex items-center justify-center">
<div className="w-6 h-6 rounded-full bg-blue-200 flex items-center justify-center">
<span className="text-blue-700 font-bold text-sm">AI</span>
</div>
</div>
<div>
<h2 className="font-semibold text-lg">AI 助手</h2>
<p className="text-blue-100 text-xs flex items-center">
<span className="w-2 h-2 rounded-full bg-green-400 mr-1"></span>
在线
</p>
</div>
</div>
<button className="p-2 rounded-full hover:bg-white/10 transition-colors">
<MoreVert size={20} />
</button>
</div>
</div>
{/* 聊天消息区域 */}
<div className="flex-1 overflow-y-auto p-4 space-y-6 bg-gray-50">
{/* 日期分隔线 */}
<div className="flex items-center justify-center">
<span className="text-xs text-gray-400 bg-gray-100 px-3 py-1 rounded-full">
今天
</span>
</div>
{/* 消息列表 */}
{messages.map((message) => (
<div
key={message.id}
className={`flex ${message.sender === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[80%] px-4 py-3 rounded-2xl shadow-sm ${
message.sender === 'user'
? 'bg-blue-600 text-white rounded-tr-none'
: 'bg-white text-gray-800 rounded-tl-none border border-gray-100'
}`}
>
<p className="whitespace-pre-wrap">{message.content}</p>
<p className={`text-xs mt-1 ${message.sender === 'user' ? 'text-blue-100' : 'text-gray-400'}`}>
{formatTime(message.timestamp)}
</p>
</div>
</div>
))}
{/* 正在输入提示 */}
{isTyping && (
<div className="flex justify-start">
<div className="bg-white px-4 py-3 rounded-2xl rounded-tl-none border border-gray-100 shadow-sm max-w-[80%]">
<div className="flex space-x-1">
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce [animation-delay:-0.3s]"></div>
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce [animation-delay:-0.15s]"></div>
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"></div>
</div>
</div>
</div>
)}
{/* 用于自动滚动的参考点 */}
<div ref={messagesEndRef} />
</div>
{/* 输入区域 */}
<div className="border-t border-gray-200 p-3 bg-white">
<div className="relative">
<textarea
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="输入消息..."
className="w-full p-3 pr-20 pl-10 rounded-full border border-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none transition-all duration-200"
rows={1}
/>
{/* 表情按钮 */}
<button
onClick={() => setShowEmojiPicker(!showEmojiPicker)}
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 transition-colors"
>
<Smile size={20} />
</button>
{/* 表情选择器 */}
{showEmojiPicker && (
<div className="absolute bottom-full left-0 mb-2 bg-white rounded-lg shadow-lg p-2 border border-gray-200 z-10">
<div className="grid grid-cols-7 gap-2">
{['😀', '😂', '😍', '🤔', '👍', '🙌', '👏', '🙏', '🎉', '😊', '🤗', '😢', '😮', '😴', '🤯', '🤩', '🤪', '😎', '🥳', '🤓'].map((emoji) => (
<button
key={emoji}
onClick={() => addEmoji(emoji)}
className="p-2 rounded-full hover:bg-gray-100 transition-colors"
>
{emoji}
</button>
))}
</div>
</div>
)}
{/* 附件按钮 */}
<button className="absolute right-10 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 transition-colors">
<Paperclip size={20} />
</button>
{/* 语音按钮 */}
<button className="absolute right-18 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 transition-colors">
<Mic size={20} />
</button>
{/* 发送按钮 */}
<button
onClick={handleSendMessage}
disabled={!input.trim() || isTyping}
className={`absolute right-3 top-1/2 transform -translate-y-1/2 w-8 h-8 rounded-full flex items-center justify-center transition-all ${
input.trim() && !isTyping
? 'bg-blue-600 text-white hover:bg-blue-700'
: 'bg-gray-100 text-gray-400'
}`}
>
{isTyping ? <Loader2 size={16} className="animate-spin" /> : <Send size={16} />}
</button>
</div>
</div>
</div>
);
};
export default AIChatWindow;
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。