import React, { useState, useRef, useEffect } from 'react'; import { Send, Menu, MoreHorizontal, UserPlus, UserMinus, Users2, Users, MoreVertical, MessageCircle } from 'lucide-react'; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog" // 使用本地头像数据,避免外部依赖 const getAvatarData = (name: string) => { const colors = ['#1abc9c', '#3498db', '#9b59b6', '#f1c40f', '#e67e22']; const index = (name.charCodeAt(0) + (name.charCodeAt(1) || 0 )) % colors.length; return { backgroundColor: colors[index], text: name[0], }; }; // 单个完整头像 const SingleAvatar = ({ user }: { user: User }) => { const avatarData = getAvatarData(user.name); return (
{avatarData.text}
); }; // 左右分半头像 const HalfAvatar = ({ user, isFirst }: { user: User, isFirst: boolean }) => { const avatarData = getAvatarData(user.name); return (
{avatarData.text}
); }; // 四分之一头像 const QuarterAvatar = ({ user, index }: { user: User, index: number }) => { const avatarData = getAvatarData(user.name); return (
{avatarData.text}
); }; const ChatUI = () => { // 添加 AI 角色定义 const aiCharacters = [ { id: 'ai1', name: "暖心姐", personality: "high_eq" }, { id: 'ai2', name: "直男哥", personality: "low_eq" } ]; const [users, setUsers] = useState([ { id: 1, name: "我" }, ...aiCharacters ]); const [showMembers, setShowMembers] = useState(false); const [messages, setMessages] = useState([ ]); const [inputMessage, setInputMessage] = useState(""); const [isLoading, setIsLoading] = useState(false); const [pendingContent, setPendingContent] = useState(""); const [isTyping, setIsTyping] = useState(false); const typingSpeed = 30; const currentMessageRef = useRef(null); const typewriterRef = useRef(null); const accumulatedContentRef = useRef(""); // 用于跟踪完整内容 const messagesEndRef = useRef(null); const abortController = new AbortController(); const handleRemoveUser = (userId: number) => { setUsers(users.filter(user => user.id !== userId)); }; const typeWriter = (newContent: string, messageId: number) => { if (!newContent) return; setIsTyping(true); currentMessageRef.current = messageId; // 获取已显示的内容长度作为起始位置 const startIndex = accumulatedContentRef.current.length; let currentIndex = startIndex; // 清除之前的打字效果 if (typewriterRef.current) { clearInterval(typewriterRef.current); } typewriterRef.current = setInterval(() => { currentIndex++; setMessages(prev => { const newMessages = [...prev]; const messageIndex = newMessages.findIndex(msg => msg.id === messageId); if (messageIndex !== -1) { newMessages[messageIndex] = { ...newMessages[messageIndex], content: newContent.slice(0, currentIndex) }; } return newMessages; }); if (currentIndex >= newContent.length) { if (typewriterRef.current) { clearInterval(typewriterRef.current); } setIsTyping(false); currentMessageRef.current = null; accumulatedContentRef.current = newContent; // 更新完整内容 } }, typingSpeed); }; const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }; useEffect(() => { scrollToBottom(); }, [messages]); const handleSendMessage = async () => { if (!inputMessage.trim()) return; // 添加用户消息 const userMessage = { id: messages.length + 1, sender: users[0], content: inputMessage, isAI: false }; setMessages(prev => [...prev, userMessage]); setInputMessage(""); setIsLoading(true); setPendingContent(""); accumulatedContentRef.current = ""; // 构建历史消息数组 const messageHistory = messages.map(msg => ({ role: 'system', content: msg.sender.name == "我" ? 'user:' + msg.content : msg.sender.name + ':' + msg.content, name: msg.sender.name })); // 依次请求两个 AI 的回复 for (let i = 0; i < aiCharacters.length; i++) { // 创建当前 AI 角色的消息 const aiMessage = { id: messages.length + 2 + i, sender: { id: aiCharacters[i].id, name: aiCharacters[i].name }, content: "", isAI: true }; // 添加当前 AI 的消息 setMessages(prev => [...prev, aiMessage]); try { const response = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ message: inputMessage, personality: aiCharacters[i].personality, history: messageHistory, aiName: aiCharacters[i].name }), }); if (!response.ok) { throw new Error('请求失败'); } const reader = response.body?.getReader(); const decoder = new TextDecoder(); if (!reader) { throw new Error('无法获取响应流'); } let buffer = ''; let completeResponse = ''; // 用于跟踪完整的响应 while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); let newlineIndex; while ((newlineIndex = buffer.indexOf('\n')) >= 0) { const line = buffer.slice(0, newlineIndex); buffer = buffer.slice(newlineIndex + 1); if (line.startsWith('data: ')) { try { const data = JSON.parse(line.slice(6)); if (data.content) { completeResponse += data.content; setMessages(prev => { const newMessages = [...prev]; const aiMessageIndex = newMessages.findIndex(msg => msg.id === aiMessage.id); if (aiMessageIndex !== -1) { newMessages[aiMessageIndex] = { ...newMessages[aiMessageIndex], content: completeResponse }; } return newMessages; }); } } catch (e) { console.error('解析响应数据失败:', e); } } } } // 等待一小段时间再开始下一个 AI 的回复 if (i < aiCharacters.length - 1) { await new Promise(resolve => setTimeout(resolve, 1000)); } } catch (error) { console.error("发送消息失败:", error); setMessages(prev => prev.map(msg => msg.id === aiMessage.id ? { ...msg, content: "错误: " + error.message, isError: true } : msg )); } } setIsLoading(false); }; const handleCancel = () => { abortController.abort(); }; // 清理打字机效果 useEffect(() => { return () => { if (typewriterRef.current) { clearInterval(typewriterRef.current); } }; }, []); return (
{/* Header */}
{/* 左侧群组信息 */}
{users.length === 1 ? ( // 单个成员时显示一个大头像 ) : users.length === 2 ? ( // 两个成员时左右分布
{users.slice(0, 2).map((user, index) => ( ))}
) : users.length === 3 ? ( // 三个成员时上2下1
{users.slice(0, 2).map((user, index) => ( ))}
) : ( // 四个或更多成员时显示2x2网格
{users.slice(0, 4).map((user, index) => ( ))}
)}

硅碳摸鱼交流群

{users.length} 名成员

{/* 右侧头像组和按钮 */}
{users.slice(0, 4).map((user) => { const avatarData = getAvatarData(user.name); return ( {avatarData.text}

{user.name}

); })} {users.length > 4 && (
+{users.length - 4}
)}
{/* Main Chat Area */}
{messages.map((message) => (
{message.sender.name !== "我" && ( {message.sender.name[0]} )}
{message.sender.name}
{message.content} {message.isAI && isTyping && currentMessageRef.current === message.id && ( )}
{message.sender.name === "我" && ( {message.sender.name[0]} )}
))}
{/* Input Area */}
setInputMessage(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()} />
{/* Members Management Dialog */} 群成员管理
当前成员({users.length})
{users.map((user) => (
{user.name[0]} {user.name}
{user.name !== "我" && ( )}
))}
); }; export default ChatUI;