Compare commits

...

3 Commits

Author SHA1 Message Date
zdl
2d49af3bea feat: UI调整 2025-11-24 16:11:13 +08:00
zdl
3a0898634f feat: 将滚动事件移东到组件内部 2025-11-24 15:54:26 +08:00
zdl
44ecf7e5c7 feat: 去掉背景组件 2025-11-24 15:47:23 +08:00
6 changed files with 63 additions and 140 deletions

View File

@@ -30,12 +30,12 @@ const MemoizedAppFooter = memo(AppFooter);
*/
export default function MainLayout() {
return (
<Box minH="100vh" display="flex" flexDirection="column">
<Box flex="1" h="100vh" w="100%" position="relative" display="flex" flexDirection="column">
{/* 导航栏 - 在所有页面间共享memo 后不会在路由切换时重新渲染 */}
<MemoizedHomeNavbar />
{/* 页面内容区域 - flex: 1 占据剩余空间,包含错误边界、懒加载 */}
<Box flex="1" w="100%" position="relative" overflow="hidden" pt="72px">
<Box flex="1" pt="72px">
<ErrorBoundary>
<Suspense fallback={<PageLoader message="页面加载中..." />}>
<Outlet />
@@ -47,11 +47,11 @@ export default function MainLayout() {
<MemoizedAppFooter />
{/* 返回顶部按钮 - 滚动超过阈值时显示 */}
<BackToTopButton
{/* <BackToTopButton
scrollThreshold={BACK_TO_TOP_CONFIG.scrollThreshold}
position={BACK_TO_TOP_CONFIG.position}
zIndex={BACK_TO_TOP_CONFIG.zIndex}
/>
/> */}
</Box>
);
}

View File

@@ -1,59 +0,0 @@
// src/views/AgentChat/components/BackgroundEffects.js
// 背景渐变装饰层组件
import React from 'react';
import { Box } from '@chakra-ui/react';
/**
* BackgroundEffects - 背景渐变装饰层
*
* 包含主背景渐变和两个装饰光效(右上紫色、左下蓝色)
*
* @returns {JSX.Element}
*/
const BackgroundEffects = () => {
return (
<>
{/* 主背景渐变层 */}
<Box
position="absolute"
top={0}
left={0}
right={0}
bottom={0}
bgGradient="linear(to-br, gray.900, gray.800, purple.900)"
zIndex={0}
/>
{/* 右上角紫色光效 */}
<Box
position="absolute"
top="0"
right="-20%"
width="600px"
height="600px"
bgGradient="radial(circle, purple.600, transparent)"
opacity="0.15"
filter="blur(100px)"
pointerEvents="none"
zIndex={0}
/>
{/* 左下角蓝色光效 */}
<Box
position="absolute"
bottom="-30%"
left="-10%"
width="500px"
height="500px"
bgGradient="radial(circle, blue.600, transparent)"
opacity="0.1"
filter="blur(100px)"
pointerEvents="none"
zIndex={0}
/>
</>
);
};
export default BackgroundEffects;

View File

@@ -1,7 +1,7 @@
// src/views/AgentChat/components/ChatArea/index.js
// 中间聊天区域组件
import React from 'react';
import React, { useRef, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import {
Box,
@@ -56,7 +56,6 @@ import MessageRenderer from './MessageRenderer';
* @param {Function} props.onToggleRightSidebar - 切换右侧栏回调
* @param {Function} props.onNewSession - 新建会话回调
* @param {string} props.userAvatar - 用户头像 URL
* @param {RefObject} props.messagesEndRef - 消息列表底部引用
* @param {RefObject} props.inputRef - 输入框引用
* @param {RefObject} props.fileInputRef - 文件上传输入引用
* @returns {JSX.Element}
@@ -78,10 +77,15 @@ const ChatArea = ({
onToggleRightSidebar,
onNewSession,
userAvatar,
messagesEndRef,
inputRef,
fileInputRef,
}) => {
// Auto-scroll 功能:当消息列表更新时,自动滚动到底部
const messagesEndRef = useRef(null);
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
return (
<Flex flex={1} direction="column">
{/* 顶部标题栏 - 深色毛玻璃 */}
@@ -214,7 +218,6 @@ const ChatArea = ({
{/* 消息列表 */}
<Box
flex={1}
p={6}
bgGradient="linear(to-b, rgba(17, 24, 39, 0.5), rgba(17, 24, 39, 0.3))"
overflowY="auto"
>
@@ -253,7 +256,7 @@ const ChatArea = ({
animate="animate"
exit={{ opacity: 0, y: 20 }}
>
<Box px={6} py={3}>
<Box px={6}>
<Box maxW="896px" mx="auto">
<HStack fontSize="xs" color="gray.500" mb={2} fontWeight="medium" spacing={1}>
<Sparkles className="w-3 h-3" />
@@ -308,7 +311,7 @@ const ChatArea = ({
borderTop="1px solid"
borderColor="rgba(255, 255, 255, 0.1)"
px={6}
py={4}
py={1}
boxShadow="0 -8px 32px 0 rgba(31, 38, 135, 0.37)"
>
<Box maxW="896px" mx="auto">

View File

@@ -71,6 +71,7 @@ const LeftSidebar = ({
>
<Box
w="320px"
h="100%"
display="flex"
flexDirection="column"
bg="rgba(17, 24, 39, 0.8)"

View File

@@ -78,6 +78,7 @@ const RightSidebar = ({
>
<Box
w="320px"
h="100%"
display="flex"
flexDirection="column"
bg="rgba(17, 24, 39, 0.8)"

View File

@@ -2,8 +2,8 @@
// 超炫酷的 AI 投研助手 - HeroUI v3 现代深色主题版本
// 使用 Framer Motion 物理动画引擎 + 毛玻璃效果
import React, { useState, useEffect } from 'react';
import { Box, Flex, useToast, useColorMode } from '@chakra-ui/react';
import React, { useState } from 'react';
import { Box, Flex, useToast } from '@chakra-ui/react';
import { useAuth } from '@contexts/AuthContext';
// 常量配置 - 从 TypeScript 模块导入
@@ -11,13 +11,12 @@ import { DEFAULT_MODEL_ID } from './constants/models';
import { DEFAULT_SELECTED_TOOLS } from './constants/tools';
// 拆分后的子组件
import BackgroundEffects from './components/BackgroundEffects';
import LeftSidebar from './components/LeftSidebar';
import ChatArea from './components/ChatArea';
import RightSidebar from './components/RightSidebar';
// 自定义 Hooks
import { useAgentSessions, useAgentChat, useFileUpload, useAutoScroll } from './hooks';
import { useAgentSessions, useAgentChat, useFileUpload } from './hooks';
/**
* Agent Chat - 主组件HeroUI v3 深色主题)
@@ -35,7 +34,6 @@ import { useAgentSessions, useAgentChat, useFileUpload, useAutoScroll } from './
const AgentChat = () => {
const { user } = useAuth();
const toast = useToast();
const { setColorMode } = useColorMode();
// ==================== UI 状态(主组件管理)====================
const [selectedModel, setSelectedModel] = useState(DEFAULT_MODEL_ID);
@@ -83,79 +81,58 @@ const AgentChat = () => {
loadSessions,
});
// 自动滚动 Hook
const { messagesEndRef } = useAutoScroll(messages);
// ==================== 输入框引用(保留在主组件)====================
const inputRef = React.useRef(null);
// ==================== 启用深色模式 ====================
useEffect(() => {
// 为 AgentChat 页面强制启用深色模式
setColorMode('dark');
document.documentElement.classList.add('dark');
return () => {
// 组件卸载时不移除,让其他页面自己控制
// document.documentElement.classList.remove('dark');
};
}, [setColorMode]);
// ==================== 渲染组件 ====================
return (
<Box flex={1} bg="gray.900">
<Flex h="100%" overflow="hidden" position="relative">
{/* 背景渐变装饰 */}
<BackgroundEffects />
<Flex h="100%" position="relative" bg="gray.900">
{/* 左侧栏 */}
<LeftSidebar
isOpen={isLeftSidebarOpen}
onClose={() => setIsLeftSidebarOpen(false)}
sessions={sessions}
currentSessionId={currentSessionId}
onSessionSwitch={switchSession}
onNewSession={createNewSession}
isLoadingSessions={isLoadingSessions}
user={user}
/>
{/* 左侧栏 */}
<LeftSidebar
isOpen={isLeftSidebarOpen}
onClose={() => setIsLeftSidebarOpen(false)}
sessions={sessions}
currentSessionId={currentSessionId}
onSessionSwitch={switchSession}
onNewSession={createNewSession}
isLoadingSessions={isLoadingSessions}
user={user}
/>
{/* 中间聊天区 */}
<ChatArea
messages={messages}
inputValue={inputValue}
onInputChange={setInputValue}
isProcessing={isProcessing}
onSendMessage={handleSendMessage}
onKeyPress={handleKeyPress}
uploadedFiles={uploadedFiles}
onFileSelect={handleFileSelect}
onFileRemove={removeFile}
selectedModel={selectedModel}
isLeftSidebarOpen={isLeftSidebarOpen}
isRightSidebarOpen={isRightSidebarOpen}
onToggleLeftSidebar={() => setIsLeftSidebarOpen(true)}
onToggleRightSidebar={() => setIsRightSidebarOpen(true)}
onNewSession={createNewSession}
userAvatar={user?.avatar}
inputRef={inputRef}
fileInputRef={fileInputRef}
/>
{/* 中间聊天区 */}
<ChatArea
messages={messages}
inputValue={inputValue}
onInputChange={setInputValue}
isProcessing={isProcessing}
onSendMessage={handleSendMessage}
onKeyPress={handleKeyPress}
uploadedFiles={uploadedFiles}
onFileSelect={handleFileSelect}
onFileRemove={removeFile}
selectedModel={selectedModel}
isLeftSidebarOpen={isLeftSidebarOpen}
isRightSidebarOpen={isRightSidebarOpen}
onToggleLeftSidebar={() => setIsLeftSidebarOpen(true)}
onToggleRightSidebar={() => setIsRightSidebarOpen(true)}
onNewSession={createNewSession}
userAvatar={user?.avatar}
messagesEndRef={messagesEndRef}
inputRef={inputRef}
fileInputRef={fileInputRef}
/>
{/* 右侧栏 */}
<RightSidebar
isOpen={isRightSidebarOpen}
onClose={() => setIsRightSidebarOpen(false)}
selectedModel={selectedModel}
onModelChange={setSelectedModel}
selectedTools={selectedTools}
onToolsChange={setSelectedTools}
sessionsCount={sessions.length}
messagesCount={messages.length}
/>
</Flex>
</Box>
{/* 右侧栏 */}
<RightSidebar
isOpen={isRightSidebarOpen}
onClose={() => setIsRightSidebarOpen(false)}
selectedModel={selectedModel}
onModelChange={setSelectedModel}
selectedTools={selectedTools}
onToolsChange={setSelectedTools}
sessionsCount={sessions.length}
messagesCount={messages.length}
/>
</Flex>
);
};