import {useEffect, useState, useRef} from "react"; import {Textarea} from '@tarojs/components'; import Taro from '@tarojs/taro'; import {Button} from '@nutui/nutui-react-taro'; import {User, Home} from '@nutui/icons-react-taro'; import {sendAiMessage} from '@/api/ai'; import {createWebSocket} from '@/utils/websocket'; import {getAiToken} from '@/utils/aiToken'; import MarkdownRenderer from '@/components/MarkdownRenderer'; // 显示html富文本 import {View, RichText} from '@tarojs/components' import './index.scss'; import {WSS_API_URL} from "@/utils/server"; // 消息类型 interface Message { id?: string; type?: 'user' | 'ai'; query?: string; timestamp?: number; isTyping?: boolean; user?: string; responseMode?: string; } /** * AI问答页面 */ const AiChat = () => { const [messages, setMessages] = useState([]); const [inputValue, setInputValue] = useState(''); const [isLoading, setIsLoading] = useState(false); // const [currentTaskId, setCurrentTaskId] = useState(''); const [wsConnected, setWsConnected] = useState(false); const [isInitialized, setIsInitialized] = useState(false); const messagesEndRef = useRef(null); const wsRef = useRef(null); // 快捷问题 const quickQuestions = [ "当兵的注意事项?", "男兵应征报名对象:", "当兵公务员考试?", "请问您还有什么问题吗?" ]; // 检查并获取AI Token const checkAiToken = () => { return getAiToken(); }; // 调试函数:检查输入框状态 const debugInputStatus = () => { console.log('输入框状态调试:', { isInitialized, isLoading, wsConnected, inputValue, inputDisabled: !isInitialized || isLoading, sendButtonDisabled: !isInitialized || !inputValue.trim() }); }; // 滚动到底部 const scrollToBottom = () => { // 检查并确保AI_TOKEN存在 checkAiToken(); setTimeout(() => { messagesEndRef.current?.scrollIntoView({behavior: 'smooth'}); }, 100); }; // 初始化WebSocket连接 const initWebSocket = () => { const userToken = Taro.getStorageSync('AI_TOKEN') || 'anonymous'; console.log(WSS_API_URL + "/chat/" + userToken, 'wsUrl'); wsRef.current = createWebSocket(WSS_API_URL + "/chat/" + userToken); wsRef.current.onMessage((data: any) => { console.log('收到WebSocket消息:', data); if (data.answer) { if (data.answer === '__END__') { // 消息结束,移除typing状态 setMessages(prev => prev.map(msg => msg.isTyping ? {...msg, isTyping: false} : msg )); setIsLoading(false); // setCurrentTaskId(''); } else { // 实时更新AI回复消息 setMessages(prev => { const newMessages = [...prev]; const lastMessage = newMessages[newMessages.length - 1]; if (lastMessage && lastMessage.type === 'ai' && lastMessage.isTyping) { // 直接追加内容到现有AI消息 lastMessage.query = (lastMessage.query || '') + data.answer; } else { // 创建新的AI消息 newMessages.push({ id: Date.now().toString(), type: 'ai', query: data.answer, timestamp: Date.now(), isTyping: true }); } return newMessages; }); if (data.taskId) { // setCurrentTaskId(data.taskId); } } // 实时滚动到底部 setTimeout(() => { messagesEndRef.current?.scrollIntoView({behavior: 'smooth'}); }, 50); } }); wsRef.current.onOpen(() => { console.log('WebSocket连接成功'); setWsConnected(true); }); wsRef.current.onError((error: any) => { console.error('WebSocket连接错误:', error); setWsConnected(false); // Taro.showToast({ // title: 'WebSocket连接失败', // icon: 'none', // duration: 2000 // }); }); wsRef.current.onClose(() => { console.log('WebSocket连接关闭'); setWsConnected(false); // 如果正在加载中,显示连接断开提示 if (isLoading) { Taro.showToast({ title: '连接已断开,正在重连...', icon: 'none', duration: 2000 }); } }); wsRef.current.connect().catch((error: any) => { console.error('WebSocket连接失败:', error); // Taro.showToast({ // title: 'WebSocket连接失败', // icon: 'none', // duration: 2000 // }); }); }; useEffect(() => { // 初始化时检查并生成AI Token const token = checkAiToken(); console.log('AI Token初始化完成:', token); // 检查userToken const userToken = Taro.getStorageSync('AI_TOKEN'); console.log('当前UserToken:', userToken || 'anonymous'); // 初始化WebSocket initWebSocket(); Taro.hideTabBar(); // 标记初始化完成 setIsInitialized(true); // 延迟调试,确保状态更新完成 setTimeout(() => { debugInputStatus(); }, 100); return () => { if (wsRef.current) { wsRef.current.close(); } }; }, []); useEffect(() => { scrollToBottom(); }, [messages]); // 发送消息 const handleSendMessage = async (content: string) => { if (!content.trim() || isLoading) return; // 检查并确保AI Token存在 const aiToken = checkAiToken(); if (!aiToken) { Taro.showToast({ title: '初始化失败,请重试', icon: 'none', duration: 2000 }); return; } // 检查WebSocket连接状态 if (!wsConnected) { Taro.showToast({ title: '连接已断开,请稍后重试', icon: 'none', duration: 2000 }); return; } const userMessage: Message = { id: Date.now().toString(), type: 'user', query: content.trim(), timestamp: Date.now(), user: `${Taro.getStorageSync('AI_TOKEN') || 'anonymous'}` }; // 立即添加用户消息 setMessages(prev => [...prev, userMessage]); setInputValue(''); setIsLoading(true); // 立即添加一个空的AI消息占位符,准备接收流式回复 const aiPlaceholder: Message = { id: (Date.now() + 1).toString(), type: 'ai', query: '', timestamp: Date.now(), isTyping: true }; setMessages(prev => [...prev, aiPlaceholder]); try { await sendAiMessage({ query: content.trim(), user: `${Taro.getStorageSync('AI_TOKEN') || 'anonymous'}`, responseMode: 'streaming', aiToken: aiToken, // 包含AI Token }); } catch (error) { console.error('发送消息失败:', error); setIsLoading(false); // 移除AI占位符消息 setMessages(prev => prev.filter(msg => msg.id !== aiPlaceholder.id)); // 添加错误消息 const errorMessage: Message = { id: (Date.now() + 2).toString(), type: 'ai', query: '抱歉,发送消息时出现错误,请检查网络连接后重试。', timestamp: Date.now(), isTyping: false }; setMessages(prev => [...prev, errorMessage]); Taro.showToast({ title: '发送失败,请重试', icon: 'none', duration: 2000 }); } }; // 停止AI回复 // const handleStopMessage = async () => { // if (currentTaskId) { // try { // await stopAiMessage({taskId: currentTaskId}); // setIsLoading(false); // setCurrentTaskId(''); // setMessages(prev => prev.map(msg => // msg.isTyping ? {...msg, isTyping: false} : msg // )); // } catch (error) { // console.error('停止消息失败:', error); // } // } // }; // 处理快捷问题点击 const handleQuickQuestion = (question: string) => { if (!isInitialized) { Taro.showToast({ title: '正在初始化,请稍候...', icon: 'none', duration: 2000 }); return; } handleSendMessage(question); }; // 手动重连WebSocket const handleReconnect = () => { if (wsRef.current) { wsRef.current.close(); } initWebSocket(); }; return ( {!wsConnected && ( 连接已断开,正在重连... )} {messages.length === 0 ? ( 🤖 您好!我是AI助手 我可以为您解答各种问题,请输入您想了解的内容 ) : ( messages.map((message) => ( {message.type === 'user' ? : '🤖'} {message.isTyping && message.type === 'ai' && message.query && ( | )} )) )} {messages.length === 0 && ( 💡 您可以问我: {quickQuestions.map((question, index) => ( handleQuickQuestion(question)} > ))} )}
Taro.reLaunch({url: '/pages/index/index'})}/>