146 lines
4.1 KiB
JavaScript
146 lines
4.1 KiB
JavaScript
// 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;
|