import React, { useState, useEffect, useRef } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { fetchMessages } from '../../store/chat/chat.messages.thunks'; import { resetMessageOperation, addMessage } from '../../store/chat/chat.slice'; import { showNotification } from '../../store/notification.slice'; import { createChatRecord, fetchAvailableDatasets, fetchConversationDetail } from '../../store/chat/chat.thunks'; import { fetchKnowledgeBases } from '../../store/knowledgeBase/knowledgeBase.thunks'; import SvgIcon from '../../components/SvgIcon'; import SafeMarkdown from '../../components/SafeMarkdown'; import ChatSidePanel from './ChatSidePanel'; import { get } from '../../services/api'; export default function ChatWindow({ chatId, knowledgeBaseId }) { const dispatch = useDispatch(); const [inputMessage, setInputMessage] = useState(''); const [loading, setLoading] = useState(false); const messagesEndRef = useRef(null); const hasLoadedDetailRef = useRef({}); // 添加ref来跟踪已加载的会话 // Gmail无邮件警告状态 const [noEmailsWarning, setNoEmailsWarning] = useState(null); const [troubleshooting, setTroubleshooting] = useState(null); // 从 Redux store 获取聊天数据 const chats = useSelector((state) => state.chat.chats.items); const currentChat = chats.find((chat) => chat.conversation_id === chatId); const messages = currentChat?.messages || []; // 获取消息操作状态 const messageStatus = useSelector((state) => state.chat.messageOperation.status); const messageError = useSelector((state) => state.chat.messageOperation.error); // Gmail集成状态 const gmailSetupStatus = useSelector((state) => state.gmailChat?.setup?.status); const gmailNoEmailsWarning = useSelector((state) => state.gmailChat?.setup?.noEmailsWarning); const gmailTroubleshooting = useSelector((state) => state.gmailChat?.setup?.troubleshooting); // 使用新的Redux状态结构 const knowledgeBases = useSelector((state) => state.knowledgeBase.knowledgeBases || []); const knowledgeBase = knowledgeBases.find((kb) => kb.id === knowledgeBaseId); const isLoadingKnowledgeBases = useSelector((state) => state.knowledgeBase.loading); // 获取可用数据集列表 const availableDatasets = useSelector((state) => state.chat.availableDatasets.items || []); const availableDatasetsLoading = useSelector((state) => state.chat.availableDatasets.status === 'loading'); // 获取当前活跃聊天ID const activeConversationId = useSelector((state) => state.chat.activeConversationId); // 聊天操作状态 const chatOperationStatus = useSelector((state) => state.chat.chatOperation.status); // 提取达人邮箱信息(用于侧边栏功能) const [talentEmail, setTalentEmail] = useState(''); // 监听知识库ID变更,确保保存在组件状态中 const [selectedKnowledgeBaseIds, setSelectedKnowledgeBaseIds] = useState([]); // 检查是否存在Gmail无邮件警告 useEffect(() => { // 优先使用Redux状态中的警告 if (gmailNoEmailsWarning) { setNoEmailsWarning(gmailNoEmailsWarning); setTroubleshooting(gmailTroubleshooting); return; } // 从localStorage中获取警告信息 try { const savedWarning = localStorage.getItem('gmailNoEmailsWarning'); if (savedWarning) { const warningData = JSON.parse(savedWarning); // 检查是否是当前会话的警告且未过期(24小时内) const isCurrentChat = warningData.chatId === chatId; const isStillValid = Date.now() - warningData.timestamp < 24 * 60 * 60 * 1000; if (isCurrentChat && isStillValid) { setNoEmailsWarning(warningData.message); setTroubleshooting(warningData.troubleshooting); } else if (!isStillValid) { // 警告过期,清除 localStorage.removeItem('gmailNoEmailsWarning'); } } } catch (error) { console.error('解析Gmail警告信息失败:', error); localStorage.removeItem('gmailNoEmailsWarning'); } }, [chatId, gmailNoEmailsWarning, gmailTroubleshooting]); // 当currentChat或knowledgeBaseId更新时,更新selectedKnowledgeBaseIds useEffect(() => { // 优先使用currentChat中的知识库列表 if (currentChat && currentChat.datasets && currentChat.datasets.length > 0) { const datasetIds = currentChat.datasets.map((ds) => ds.id); console.log('从会话中获取知识库列表:', datasetIds); setSelectedKnowledgeBaseIds(datasetIds); } // 其次使用URL中传入的知识库ID else if (knowledgeBaseId) { // 可能是单个ID或以逗号分隔的多个ID const ids = knowledgeBaseId.split(',').map((id) => id.trim()); console.log('从URL参数中获取知识库列表:', ids); setSelectedKnowledgeBaseIds(ids); } }, [currentChat, knowledgeBaseId]); // 获取聊天详情 useEffect(() => { if (!chatId) return; // 如果已经加载过这个chatId的详情,不再重复加载 if (hasLoadedDetailRef.current[chatId]) { console.log('跳过已加载过的会话详情:', chatId); return; } // 检查是否是已存在的聊天 const existingChat = chats.find((chat) => chat.conversation_id === chatId); // 如果已经有这个聊天的消息,则不需要获取详情 if (existingChat && existingChat.messages && existingChat.messages.length > 0) { console.log('聊天已存在且有消息,跳过详情获取:', chatId); hasLoadedDetailRef.current[chatId] = true; return; } console.log('获取会话详情:', chatId); setLoading(true); dispatch(fetchConversationDetail(chatId)) .unwrap() .then((response) => { console.log('获取会话详情成功:', response); // 标记为已加载 hasLoadedDetailRef.current[chatId] = true; }) .catch((error) => { console.error('获取会话详情失败:', error); dispatch( showNotification({ message: `获取聊天详情失败: ${error || '未知错误'}`, type: 'danger', }) ); }) .finally(() => { setLoading(false); }); }, [chatId, dispatch, chats]); // 组件销毁时完全清空ref缓存 useEffect(() => { return () => { hasLoadedDetailRef.current = {}; }; }, []); // 新会话自动添加欢迎消息 useEffect(() => { // 如果是新聊天且没有任何消息,添加一条系统欢迎消息 if (chatId && messages.length === 0 && !loading && messageStatus !== 'loading') { // 检查是否有无邮件警告作为首条消息 let isNoEmailsFirstMessage = false; let noEmailsMessage = null; try { const savedWarning = localStorage.getItem('gmailNoEmailsWarning'); if (savedWarning) { const warningData = JSON.parse(savedWarning); // 检查是否是当前会话的警告、是否是首条消息 if (warningData.chatId === chatId && warningData.isFirstMessage) { isNoEmailsFirstMessage = true; noEmailsMessage = warningData.message; } } } catch (error) { console.error('解析Gmail警告信息失败:', error); } if (isNoEmailsFirstMessage && noEmailsMessage) { // 使用警告消息作为首条消息 dispatch( addMessage({ conversationId: chatId, message: { id: 'gmail-warning-' + Date.now(), role: 'assistant', content: `⚠️ ${noEmailsMessage}\n\n您仍然可以在此聊天中提问,但可能无法获得与邮件内容相关的回答。`, created_at: new Date().toISOString(), }, }) ); // 移除isFirstMessage标记,防止再次显示 try { const savedWarning = localStorage.getItem('gmailNoEmailsWarning'); if (savedWarning) { const warningData = JSON.parse(savedWarning); warningData.isFirstMessage = false; localStorage.setItem('gmailNoEmailsWarning', JSON.stringify(warningData)); } } catch (error) { console.error('更新Gmail警告信息失败:', error); } } else { // 使用常规欢迎消息 const selectedKb = knowledgeBase || availableDatasets.find((ds) => ds.id === knowledgeBaseId) || { name: '知识库' }; dispatch( addMessage({ conversationId: chatId, message: { id: 'welcome-' + Date.now(), role: 'assistant', content: `欢迎使用${selectedKb.name}。您可以向我提问任何相关问题。`, created_at: new Date().toISOString(), }, }) ); } } }, [chatId, messages.length, loading, messageStatus, knowledgeBase, knowledgeBaseId, availableDatasets, dispatch]); // 监听发送消息状态 useEffect(() => { if (messageStatus === 'failed' && messageError) { dispatch( showNotification({ message: `发送失败: ${messageError}`, type: 'danger', }) ); dispatch(resetMessageOperation()); } }, [messageStatus, messageError, dispatch]); // 滚动到底部 useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); // 获取当前会话的知识库信息 useEffect(() => { // 如果currentChat有数据集信息,优先使用它 if (currentChat && currentChat.datasets && currentChat.datasets.length > 0) { return; } // 如果没有会话数据集信息,但有knowledgeBaseId,尝试从知识库列表中查找 if (knowledgeBaseId && knowledgeBases.length === 0 && !availableDatasets.length) { dispatch(fetchAvailableDatasets()); } }, [dispatch, knowledgeBaseId, knowledgeBases, currentChat, availableDatasets]); useEffect(() => { // 尝试从聊天数据或知识库中提取达人邮箱 if (currentChat?.talent_email) { setTalentEmail(currentChat.talent_email); } else if (currentChat?.datasets?.[0]?.talent_email) { setTalentEmail(currentChat.datasets[0].talent_email); } else if (currentChat?.datasets?.[0]?.name && currentChat?.datasets[0]?.name.includes('@')) { // 如果知识库名称中包含邮箱格式,提取出来 const emailMatch = currentChat.datasets[0].name.match(/[\w.-]+@[\w.-]+\.\w+/); if (emailMatch) { setTalentEmail(emailMatch[0]); } } else if (messages.length > 0) { // 从消息中查找可能包含的达人邮箱 for (const message of messages) { const emailMatch = message.content?.match(/[\w.-]+@[\w.-]+\.\w+/); if (emailMatch) { setTalentEmail(emailMatch[0]); break; } } } }, [currentChat, messages]); const handleSendMessage = (e) => { e.preventDefault(); if (!inputMessage.trim() || messageStatus === 'loading') return; console.log('准备发送消息:', inputMessage); console.log('当前会话ID:', chatId); // 获取知识库ID列表 let dataset_id_list = []; // 优先使用组件状态中保存的知识库列表 if (selectedKnowledgeBaseIds.length > 0) { // 使用已保存的知识库列表 dataset_id_list = selectedKnowledgeBaseIds.map((id) => id.replace(/-/g, '')); console.log('使用组件状态中的知识库列表:', dataset_id_list); } else if (currentChat && currentChat.datasets && currentChat.datasets.length > 0) { // 如果已有会话,使用会话中的知识库 dataset_id_list = currentChat.datasets.map((ds) => ds.id.replace(/-/g, '')); console.log('使用会话中的知识库列表:', dataset_id_list); } else if (knowledgeBaseId) { // 如果是新会话,使用当前选择的知识库 // 可能是单个ID或以逗号分隔的多个ID const ids = knowledgeBaseId.split(',').map((id) => id.trim().replace(/-/g, '')); dataset_id_list = ids; console.log('使用URL参数中的知识库:', dataset_id_list); } else if (availableDatasets.length > 0) { // 如果都没有,尝试使用可用知识库列表中的第一个 dataset_id_list = [availableDatasets[0].id.replace(/-/g, '')]; console.log('使用可用知识库列表中的第一个:', dataset_id_list); } if (dataset_id_list.length === 0) { dispatch( showNotification({ message: '发送失败:未选择知识库', type: 'danger', }) ); return; } console.log('发送消息参数:', { dataset_id_list, question: inputMessage, conversation_id: chatId, }); // 发送消息到服务器 dispatch( createChatRecord({ dataset_id_list: dataset_id_list, question: inputMessage, conversation_id: chatId, }) ) .unwrap() .then((response) => { // 成功发送后,可以执行任何需要的操作 console.log('消息发送成功:', response); }) .catch((error) => { // 发送失败,显示错误信息 console.error('消息发送失败:', error); dispatch( showNotification({ message: `发送失败: ${error}`, type: 'danger', }) ); }); // 清空输入框 setInputMessage(''); }; // 渲染加载状态 const renderLoading = () => (
加载消息失败
{messageError}
暂无消息,开始发送第一条消息吧