import React, { useEffect, useState, useRef, useCallback, useLayoutEffect } from "react"; import { SideBar, Input, Button, List, Space, Popup, ActionSheet, Toast } from "antd-mobile"; import { UploadOutline, CloseOutline, CopyOutline, ReloadOutline, MoreOutline } from "antd-mobile-icons"; import styles from "./index.less"; // 引入外部样式 const AIAssistant = () => { const [messages, setMessages] = useState([ { id: 1, type: 'ai', content: '欢迎使用小羚羊AI印刷助手!我是专业的印刷行业智能顾问,可以帮助您解答印刷相关问题。', time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) } ]); const [inputValue, setInputValue] = useState(''); const [currentModel, setCurrentModel] = useState('process'); const [showSidebar, setShowSidebar] = useState(false); const [showActionSheet, setShowActionSheet] = useState(false); const [selectedMessageId, setSelectedMessageId] = useState(null); const messagesEndRef = useRef(null); // 预设问题列表 const presetQuestions = [ "如何选择合适的印刷材料?", "印刷色彩搭配建议", "印刷工艺选择指南", "印刷成本优化方案", "印刷质量控制要点" ]; // 模型选项 const modelOptions = [ { label: '小羚羊印刷行业大模型', value: 'process' }, { label: '通用模型 qwen2.5:14b', value: 'general' } ]; const scrollToBottom = useCallback(() => { if (messagesEndRef.current) { messagesEndRef.current.scrollIntoView({ behavior: 'auto', // 流式中用 instant 更流畅 block: 'end' }); } }, []); // 节流版本(每 100ms 最多一次) const throttledScroll = useRef(null); useLayoutEffect(() => { if (throttledScroll.current) clearTimeout(throttledScroll.current); throttledScroll.current = setTimeout(() => { scrollToBottom(); }, 50); // 流式中可设为 50ms,平衡流畅与性能 }, [messages, streamingMessage]); // 在组件内部添加新的状态 const [streamingMessage, setStreamingMessage] = useState(null); // { id, content, fullContent, time } // 发送 const sendMessage = async () => { if (!inputValue.trim()) return; const userMessage = { id: Date.now(), type: 'user', content: inputValue, time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) }; setMessages(prev => [...prev, userMessage]); setInputValue(''); // 模拟 AI 的完整回复内容 const fullResponse = `关于"${inputValue}"的问题,根据${currentModel === 'process' ? '印刷行业专业模型' : '通用模型'}的分析,这是相关的专业建议...`; const aiId = Date.now() + 1; const startTime = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); // 开始流式输出:逐字显示 let index = 0; const interval = setInterval(() => { setStreamingMessage({ id: aiId, type: 'ai', content: fullResponse.slice(0, index + 1), fullContent: fullResponse, time: startTime }); index++; if (index >= fullResponse.length) { clearInterval(interval); // 流式结束,合并到正式 messages setTimeout(() => { setMessages(prev => [ ...prev.filter(msg => msg.id !== aiId), // 防止重复(如果之前已插入占位) { id: aiId, type: 'ai', content: fullResponse, time: startTime } ]); setStreamingMessage(null); }, 100); } }, 30); // 每30ms输出一个字符(可调整速度) }; // 处理回车发送 const handleKeyDown = (e) => { if (e.key === 'Enter') { sendMessage(); } }; // 复制消息 const copyMessage = (content) => { navigator.clipboard.writeText(content).then(() => { Toast.show({ content: '已复制到剪贴板', duration: 1000 }); }); }; // 重新生成 const regenerateMessage = (content) => { setInputValue(content); setShowActionSheet(false); }; // 清空对话 const clearChat = () => { setMessages([]); Toast.show({ content: '对话已清空', duration: 1000 }); }; // 选择预设问题 const selectPresetQuestion = (question) => { setInputValue(question); setShowSidebar(false); }; return (