Files
vf_react/src/components/ChatBot/PlanCard.js
2025-11-21 14:47:47 +08:00

146 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/components/ChatBot/PlanCard.js
// 执行计划展示卡片
import React from 'react';
import {
Box,
VStack,
HStack,
Text,
Badge,
Accordion,
AccordionItem,
AccordionButton,
AccordionPanel,
AccordionIcon,
Icon,
useColorModeValue,
Divider,
} from '@chakra-ui/react';
import { FiTarget, FiCheckCircle, FiXCircle, FiClock, FiTool } from 'react-icons/fi';
/**
* 执行计划卡片组件
*/
export const PlanCard = ({ plan, stepResults }) => {
const cardBg = useColorModeValue('blue.50', 'rgba(40, 45, 50, 0.8)');
const borderColor = useColorModeValue('blue.200', 'rgba(255, 215, 0, 0.3)');
const successColor = useColorModeValue('green.500', 'green.300');
const errorColor = useColorModeValue('red.500', 'red.300');
const pendingColor = useColorModeValue('gray.400', 'gray.500');
const getStepStatus = (stepIndex) => {
if (!stepResults || stepResults.length === 0) return 'pending';
const result = stepResults.find(r => r.step_index === stepIndex);
return result ? result.status : 'pending';
};
const getStepIcon = (status) => {
switch (status) {
case 'success':
return FiCheckCircle;
case 'failed':
return FiXCircle;
default:
return FiClock;
}
};
const getStepColor = (status) => {
switch (status) {
case 'success':
return successColor;
case 'failed':
return errorColor;
default:
return pendingColor;
}
};
return (
<Box
bg={cardBg}
borderRadius="lg"
borderWidth="2px"
borderColor={borderColor}
p={4}
mb={4}
boxShadow="md"
>
<VStack align="stretch" spacing={3}>
{/* 目标 */}
<HStack>
<Icon as={FiTarget} color="blue.500" boxSize={5} />
<Text fontWeight="bold" fontSize="md">执行目标</Text>
</HStack>
<Text fontSize="sm" color={useColorModeValue('gray.600', '#9BA1A6')} pl={7}>
{plan.goal}
</Text>
<Divider />
{/* 规划思路 */}
{plan.reasoning && (
<>
<Text fontSize="sm" fontWeight="bold">规划思路</Text>
<Text fontSize="sm" color={useColorModeValue('gray.600', '#9BA1A6')}>
{plan.reasoning}
</Text>
<Divider />
</>
)}
{/* 执行步骤 */}
<HStack justify="space-between">
<Text fontSize="sm" fontWeight="bold">执行步骤</Text>
<Badge colorScheme="blue">{plan.steps.length} </Badge>
</HStack>
<VStack align="stretch" spacing={2}>
{plan.steps.map((step, index) => {
const status = getStepStatus(index);
const StepIcon = getStepIcon(status);
const stepColor = getStepColor(status);
return (
<HStack
key={index}
p={2}
bg={useColorModeValue('white', 'rgba(50, 55, 60, 0.6)')}
borderRadius="md"
borderWidth="1px"
borderColor={stepColor}
align="flex-start"
>
<Icon as={StepIcon} color={stepColor} boxSize={4} mt={1} />
<VStack align="stretch" flex={1} spacing={1}>
<HStack justify="space-between">
<Text fontSize="sm" fontWeight="bold">
步骤 {index + 1}: {step.tool}
</Text>
<Badge
colorScheme={
status === 'success' ? 'green' :
status === 'failed' ? 'red' : 'gray'
}
fontSize="xs"
>
{status === 'success' ? '✓ 完成' :
status === 'failed' ? '✗ 失败' : '⏳ 等待'}
</Badge>
</HStack>
<Text fontSize="xs" color={useColorModeValue('gray.600', '#9BA1A6')}>
{step.reason}
</Text>
</VStack>
</HStack>
);
})}
</VStack>
</VStack>
</Box>
);
};
export default PlanCard;