update pay ui
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
// 消息渲染器组件
|
||||
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import {
|
||||
Card,
|
||||
CardBody,
|
||||
@@ -12,11 +12,13 @@ import {
|
||||
Tooltip,
|
||||
IconButton,
|
||||
HStack,
|
||||
VStack,
|
||||
Flex,
|
||||
Text,
|
||||
Box,
|
||||
Progress,
|
||||
} from '@chakra-ui/react';
|
||||
import { Cpu, User, Copy, ThumbsUp, ThumbsDown, File } from 'lucide-react';
|
||||
import { Cpu, User, Copy, ThumbsUp, ThumbsDown, File, ListChecks, Play, CheckCircle, XCircle, Clock } from 'lucide-react';
|
||||
import { MessageTypes } from '../../constants/messageTypes';
|
||||
import ExecutionStepsDisplay from './ExecutionStepsDisplay';
|
||||
import { MarkdownWithCharts } from '@components/ChatBot/MarkdownWithCharts';
|
||||
@@ -239,6 +241,272 @@ const MessageRenderer = ({ message, userAvatar }) => {
|
||||
</Flex>
|
||||
);
|
||||
|
||||
case MessageTypes.AGENT_PLAN:
|
||||
return (
|
||||
<Flex justify="flex-start" w="100%">
|
||||
<HStack align="start" spacing={3} maxW={{ base: '95%', md: '85%', lg: '80%' }} w="100%">
|
||||
<Avatar
|
||||
src="/images/agent/基金经理.png"
|
||||
icon={<Cpu className="w-4 h-4" />}
|
||||
size="sm"
|
||||
bgGradient="linear(to-br, purple.500, pink.500)"
|
||||
boxShadow="0 0 12px rgba(236, 72, 153, 0.4)"
|
||||
flexShrink={0}
|
||||
/>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
|
||||
style={{ flex: 1, minWidth: 0 }}
|
||||
>
|
||||
<Card
|
||||
bg="rgba(255, 255, 255, 0.05)"
|
||||
backdropFilter="blur(16px) saturate(180%)"
|
||||
border="1px solid"
|
||||
borderColor="rgba(139, 92, 246, 0.3)"
|
||||
boxShadow="0 8px 32px 0 rgba(139, 92, 246, 0.2)"
|
||||
w="100%"
|
||||
>
|
||||
<CardBody px={5} py={3}>
|
||||
<HStack spacing={2} mb={3}>
|
||||
<ListChecks className="w-4 h-4" color="#C084FC" />
|
||||
<Text fontSize="sm" fontWeight="medium" color="purple.300">
|
||||
执行计划
|
||||
</Text>
|
||||
{message.plan?.steps && (
|
||||
<Badge
|
||||
bgGradient="linear(to-r, purple.500, pink.500)"
|
||||
color="white"
|
||||
fontSize="xs"
|
||||
>
|
||||
{message.plan.steps.length} 个步骤
|
||||
</Badge>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
{message.plan?.goal && (
|
||||
<Text fontSize="sm" color="gray.300" mb={3}>
|
||||
🎯 {message.plan.goal}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{message.plan?.steps && (
|
||||
<VStack align="stretch" spacing={2}>
|
||||
{message.plan.steps.map((step, idx) => (
|
||||
<motion.div
|
||||
key={idx}
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: idx * 0.1 }}
|
||||
>
|
||||
<HStack
|
||||
p={2}
|
||||
bg="rgba(255, 255, 255, 0.03)"
|
||||
borderRadius="md"
|
||||
border="1px solid"
|
||||
borderColor="rgba(255, 255, 255, 0.1)"
|
||||
spacing={2}
|
||||
>
|
||||
<Badge
|
||||
bg="rgba(139, 92, 246, 0.2)"
|
||||
color="purple.300"
|
||||
fontSize="xs"
|
||||
minW="24px"
|
||||
textAlign="center"
|
||||
>
|
||||
{idx + 1}
|
||||
</Badge>
|
||||
<Text fontSize="xs" color="gray.400" flex={1}>
|
||||
{step.tool}
|
||||
</Text>
|
||||
</HStack>
|
||||
</motion.div>
|
||||
))}
|
||||
</VStack>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</motion.div>
|
||||
</HStack>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
case MessageTypes.AGENT_EXECUTING:
|
||||
const steps = message.plan?.steps || [];
|
||||
const completedSteps = message.stepResults || [];
|
||||
const currentStepIndex = completedSteps.length;
|
||||
const progress = steps.length > 0 ? (completedSteps.length / steps.length) * 100 : 0;
|
||||
|
||||
return (
|
||||
<Flex justify="flex-start" w="100%">
|
||||
<HStack align="start" spacing={3} maxW={{ base: '95%', md: '85%', lg: '80%' }} w="100%">
|
||||
<Avatar
|
||||
src="/images/agent/基金经理.png"
|
||||
icon={<Cpu className="w-4 h-4" />}
|
||||
size="sm"
|
||||
bgGradient="linear(to-br, purple.500, pink.500)"
|
||||
boxShadow="0 0 12px rgba(236, 72, 153, 0.4)"
|
||||
flexShrink={0}
|
||||
/>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
style={{ flex: 1, minWidth: 0 }}
|
||||
>
|
||||
<Card
|
||||
bg="rgba(255, 255, 255, 0.05)"
|
||||
backdropFilter="blur(16px) saturate(180%)"
|
||||
border="1px solid"
|
||||
borderColor="rgba(59, 130, 246, 0.3)"
|
||||
boxShadow="0 8px 32px 0 rgba(59, 130, 246, 0.2)"
|
||||
w="100%"
|
||||
>
|
||||
<CardBody px={5} py={3}>
|
||||
<HStack spacing={2} mb={3}>
|
||||
<motion.div
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ duration: 2, repeat: Infinity, ease: 'linear' }}
|
||||
>
|
||||
<Play className="w-4 h-4" color="#60A5FA" />
|
||||
</motion.div>
|
||||
<Text fontSize="sm" fontWeight="medium" color="blue.300">
|
||||
正在执行
|
||||
</Text>
|
||||
<Badge colorScheme="blue" fontSize="xs">
|
||||
{completedSteps.length} / {steps.length}
|
||||
</Badge>
|
||||
</HStack>
|
||||
|
||||
{/* 进度条 */}
|
||||
<Progress
|
||||
value={progress}
|
||||
size="xs"
|
||||
colorScheme="blue"
|
||||
bg="rgba(255, 255, 255, 0.1)"
|
||||
borderRadius="full"
|
||||
mb={3}
|
||||
hasStripe
|
||||
isAnimated
|
||||
/>
|
||||
|
||||
{/* 步骤列表 */}
|
||||
<VStack align="stretch" spacing={2}>
|
||||
<AnimatePresence>
|
||||
{steps.map((step, idx) => {
|
||||
const result = completedSteps[idx];
|
||||
const isCompleted = idx < completedSteps.length;
|
||||
const isRunning = idx === currentStepIndex;
|
||||
const isPending = idx > currentStepIndex;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={idx}
|
||||
initial={{ opacity: 0, y: -10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: idx * 0.05 }}
|
||||
>
|
||||
<HStack
|
||||
p={2}
|
||||
bg={
|
||||
isCompleted
|
||||
? result?.status === 'success'
|
||||
? 'rgba(16, 185, 129, 0.1)'
|
||||
: 'rgba(239, 68, 68, 0.1)'
|
||||
: isRunning
|
||||
? 'rgba(59, 130, 246, 0.15)'
|
||||
: 'rgba(255, 255, 255, 0.03)'
|
||||
}
|
||||
borderRadius="md"
|
||||
border="1px solid"
|
||||
borderColor={
|
||||
isCompleted
|
||||
? result?.status === 'success'
|
||||
? 'rgba(16, 185, 129, 0.3)'
|
||||
: 'rgba(239, 68, 68, 0.3)'
|
||||
: isRunning
|
||||
? 'rgba(59, 130, 246, 0.4)'
|
||||
: 'rgba(255, 255, 255, 0.1)'
|
||||
}
|
||||
spacing={2}
|
||||
transition="all 0.3s"
|
||||
>
|
||||
{/* 状态图标 */}
|
||||
<Box w="20px" h="20px" display="flex" alignItems="center" justifyContent="center">
|
||||
{isCompleted ? (
|
||||
result?.status === 'success' ? (
|
||||
<motion.div
|
||||
initial={{ scale: 0 }}
|
||||
animate={{ scale: 1 }}
|
||||
transition={{ type: 'spring', stiffness: 500 }}
|
||||
>
|
||||
<CheckCircle className="w-4 h-4" color="#10B981" />
|
||||
</motion.div>
|
||||
) : (
|
||||
<XCircle className="w-4 h-4" color="#EF4444" />
|
||||
)
|
||||
) : isRunning ? (
|
||||
<Spinner size="xs" color="blue.400" thickness="2px" />
|
||||
) : (
|
||||
<Clock className="w-3 h-3" color="#6B7280" />
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* 步骤序号 */}
|
||||
<Badge
|
||||
bg={
|
||||
isCompleted
|
||||
? result?.status === 'success'
|
||||
? 'rgba(16, 185, 129, 0.2)'
|
||||
: 'rgba(239, 68, 68, 0.2)'
|
||||
: isRunning
|
||||
? 'rgba(59, 130, 246, 0.2)'
|
||||
: 'rgba(107, 114, 128, 0.2)'
|
||||
}
|
||||
color={
|
||||
isCompleted
|
||||
? result?.status === 'success'
|
||||
? 'green.300'
|
||||
: 'red.300'
|
||||
: isRunning
|
||||
? 'blue.300'
|
||||
: 'gray.500'
|
||||
}
|
||||
fontSize="xs"
|
||||
minW="20px"
|
||||
textAlign="center"
|
||||
>
|
||||
{idx + 1}
|
||||
</Badge>
|
||||
|
||||
{/* 工具名称 */}
|
||||
<Text
|
||||
fontSize="xs"
|
||||
color={isPending ? 'gray.500' : 'gray.300'}
|
||||
flex={1}
|
||||
fontWeight={isRunning ? 'medium' : 'normal'}
|
||||
>
|
||||
{step.tool}
|
||||
</Text>
|
||||
|
||||
{/* 执行时间 */}
|
||||
{result?.execution_time && (
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
{result.execution_time.toFixed(2)}s
|
||||
</Text>
|
||||
)}
|
||||
</HStack>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</AnimatePresence>
|
||||
</VStack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</motion.div>
|
||||
</HStack>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
case MessageTypes.ERROR:
|
||||
return (
|
||||
<Flex justify="center">
|
||||
|
||||
@@ -70,8 +70,9 @@ export interface AgentMessage extends BaseMessage {
|
||||
plan?: any;
|
||||
/** 执行步骤结果 */
|
||||
stepResults?: Array<{
|
||||
tool_name: string;
|
||||
status: 'success' | 'error';
|
||||
tool: string;
|
||||
status: 'success' | 'error' | 'failed';
|
||||
result?: any;
|
||||
execution_time?: number;
|
||||
error?: string;
|
||||
}>;
|
||||
|
||||
@@ -404,9 +404,10 @@ export const useAgentChat = ({
|
||||
break;
|
||||
|
||||
case 'step_start':
|
||||
// 步骤开始执行
|
||||
// 步骤开始执行 - 保留已完成的步骤结果,更新当前执行状态
|
||||
updateLastMessage(MessageTypes.AGENT_EXECUTING, {
|
||||
content: `正在执行步骤 ${(data?.step_index || 0) + 1}: ${data?.tool || '工具'}...`,
|
||||
stepResults: [...streamStateRef.current.stepResults], // 保留已完成的步骤
|
||||
});
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user