update pay function
This commit is contained in:
310
src/views/AgentChat/components/ChatArea/WelcomeScreen.js
Normal file
310
src/views/AgentChat/components/ChatArea/WelcomeScreen.js
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
// src/views/AgentChat/components/ChatArea/WelcomeScreen.js
|
||||||
|
// 欢迎界面组件 - 类似 Gemini/ChatGPT 风格
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
VStack,
|
||||||
|
HStack,
|
||||||
|
Text,
|
||||||
|
SimpleGrid,
|
||||||
|
Icon,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import {
|
||||||
|
Cpu,
|
||||||
|
TrendingUp,
|
||||||
|
BarChart3,
|
||||||
|
Newspaper,
|
||||||
|
Target,
|
||||||
|
Lightbulb,
|
||||||
|
Search,
|
||||||
|
PieChart,
|
||||||
|
Sparkles,
|
||||||
|
} from 'lucide-react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建议任务卡片数据
|
||||||
|
*/
|
||||||
|
const SUGGESTION_CARDS = [
|
||||||
|
{
|
||||||
|
icon: TrendingUp,
|
||||||
|
title: '今日涨停分析',
|
||||||
|
description: '分析今天涨停板的概念分布和热点板块',
|
||||||
|
prompt: '分析一下今天的涨停板,有哪些热点概念?',
|
||||||
|
gradient: 'linear(to-br, orange.400, red.500)',
|
||||||
|
shadowColor: 'rgba(251, 146, 60, 0.3)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Search,
|
||||||
|
title: '个股深度研究',
|
||||||
|
description: '全面分析某只股票的基本面和技术面',
|
||||||
|
prompt: '帮我分析一下贵州茅台的投资价值',
|
||||||
|
gradient: 'linear(to-br, blue.400, cyan.500)',
|
||||||
|
shadowColor: 'rgba(59, 130, 246, 0.3)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: BarChart3,
|
||||||
|
title: '板块轮动追踪',
|
||||||
|
description: '追踪近期市场板块轮动和资金流向',
|
||||||
|
prompt: '最近有哪些板块在轮动?资金流向如何?',
|
||||||
|
gradient: 'linear(to-br, purple.400, pink.500)',
|
||||||
|
shadowColor: 'rgba(168, 85, 247, 0.3)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Newspaper,
|
||||||
|
title: '财经热点解读',
|
||||||
|
description: '解读最新的财经新闻和政策影响',
|
||||||
|
prompt: '今天有什么重要的财经新闻?对市场有什么影响?',
|
||||||
|
gradient: 'linear(to-br, green.400, teal.500)',
|
||||||
|
shadowColor: 'rgba(34, 197, 94, 0.3)',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 能力标签
|
||||||
|
*/
|
||||||
|
const CAPABILITIES = [
|
||||||
|
{ icon: Target, text: '精准股票分析' },
|
||||||
|
{ icon: Lightbulb, text: '智能投资建议' },
|
||||||
|
{ icon: PieChart, text: '数据可视化' },
|
||||||
|
{ icon: Sparkles, text: '实时热点追踪' },
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建议卡片组件
|
||||||
|
*/
|
||||||
|
const SuggestionCard = ({ card, onClick, index }) => {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.1 + index * 0.1, duration: 0.4 }}
|
||||||
|
whileHover={{ scale: 1.02, y: -4 }}
|
||||||
|
whileTap={{ scale: 0.98 }}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
as="button"
|
||||||
|
w="full"
|
||||||
|
p={4}
|
||||||
|
bg="rgba(255, 255, 255, 0.03)"
|
||||||
|
backdropFilter="blur(12px)"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="rgba(255, 255, 255, 0.08)"
|
||||||
|
borderRadius="xl"
|
||||||
|
textAlign="left"
|
||||||
|
cursor="pointer"
|
||||||
|
transition="all 0.3s"
|
||||||
|
onClick={() => onClick(card.prompt)}
|
||||||
|
_hover={{
|
||||||
|
bg: 'rgba(255, 255, 255, 0.06)',
|
||||||
|
borderColor: 'rgba(255, 255, 255, 0.15)',
|
||||||
|
boxShadow: `0 8px 32px ${card.shadowColor}`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<HStack spacing={3} mb={2}>
|
||||||
|
<Box
|
||||||
|
p={2}
|
||||||
|
borderRadius="lg"
|
||||||
|
bgGradient={card.gradient}
|
||||||
|
boxShadow={`0 4px 12px ${card.shadowColor}`}
|
||||||
|
>
|
||||||
|
<Icon as={card.icon} w={4} h={4} color="white" />
|
||||||
|
</Box>
|
||||||
|
<Text fontWeight="semibold" color="gray.200" fontSize="sm">
|
||||||
|
{card.title}
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
<Text fontSize="xs" color="gray.500" lineHeight="tall">
|
||||||
|
{card.description}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WelcomeScreen - 欢迎界面组件
|
||||||
|
*
|
||||||
|
* @param {Object} props
|
||||||
|
* @param {Function} props.onSuggestionClick - 点击建议时的回调
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
const WelcomeScreen = ({ onSuggestionClick }) => {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
flex={1}
|
||||||
|
display="flex"
|
||||||
|
flexDirection="column"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
px={6}
|
||||||
|
py={8}
|
||||||
|
>
|
||||||
|
<VStack spacing={8} maxW="700px" w="full">
|
||||||
|
{/* Logo 和标题 */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, scale: 0.9 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
>
|
||||||
|
<VStack spacing={4}>
|
||||||
|
{/* 动态 Logo */}
|
||||||
|
<Box position="relative">
|
||||||
|
{/* 外层光晕 */}
|
||||||
|
<motion.div
|
||||||
|
animate={{
|
||||||
|
boxShadow: [
|
||||||
|
'0 0 30px rgba(139, 92, 246, 0.3)',
|
||||||
|
'0 0 60px rgba(139, 92, 246, 0.5)',
|
||||||
|
'0 0 30px rgba(139, 92, 246, 0.3)',
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
transition={{ duration: 2, repeat: Infinity }}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
inset: -8,
|
||||||
|
borderRadius: '50%',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* 旋转边框 */}
|
||||||
|
<motion.div
|
||||||
|
animate={{ rotate: 360 }}
|
||||||
|
transition={{ duration: 8, repeat: Infinity, ease: 'linear' }}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
inset: -4,
|
||||||
|
borderRadius: '50%',
|
||||||
|
background: 'linear-gradient(45deg, transparent 40%, rgba(139, 92, 246, 0.5) 50%, transparent 60%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Box
|
||||||
|
w={20}
|
||||||
|
h={20}
|
||||||
|
borderRadius="full"
|
||||||
|
bgGradient="linear(to-br, purple.500, pink.500, blue.500)"
|
||||||
|
display="flex"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
position="relative"
|
||||||
|
boxShadow="0 8px 32px rgba(139, 92, 246, 0.4)"
|
||||||
|
>
|
||||||
|
<Cpu className="w-10 h-10" color="white" />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 标题 */}
|
||||||
|
<VStack spacing={1}>
|
||||||
|
<Text
|
||||||
|
fontSize="3xl"
|
||||||
|
fontWeight="bold"
|
||||||
|
bgGradient="linear(to-r, blue.300, purple.400, pink.400)"
|
||||||
|
bgClip="text"
|
||||||
|
letterSpacing="tight"
|
||||||
|
>
|
||||||
|
你好,我是价小前
|
||||||
|
</Text>
|
||||||
|
<Text fontSize="md" color="gray.400" textAlign="center">
|
||||||
|
你的 AI 投研助手
|
||||||
|
</Text>
|
||||||
|
</VStack>
|
||||||
|
</VStack>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* 简介 */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.2, duration: 0.4 }}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
p={4}
|
||||||
|
bg="rgba(255, 255, 255, 0.02)"
|
||||||
|
borderRadius="xl"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="rgba(255, 255, 255, 0.05)"
|
||||||
|
>
|
||||||
|
<Text fontSize="sm" color="gray.400" textAlign="center" lineHeight="tall">
|
||||||
|
基于最先进的大语言模型,结合专业微调的金融理解能力,
|
||||||
|
<br />
|
||||||
|
整合实时投研数据与专业分析工具,为你提供智能投资研究服务。
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* 能力标签 */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ delay: 0.3, duration: 0.4 }}
|
||||||
|
>
|
||||||
|
<HStack spacing={4} flexWrap="wrap" justify="center">
|
||||||
|
{CAPABILITIES.map((cap, idx) => (
|
||||||
|
<motion.div
|
||||||
|
key={idx}
|
||||||
|
initial={{ opacity: 0, scale: 0.8 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
transition={{ delay: 0.3 + idx * 0.05 }}
|
||||||
|
>
|
||||||
|
<HStack
|
||||||
|
spacing={2}
|
||||||
|
px={3}
|
||||||
|
py={1.5}
|
||||||
|
bg="rgba(139, 92, 246, 0.1)"
|
||||||
|
borderRadius="full"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="rgba(139, 92, 246, 0.2)"
|
||||||
|
>
|
||||||
|
<Icon as={cap.icon} w={3.5} h={3.5} color="purple.400" />
|
||||||
|
<Text fontSize="xs" color="purple.300" fontWeight="medium">
|
||||||
|
{cap.text}
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</HStack>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* 建议任务卡片 */}
|
||||||
|
<Box w="full">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ delay: 0.4 }}
|
||||||
|
>
|
||||||
|
<HStack spacing={2} mb={4} justify="center">
|
||||||
|
<Sparkles className="w-4 h-4" color="#a78bfa" />
|
||||||
|
<Text fontSize="sm" color="gray.400" fontWeight="medium">
|
||||||
|
试试这些问题
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={3}>
|
||||||
|
{SUGGESTION_CARDS.map((card, idx) => (
|
||||||
|
<SuggestionCard
|
||||||
|
key={idx}
|
||||||
|
card={card}
|
||||||
|
index={idx}
|
||||||
|
onClick={onSuggestionClick}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 底部提示 */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ delay: 0.6 }}
|
||||||
|
>
|
||||||
|
<Text fontSize="xs" color="gray.600" textAlign="center">
|
||||||
|
输入你的问题,或点击上方卡片快速开始
|
||||||
|
</Text>
|
||||||
|
</motion.div>
|
||||||
|
</VStack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WelcomeScreen;
|
||||||
@@ -5,7 +5,6 @@ import React, { useRef, useEffect } from 'react';
|
|||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
|
||||||
Input,
|
Input,
|
||||||
Avatar,
|
Avatar,
|
||||||
Badge,
|
Badge,
|
||||||
@@ -27,14 +26,13 @@ import {
|
|||||||
Settings,
|
Settings,
|
||||||
Cpu,
|
Cpu,
|
||||||
Zap,
|
Zap,
|
||||||
Sparkles,
|
|
||||||
Paperclip,
|
Paperclip,
|
||||||
Image as ImageIcon,
|
Image as ImageIcon,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { AVAILABLE_MODELS } from '../../constants/models';
|
import { AVAILABLE_MODELS } from '../../constants/models';
|
||||||
import { quickQuestions } from '../../constants/quickQuestions';
|
|
||||||
import { animations } from '../../constants/animations';
|
import { animations } from '../../constants/animations';
|
||||||
import MessageRenderer from './MessageRenderer';
|
import MessageRenderer from './MessageRenderer';
|
||||||
|
import WelcomeScreen from './WelcomeScreen';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ChatArea - 中间聊天区域组件
|
* ChatArea - 中间聊天区域组件
|
||||||
@@ -215,94 +213,49 @@ const ChatArea = ({
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* 消息列表 */}
|
{/* 消息列表 / 欢迎界面 */}
|
||||||
<Box
|
<Box
|
||||||
flex={1}
|
flex={1}
|
||||||
bgGradient="linear(to-b, rgba(17, 24, 39, 0.5), rgba(17, 24, 39, 0.3))"
|
bgGradient="linear(to-b, rgba(17, 24, 39, 0.5), rgba(17, 24, 39, 0.3))"
|
||||||
overflowY="auto"
|
overflowY="auto"
|
||||||
|
display="flex"
|
||||||
|
flexDirection="column"
|
||||||
>
|
>
|
||||||
<motion.div
|
{/* 判断是否显示欢迎界面:只有初始欢迎消息(1条)或没有消息时显示 */}
|
||||||
style={{ maxWidth: '896px', margin: '0 auto' }}
|
{messages.length <= 1 && !isProcessing ? (
|
||||||
variants={animations.staggerContainer}
|
<WelcomeScreen
|
||||||
initial="initial"
|
onSuggestionClick={(prompt) => {
|
||||||
animate="animate"
|
onInputChange(prompt);
|
||||||
>
|
inputRef.current?.focus();
|
||||||
<VStack spacing={4} align="stretch">
|
}}
|
||||||
<AnimatePresence mode="popLayout">
|
/>
|
||||||
{messages.map((message) => (
|
) : (
|
||||||
<motion.div
|
|
||||||
key={message.id}
|
|
||||||
variants={animations.fadeInUp}
|
|
||||||
initial="initial"
|
|
||||||
animate="animate"
|
|
||||||
exit={{ opacity: 0, y: -20 }}
|
|
||||||
layout
|
|
||||||
>
|
|
||||||
<MessageRenderer message={message} userAvatar={userAvatar} />
|
|
||||||
</motion.div>
|
|
||||||
))}
|
|
||||||
</AnimatePresence>
|
|
||||||
<div ref={messagesEndRef} />
|
|
||||||
</VStack>
|
|
||||||
</motion.div>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* 快捷问题 */}
|
|
||||||
<AnimatePresence>
|
|
||||||
{messages.length <= 2 && !isProcessing && (
|
|
||||||
<motion.div
|
<motion.div
|
||||||
variants={animations.fadeInUp}
|
style={{ maxWidth: '896px', margin: '0 auto', width: '100%', padding: '16px' }}
|
||||||
|
variants={animations.staggerContainer}
|
||||||
initial="initial"
|
initial="initial"
|
||||||
animate="animate"
|
animate="animate"
|
||||||
exit={{ opacity: 0, y: 20 }}
|
|
||||||
>
|
>
|
||||||
<Box px={6}>
|
<VStack spacing={4} align="stretch">
|
||||||
<Box maxW="896px" mx="auto">
|
<AnimatePresence mode="popLayout">
|
||||||
<HStack fontSize="xs" color="gray.500" mb={2} fontWeight="medium" spacing={1}>
|
{messages.map((message) => (
|
||||||
<Sparkles className="w-3 h-3" />
|
<motion.div
|
||||||
<Text>快速开始</Text>
|
key={message.id}
|
||||||
</HStack>
|
variants={animations.fadeInUp}
|
||||||
<Box display="grid" gridTemplateColumns="repeat(2, 1fr)" gap={2}>
|
initial="initial"
|
||||||
{quickQuestions.map((question, idx) => (
|
animate="animate"
|
||||||
<motion.div
|
exit={{ opacity: 0, y: -20 }}
|
||||||
key={idx}
|
layout
|
||||||
whileHover={{ scale: 1.02, y: -2 }}
|
>
|
||||||
whileTap={{ scale: 0.98 }}
|
<MessageRenderer message={message} userAvatar={userAvatar} />
|
||||||
transition={{ type: 'spring', stiffness: 400, damping: 17 }}
|
</motion.div>
|
||||||
>
|
))}
|
||||||
<Button
|
</AnimatePresence>
|
||||||
variant="outline"
|
<div ref={messagesEndRef} />
|
||||||
w="full"
|
</VStack>
|
||||||
justifyContent="flex-start"
|
|
||||||
h="auto"
|
|
||||||
py={3}
|
|
||||||
bg="rgba(255, 255, 255, 0.05)"
|
|
||||||
backdropFilter="blur(12px)"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="rgba(255, 255, 255, 0.1)"
|
|
||||||
color="gray.300"
|
|
||||||
_hover={{
|
|
||||||
bg: 'rgba(59, 130, 246, 0.15)',
|
|
||||||
borderColor: 'blue.400',
|
|
||||||
boxShadow: '0 4px 12px rgba(59, 130, 246, 0.3)',
|
|
||||||
color: 'white',
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
onInputChange(question.text);
|
|
||||||
inputRef.current?.focus();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text mr={2}>{question.emoji}</Text>
|
|
||||||
<Text>{question.text}</Text>
|
|
||||||
</Button>
|
|
||||||
</motion.div>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</Box>
|
||||||
|
|
||||||
{/* 输入栏 - 深色毛玻璃 */}
|
{/* 输入栏 - 深色毛玻璃 */}
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
Reference in New Issue
Block a user