From b8186a2165de92d9f4b9fea459d5b7abcf06b012 Mon Sep 17 00:00:00 2001 From: yanyiwen Date: Wed, 14 May 2025 23:23:31 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat=E2=80=8C:=E6=96=B0=E5=A2=9E=E5=89=8D?= =?UTF-8?q?=E7=AB=AFAI=E5=8A=A9=E7=90=86UI=E7=95=8C=E9=9D=A2=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spug_web/src/layout/index.js | 145 +++++++++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 15 deletions(-) diff --git a/spug_web/src/layout/index.js b/spug_web/src/layout/index.js index e27e842..267a1af 100644 --- a/spug_web/src/layout/index.js +++ b/spug_web/src/layout/index.js @@ -5,54 +5,169 @@ */ import React, { useState, useEffect } from 'react'; import { Switch, Route } from 'react-router-dom'; -import { Layout, message } from 'antd'; +import { Layout, message, Drawer, Button, Input } from 'antd'; import { NotFound } from 'components'; import Sider from './Sider'; import Header from './Header'; -import Footer from './Footer' +import Footer from './Footer'; import routes from '../routes'; import { hasPermission, isMobile } from 'libs'; import styles from './layout.module.less'; +import { RobotOutlined } from '@ant-design/icons'; // AI 助理图标 function initRoutes(Routes, routes) { for (let route of routes) { if (route.component) { if (!route.auth || hasPermission(route.auth)) { - Routes.push() + Routes.push(); } } else if (route.child) { - initRoutes(Routes, route.child) + initRoutes(Routes, route.child); } } } export default function () { - const [collapsed, setCollapsed] = useState(false) + const [collapsed, setCollapsed] = useState(false); const [Routes, setRoutes] = useState([]); + const [drawerVisible, setDrawerVisible] = useState(false); // 控制抽屉显示 + const [messages, setMessages] = useState([]); // 存储对话消息 + const [loading, setLoading] = useState(false); // 控制加载状态 + const [context, setContext] = useState([]); // 新增上下文状态 useEffect(() => { - if (isMobile) { + if (isMobile) { setCollapsed(true); - message.warn('检测到您在移动设备上访问,请使用横屏模式。', 5) + message.warn('检测到您在移动设备上访问,请使用横屏模式。', 5); } const Routes = []; initRoutes(Routes, routes); - setRoutes(Routes) - }, []) + setRoutes(Routes); + }, []); + + const toggleDrawer = () => { + setDrawerVisible(!drawerVisible); + }; + + const handleSendMessage = async (value) => { + if (!value.trim()) return; + const userMessage = { role: 'user', content: value }; + setMessages((prev) => [...prev, userMessage]); // 添加用户消息 + setLoading(true); + + try { + const X_TOKEN = localStorage.getItem('token'); + const response = await fetch('/api/setting/ai_assistant/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Token': X_TOKEN, + }, + body: JSON.stringify({ question: value, context }), + }); + + if (!response.body) { + throw new Error('No response body'); + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder('utf-8'); + let done = false; + let assistantMessage = { role: 'assistant', content: '' }; + + while (!done) { + const { value, done: readerDone } = await reader.read(); + done = readerDone; + if (value) { + const chunk = decoder.decode(value, { stream: true }); + assistantMessage.content += chunk; + setMessages((prev) => { + const updatedMessages = [...prev]; + const lastMessage = updatedMessages[updatedMessages.length - 1]; + if (lastMessage?.role === 'assistant') { + lastMessage.content += chunk; + } else { + updatedMessages.push(assistantMessage); + } + return updatedMessages; + }); + } + } + + // 更新上下文 + setContext((prev) => [...prev, assistantMessage]); + } catch (error) { + console.error('Error fetching AI response:', error); + message.error('AI 助理接口调用失败,请稍后重试。'); + } finally { + setLoading(false); + } + }; return ( - - -
setCollapsed(!collapsed)}/> + + +
setCollapsed(!collapsed)} /> {Routes} - + + {/* AI 助理图标 */} +