import React, { useState, useRef, useEffect } from 'react' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import { Input, Button, Typography, Spin, message, Layout, Tooltip, Input as AntInput, Modal, Upload, Dropdown, Space, Select, } from 'antd' import { ArrowUpOutlined, LinuxOutlined, RobotOutlined, LoadingOutlined, CopyOutlined, ReloadOutlined, PaperClipOutlined, } from '@ant-design/icons' import { callLocalChatAPI, handleAPIError } from '@/config/api' import { conversationStore } from '@/utils/pageConversationStore' import './ChatConversation.less' const { Text } = Typography const { TextArea } = Input const { Sider, Content } = Layout function ChatConversation() { // 会话相关状态 - 使用独立的存储 const [conversations, setConversations] = useState(conversationStore.getConversations()) const [currentConversationId, setCurrentConversationId] = useState(conversationStore.getCurrentConversationId()) const [inputValue, setInputValue] = useState('') const [loading, setLoading] = useState(false) const [sidebarExpanded, setSidebarExpanded] = useState(false) const [editModalVisible, setEditModalVisible] = useState(false) const [editingConversation, setEditingConversation] = useState(null) const [newTitle, setNewTitle] = useState('') const [deepThinkingEnabled, setDeepThinkingEnabled] = useState(false) const [typingMessage, setTypingMessage] = useState('') const [isTyping, setIsTyping] = useState(false) // 流式输出状态 - 已移除 const messagesEndRef = useRef(null) const inputRef = useRef(null) const typewriterTimeoutRef = useRef(null) // 获取当前会话 const currentConversation = conversations.find(conv => conv.id === currentConversationId) const currentMessages = currentConversation?.messages || [] // 当前会话消息 // 调试信息 console.log('Current conversation state:', { conversations: conversations.length, currentConversationId, currentConversation: !!currentConversation, currentMessages: currentMessages.length }) // console.log(currentMessages,"=========================333333") const chatScrollRef = useRef(null); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }) } // 流式输出相关函数 - 已移除 const handleChange = value => { console.log(`selected ${value}`); } // 订阅对话状态变化 useEffect(() => { const unsubscribe = conversationStore.subscribe(({ conversations, currentConversationId }) => { setConversations(conversations) setCurrentConversationId(currentConversationId) }) return unsubscribe }, []) // 清理定时器 useEffect(() => { return () => { if (typewriterTimeoutRef.current) { clearTimeout(typewriterTimeoutRef.current) } } }, []) // 打字机效果函数 - 简化版本用于测试 const typewriterEffect = (text, onComplete) => { console.log('Typewriter effect started with text:', text) let index = 0 setTypingMessage('') setIsTyping(true) const typeNextWords = () => { if (index < text.length) { // 简化:每次显示3个字符 const nextIndex = Math.min(index + 3, text.length) const currentText = text.substring(0, nextIndex) console.log('Typing progress:', currentText) setTypingMessage(currentText) index = nextIndex // 500ms间隔 typewriterTimeoutRef.current = setTimeout(typeNextWords, 500) } else { console.log('Typewriter effect completed') setIsTyping(false) onComplete(text) } } typeNextWords() } // 创建新对话 const createNewConversation = () => { conversationStore.createNewConversation() setInputValue('') } // 切换对话 const switchConversation = (conversationId) => { conversationStore.switchConversation(conversationId) setInputValue('') } // 删除对话 const deleteConversation = (conversationId) => { if (conversations.length <= 1) { message.warning('至少需要保留一个对话') return } conversationStore.deleteConversation(conversationId) } // 编辑对话标题 const editConversationTitle = (conversation) => { setEditingConversation(conversation) setNewTitle(conversation.title) setEditModalVisible(true) } // 保存对话标题 const saveConversationTitle = () => { if (!newTitle.trim()) { message.warning('标题不能为空') return } conversationStore.updateConversationTitle(editingConversation.id, newTitle.trim()) setEditModalVisible(false) setEditingConversation(null) setNewTitle('') } // 发送消息 const sendMessage = async () => { const messageText = inputValue.trim() if (!messageText || loading) return // 添加用户消息到独立存储 conversationStore.addMessage('user', messageText) setInputValue('') setLoading(true) try { // 调用本地后端接口(非流式版本) const result = await callLocalChatAPI(messageText) console.log('API result:', result) // 直接处理结果 const thought = result?.thought || '' const message = result?.message || '' console.log('Processed content:', { thought, message, deepThinkingEnabled }) // 根据深度思考按钮状态决定是否包含思考内容 const finalContent = deepThinkingEnabled ? thought + (message ? '\n\n' + message : '') : message console.log('Final content:', finalContent) if (finalContent.trim()) { // 暂时直接添加消息,不使用打字机效果 console.log('Adding message directly:', finalContent) conversationStore.addMessage('assistant', finalContent) } else { console.log('No content, adding fallback message') conversationStore.addMessage('assistant', '抱歉,我无法生成回复内容。') } } catch (error) { const errorMessage = handleAPIError(error) message.error(errorMessage) // 添加错误消息到独立存储 conversationStore.addMessage('assistant', `抱歉,我遇到了一个问题:${errorMessage}`) } finally { setLoading(false) } } // 处理回车键发送 const handleKeyPress = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() sendMessage() } } // 清空当前对话 const clearCurrentChat = () => { conversationStore.clearCurrentConversation() } // 兼容剪贴板 const copyToClipboard = async (text) => { if (!text) return Promise.reject(new Error("exmpty text")) // 现代 API,需 HTTPS 或 localhost(window.isSecureContext) if (navigator.clipboard && window.isSecureContext) { try { await navigator.clipboard.writeText(text) return } catch (e) { // 若失败,继续使用回退方案 console.warn('navigator.clipboard failed, fallback to execCommand', e) } } // 回退方案 return new Promise((resolve, reject) => { try { const ta = document.createElement('textarea') ta.value = text ta.setAttribute('readonly', '') ta.style.position = 'fixed' ta.style.left = '-9999px' document.body.appendChild(ta) ta.select() ta.setSelectionRange(0, ta.value.length) const ok = document.execCommand('copy') document.body.removeChild(ta) if (ok) resolve() else reject(new Error('execCommand failed')) } catch (err) { reject(err) } }) } // 渲染单条消息 ================================== const renderMessage = (msg, index) => { const isUser = msg.role === 'user' const displayContent = msg.content || '' // 调试信息 if (msg.role === 'assistant') { console.log('Rendering assistant message:', { index, content: msg.content, displayContent }) } return (
提出一个问题,比如:请帮我总结一段文本。