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 */}
{/* Members Management Dialog */}
);
};
export default ChatUI;