feat: deviceSlice添加

This commit is contained in:
zdl
2025-11-25 16:57:48 +08:00
parent 1bc3241596
commit bb878c5346
3 changed files with 456 additions and 0 deletions

View File

@@ -0,0 +1,203 @@
// src/views/AgentChat/components/RightSidebar/ToolSelector.tsx
// 工具选择组件
import React from 'react';
import { motion } from 'framer-motion';
import {
Button,
Badge,
Checkbox,
CheckboxGroup,
Accordion,
AccordionItem,
AccordionButton,
AccordionPanel,
AccordionIcon,
HStack,
VStack,
Box,
Text,
} from '@chakra-ui/react';
import { MCP_TOOLS, TOOL_CATEGORIES } from '../../constants/tools';
/**
* ToolSelector 组件的 Props 类型
*/
interface ToolSelectorProps {
/** 已选工具 ID 列表 */
selectedTools: string[];
/** 工具选择变化回调 */
onToolsChange: (tools: string[]) => void;
}
/**
* ToolSelector - 工具选择组件
*
* 职责:
* 1. 按分类展示工具列表Accordion 手风琴)
* 2. 复选框选择/取消工具
* 3. 显示每个分类的已选/总数(如 "3/5"
* 4. 全选/清空按钮
*
* 设计特性:
* - 手风琴分类折叠
* - 悬停工具项右移 4px
* - 全选/清空按钮渐变色
* - 分类徽章显示选中数量
*/
const ToolSelector: React.FC<ToolSelectorProps> = ({ selectedTools, onToolsChange }) => {
/**
* 全选所有工具
*/
const handleSelectAll = () => {
onToolsChange(MCP_TOOLS.map((t) => t.id));
};
/**
* 清空所有选择
*/
const handleClearAll = () => {
onToolsChange([]);
};
return (
<>
{/* 工具分类手风琴 */}
<Accordion allowMultiple>
{Object.entries(TOOL_CATEGORIES).map(([category, tools], catIdx) => {
const selectedCount = tools.filter((t) => selectedTools.includes(t.id)).length;
const totalCount = tools.length;
return (
<motion.div
key={category}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: catIdx * 0.05 }}
>
<AccordionItem
border="1px solid"
borderColor="rgba(255, 255, 255, 0.1)"
borderRadius="lg"
mb={2}
bg="rgba(255, 255, 255, 0.05)"
backdropFilter="blur(12px)"
_hover={{
bg: 'rgba(255, 255, 255, 0.08)',
borderColor: 'rgba(255, 255, 255, 0.2)',
}}
>
{/* 手风琴标题 */}
<AccordionButton>
<HStack flex={1} justify="space-between" pr={2}>
<Text color="gray.100" fontSize="sm">
{category}
</Text>
<Badge
bgGradient="linear(to-r, blue.500, purple.500)"
color="white"
variant="subtle"
boxShadow="0 2px 8px rgba(139, 92, 246, 0.3)"
>
{selectedCount}/{totalCount}
</Badge>
</HStack>
<AccordionIcon color="gray.400" />
</AccordionButton>
{/* 手风琴内容 */}
<AccordionPanel pb={4}>
<CheckboxGroup value={selectedTools} onChange={onToolsChange}>
<VStack align="stretch" spacing={2}>
{tools.map((tool) => (
<motion.div
key={tool.id}
whileHover={{ x: 4 }}
transition={{ type: 'spring', stiffness: 300 }}
>
<Checkbox
value={tool.id}
colorScheme="purple"
p={2}
borderRadius="lg"
bg="rgba(255, 255, 255, 0.02)"
_hover={{ bg: 'rgba(255, 255, 255, 0.05)' }}
transition="background 0.2s"
>
<HStack spacing={2} align="start">
{/* 工具图标 */}
<Box color="purple.400" mt={0.5}>
{tool.icon}
</Box>
{/* 工具信息 */}
<Box>
<Text fontSize="sm" color="gray.200">
{tool.name}
</Text>
<Text fontSize="xs" color="gray.500">
{tool.description}
</Text>
</Box>
</HStack>
</Checkbox>
</motion.div>
))}
</VStack>
</CheckboxGroup>
</AccordionPanel>
</AccordionItem>
</motion.div>
);
})}
</Accordion>
{/* 全选/清空按钮 */}
<HStack mt={4} spacing={2}>
{/* 全选按钮 */}
<Box flex={1}>
<motion.div whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }}>
<Button
size="sm"
w="full"
variant="ghost"
onClick={handleSelectAll}
bgGradient="linear(to-r, blue.500, purple.500)"
color="white"
_hover={{
bgGradient: 'linear(to-r, blue.600, purple.600)',
boxShadow: '0 4px 12px rgba(139, 92, 246, 0.4)',
}}
>
</Button>
</motion.div>
</Box>
{/* 清空按钮 */}
<Box flex={1}>
<motion.div whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }}>
<Button
size="sm"
w="full"
variant="ghost"
onClick={handleClearAll}
bg="rgba(255, 255, 255, 0.05)"
color="gray.300"
border="1px solid"
borderColor="rgba(255, 255, 255, 0.1)"
_hover={{
bg: 'rgba(255, 255, 255, 0.1)',
borderColor: 'rgba(255, 255, 255, 0.2)',
}}
>
</Button>
</motion.div>
</Box>
</HStack>
</>
);
};
export default ToolSelector;