agent功能开发增加MCP后端
This commit is contained in:
186
src/components/ChatBot/StepResultCard.js
Normal file
186
src/components/ChatBot/StepResultCard.js
Normal file
@@ -0,0 +1,186 @@
|
||||
// src/components/ChatBot/StepResultCard.js
|
||||
// 步骤结果展示卡片(可折叠)
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
VStack,
|
||||
HStack,
|
||||
Text,
|
||||
Badge,
|
||||
Collapse,
|
||||
Icon,
|
||||
IconButton,
|
||||
Code,
|
||||
useColorModeValue,
|
||||
Divider,
|
||||
} from '@chakra-ui/react';
|
||||
import { FiChevronDown, FiChevronUp, FiCheckCircle, FiXCircle, FiClock, FiDatabase } from 'react-icons/fi';
|
||||
|
||||
/**
|
||||
* 步骤结果卡片组件
|
||||
*/
|
||||
export const StepResultCard = ({ stepResult }) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const cardBg = useColorModeValue('white', 'gray.700');
|
||||
const borderColor = useColorModeValue('gray.200', 'gray.600');
|
||||
const successColor = useColorModeValue('green.500', 'green.300');
|
||||
const errorColor = useColorModeValue('red.500', 'red.300');
|
||||
|
||||
const getStatusIcon = () => {
|
||||
switch (stepResult.status) {
|
||||
case 'success':
|
||||
return FiCheckCircle;
|
||||
case 'failed':
|
||||
return FiXCircle;
|
||||
default:
|
||||
return FiClock;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusColor = () => {
|
||||
switch (stepResult.status) {
|
||||
case 'success':
|
||||
return 'green';
|
||||
case 'failed':
|
||||
return 'red';
|
||||
default:
|
||||
return 'gray';
|
||||
}
|
||||
};
|
||||
|
||||
const StatusIcon = getStatusIcon();
|
||||
const statusColorScheme = getStatusColor();
|
||||
|
||||
// 格式化数据以便展示
|
||||
const formatResult = (data) => {
|
||||
if (typeof data === 'string') return data;
|
||||
if (Array.isArray(data)) {
|
||||
return `找到 ${data.length} 条记录`;
|
||||
}
|
||||
if (typeof data === 'object') {
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
return String(data);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
bg={cardBg}
|
||||
borderRadius="md"
|
||||
borderWidth="1px"
|
||||
borderColor={borderColor}
|
||||
overflow="hidden"
|
||||
boxShadow="sm"
|
||||
>
|
||||
{/* 头部 - 始终可见 */}
|
||||
<HStack
|
||||
p={3}
|
||||
justify="space-between"
|
||||
cursor="pointer"
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
_hover={{ bg: useColorModeValue('gray.50', 'gray.600') }}
|
||||
>
|
||||
<HStack flex={1}>
|
||||
<Icon as={StatusIcon} color={`${statusColorScheme}.500`} boxSize={5} />
|
||||
<VStack align="stretch" spacing={0} flex={1}>
|
||||
<HStack>
|
||||
<Text fontSize="sm" fontWeight="bold">
|
||||
步骤 {stepResult.step_index + 1}: {stepResult.tool}
|
||||
</Text>
|
||||
<Badge colorScheme={statusColorScheme} fontSize="xs">
|
||||
{stepResult.status === 'success' ? '成功' :
|
||||
stepResult.status === 'failed' ? '失败' : '执行中'}
|
||||
</Badge>
|
||||
</HStack>
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
耗时: {stepResult.execution_time?.toFixed(2)}s
|
||||
</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
|
||||
<IconButton
|
||||
icon={<Icon as={isExpanded ? FiChevronUp : FiChevronDown} />}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
aria-label={isExpanded ? "收起" : "展开"}
|
||||
/>
|
||||
</HStack>
|
||||
|
||||
{/* 内容 - 可折叠 */}
|
||||
<Collapse in={isExpanded} animateOpacity>
|
||||
<Box p={3} pt={0}>
|
||||
<Divider mb={3} />
|
||||
|
||||
{/* 参数 */}
|
||||
{stepResult.arguments && Object.keys(stepResult.arguments).length > 0 && (
|
||||
<VStack align="stretch" spacing={2} mb={3}>
|
||||
<HStack>
|
||||
<Icon as={FiDatabase} color="blue.500" boxSize={4} />
|
||||
<Text fontSize="xs" fontWeight="bold">请求参数:</Text>
|
||||
</HStack>
|
||||
<Code
|
||||
p={2}
|
||||
borderRadius="md"
|
||||
fontSize="xs"
|
||||
whiteSpace="pre-wrap"
|
||||
wordBreak="break-word"
|
||||
>
|
||||
{JSON.stringify(stepResult.arguments, null, 2)}
|
||||
</Code>
|
||||
</VStack>
|
||||
)}
|
||||
|
||||
{/* 结果或错误 */}
|
||||
{stepResult.status === 'success' && stepResult.result && (
|
||||
<VStack align="stretch" spacing={2}>
|
||||
<Text fontSize="xs" fontWeight="bold">执行结果:</Text>
|
||||
<Box
|
||||
maxH="300px"
|
||||
overflowY="auto"
|
||||
p={2}
|
||||
bg={useColorModeValue('gray.50', 'gray.800')}
|
||||
borderRadius="md"
|
||||
fontSize="xs"
|
||||
>
|
||||
{typeof stepResult.result === 'string' ? (
|
||||
<Text whiteSpace="pre-wrap">{stepResult.result}</Text>
|
||||
) : Array.isArray(stepResult.result) ? (
|
||||
<VStack align="stretch" spacing={2}>
|
||||
<Text fontWeight="bold">找到 {stepResult.result.length} 条记录:</Text>
|
||||
{stepResult.result.slice(0, 3).map((item, idx) => (
|
||||
<Code key={idx} p={2} borderRadius="md" fontSize="xs">
|
||||
{JSON.stringify(item, null, 2)}
|
||||
</Code>
|
||||
))}
|
||||
{stepResult.result.length > 3 && (
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
...还有 {stepResult.result.length - 3} 条记录
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
) : (
|
||||
<Code whiteSpace="pre-wrap" wordBreak="break-word">
|
||||
{JSON.stringify(stepResult.result, null, 2)}
|
||||
</Code>
|
||||
)}
|
||||
</Box>
|
||||
</VStack>
|
||||
)}
|
||||
|
||||
{stepResult.status === 'failed' && stepResult.error && (
|
||||
<VStack align="stretch" spacing={2}>
|
||||
<Text fontSize="xs" fontWeight="bold" color="red.500">错误信息:</Text>
|
||||
<Text fontSize="xs" color="red.600" p={2} bg="red.50" borderRadius="md">
|
||||
{stepResult.error}
|
||||
</Text>
|
||||
</VStack>
|
||||
)}
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default StepResultCard;
|
||||
Reference in New Issue
Block a user