update pay ui
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
// 消息渲染器组件
|
// 消息渲染器组件
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardBody,
|
CardBody,
|
||||||
@@ -12,11 +12,13 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
IconButton,
|
IconButton,
|
||||||
HStack,
|
HStack,
|
||||||
|
VStack,
|
||||||
Flex,
|
Flex,
|
||||||
Text,
|
Text,
|
||||||
Box,
|
Box,
|
||||||
|
Progress,
|
||||||
} from '@chakra-ui/react';
|
} 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 { MessageTypes } from '../../constants/messageTypes';
|
||||||
import ExecutionStepsDisplay from './ExecutionStepsDisplay';
|
import ExecutionStepsDisplay from './ExecutionStepsDisplay';
|
||||||
import { MarkdownWithCharts } from '@components/ChatBot/MarkdownWithCharts';
|
import { MarkdownWithCharts } from '@components/ChatBot/MarkdownWithCharts';
|
||||||
@@ -239,6 +241,272 @@ const MessageRenderer = ({ message, userAvatar }) => {
|
|||||||
</Flex>
|
</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:
|
case MessageTypes.ERROR:
|
||||||
return (
|
return (
|
||||||
<Flex justify="center">
|
<Flex justify="center">
|
||||||
|
|||||||
@@ -70,8 +70,9 @@ export interface AgentMessage extends BaseMessage {
|
|||||||
plan?: any;
|
plan?: any;
|
||||||
/** 执行步骤结果 */
|
/** 执行步骤结果 */
|
||||||
stepResults?: Array<{
|
stepResults?: Array<{
|
||||||
tool_name: string;
|
tool: string;
|
||||||
status: 'success' | 'error';
|
status: 'success' | 'error' | 'failed';
|
||||||
|
result?: any;
|
||||||
execution_time?: number;
|
execution_time?: number;
|
||||||
error?: string;
|
error?: string;
|
||||||
}>;
|
}>;
|
||||||
|
|||||||
@@ -404,9 +404,10 @@ export const useAgentChat = ({
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'step_start':
|
case 'step_start':
|
||||||
// 步骤开始执行
|
// 步骤开始执行 - 保留已完成的步骤结果,更新当前执行状态
|
||||||
updateLastMessage(MessageTypes.AGENT_EXECUTING, {
|
updateLastMessage(MessageTypes.AGENT_EXECUTING, {
|
||||||
content: `正在执行步骤 ${(data?.step_index || 0) + 1}: ${data?.tool || '工具'}...`,
|
content: `正在执行步骤 ${(data?.step_index || 0) + 1}: ${data?.tool || '工具'}...`,
|
||||||
|
stepResults: [...streamStateRef.current.stepResults], // 保留已完成的步骤
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user