import React, { useState, useEffect, useRef } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { AudioOutline } from "antd-mobile-icons"; import './AiChatStyles.less'; // 引入外部样式文件 const ChatInterface = () => { // ==================== 状态管理 ==================== const [sessionId, setSessionId] = useState(''); const [sUserId] = useState('user-001'); const [sUserType] = useState('sysadmin'); const [messages, setMessages] = useState([]); const [inputValue, setInputValue] = useState(''); const [isLoading, setIsLoading] = useState(false); const [currentModel, setCurrentModel] = useState('general'); const [chatHistory, setChatHistory] = useState([]); const [welcomeContent, setWelcomeContent] = useState(''); const messagesEndRef = useRef(null); const inputRef = useRef(null); // ==================== 配置 ==================== const CONFIG = { backendUrl: 'http://localhost:8099/xlyAi', endpoints: { chat: '/api/v1/chat/query', process: '/api/v1/chat/query', }, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, maxHistory: 20, }; // ==================== 工具函数 ==================== const getCurrentTime = () => { const now = new Date(); return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`; }; const generateRandomString = (length) => { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; }; const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; // ==================== 初始化 ==================== useEffect(() => { setMessages([ { id: 'welcome', type: 'ai', content: '', time: getCurrentTime(), isWelcome: true } ]); const initSession = async () => { try { const initUrl = `${CONFIG.backendUrl}/api/v1/chat/init?sUserId=${sUserId}&sUserType=${sUserType}`; const response = await fetch(initUrl, { method: 'POST', headers: CONFIG.headers, body: JSON.stringify({}) }); const data = await response.json(); if (data.data) { setWelcomeContent(data.data); setMessages(prev => prev.map(msg => msg.id === 'welcome' ? { ...msg, content: data.data } : msg )); } } catch (error) { console.error('初始化失败:', error); } }; initSession(); inputRef.current?.focus(); const handleKeyDown = (e) => { if (e.ctrlKey && e.key === 'Enter') { handleSendMessage(); } if (e.key === 'Escape') { setInputValue(''); } if (e.key === 'ArrowUp' && inputValue === '') { const lastUserMessage = chatHistory .filter(item => item.role === 'user') .pop(); if (lastUserMessage) { setInputValue(lastUserMessage.content); e.preventDefault(); } } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, []); useEffect(() => { scrollToBottom(); }, [messages, isLoading]); // ==================== 消息处理 ==================== const addMessage = (content, type, isError = false) => { const newMessage = { id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, type, content, time: getCurrentTime(), isError }; setMessages(prev => [...prev, newMessage]); return newMessage.id; }; const handleSendMessage = async () => { const message = inputValue.trim(); if (!message || isLoading) return; let currentSessionId = sessionId; if (!currentSessionId) { currentSessionId = generateRandomString(20); setSessionId(currentSessionId); } setInputValue(''); setIsLoading(true); addMessage(message, 'user'); const newHistoryItem = { role: 'user', content: message, timestamp: Date.now() }; setChatHistory(prev => { const updated = [...prev, newHistoryItem]; return updated.length > CONFIG.maxHistory ? updated.slice(-CONFIG.maxHistory) : updated; }); try { const endpoint = currentModel === 'process' ? CONFIG.endpoints.process : CONFIG.endpoints.chat; const requestData = { message, modelType: currentModel, sUserId, sUserType, sessionId: currentSessionId }; const response = await fetch(`${CONFIG.backendUrl}${endpoint}`, { method: 'POST', headers: CONFIG.headers, body: JSON.stringify(requestData) }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (data.data) { addMessage(data.data, 'ai'); setChatHistory(prev => { const updated = [...prev, { role: 'assistant', content: data.data, timestamp: Date.now() }]; return updated.length > CONFIG.maxHistory ? updated.slice(-CONFIG.maxHistory) : updated; }); } } catch (error) { console.error('请求失败:', error); const errorMessage = ` 抱歉,请求出现错误:${error.message} **可能的原因:** 1. Spring Boot 后端服务未启动 2. API 接口路径不正确 3. 网络连接问题 **检查步骤:** 1. 确保后端服务在端口 8099 运行 2. 检查浏览器控制台查看详细错误 3. 刷新页面重试 `; addMessage(errorMessage, 'ai', true); } finally { setIsLoading(false); inputRef.current?.focus(); } }; const handleClearChat = () => { if (window.confirm('确定要清空当前对话吗?')) { setMessages([ { id: 'cleared', type: 'ai', content: '对话已清空,请开始新的对话。', time: getCurrentTime(), } ]); setChatHistory([]); setSessionId(''); } }; const handleCopyMessage = (content) => { navigator.clipboard.writeText(content).then(() => { alert('已复制到剪贴板'); }); }; const handleRegenerateMessage = (messageId, content) => { setChatHistory(prev => prev.filter(item => !(item.role === 'assistant' && item.content === content) )); setMessages(prev => prev.filter(msg => msg.id !== messageId)); setInputValue(content); setTimeout(() => handleSendMessage(), 100); }; const handleModelChange = (e) => { setCurrentModel(e.target.value); }; // ==================== 渲染 ==================== return (
AI 印刷助手
{children}
) : (
{children}
)
)
}}
>
{msg.content || welcomeContent}