373fc0496c
统一简化多个页面里的CopyButton调用,删除不再需要的空回调参数 * feat: 添加clearCookies函数的单元测试并修复cookie域名处理逻辑 * feat: 增强 data URI 处理,支持带参数的前缀并更新相关测试 * fix: 修正 AGENTS.md 中 TypeScript 类型检查命令的描述 * feat: 添加 settings.local.json 文件以配置 Bash 权限 * perf: 预设背景色避免 Popup 弹窗白屏闪烁 * perf: 避免图标过早实例化,传递组件引用而非 JSX 节点 * feat: 添加 useLazyTranslation 和 preloadNamespaces 函数以支持动态加载 i18n 命名空间 * feat: 使用 useLazyTranslation 替换 useTranslation 以支持懒加载翻译 * feat: 添加 PageSkeleton 组件及其测试用例以支持页面加载骨架屏 * feat: 使用骨架屏替换加载状态指示器,优化用户体验 * feat: 优化 CopyButton 组件的复制功能,添加定时器管理复制状态 * feat: 调整 chunk 大小警告阈值以优化构建性能
163 lines
4.5 KiB
TypeScript
163 lines
4.5 KiB
TypeScript
/**
|
|
* ToolCard 组件 - 工具卡片
|
|
*
|
|
* 用于在仪表盘中展示各个工具功能的卡片组件,支持图标、标题、描述、
|
|
* 快照内容展示,具备悬停动画效果。
|
|
*/
|
|
import { alpha, Box, Card, CardActionArea, Stack, Typography, useTheme } from '@mui/material';
|
|
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
|
import type { SvgIconProps } from '@mui/material/SvgIcon';
|
|
import type { ComponentType } from 'react';
|
|
import type { PaletteColorKey } from '@/config/features';
|
|
|
|
/**
|
|
* ToolCard 组件属性接口
|
|
*/
|
|
interface ToolCardProps {
|
|
/** 工具卡片标题 */
|
|
title: string;
|
|
/** 工具卡片描述文本(可选) */
|
|
description?: string;
|
|
/** 快照内容,用于在卡片底部展示额外信息(可选) */
|
|
snapshot?: React.ReactNode;
|
|
/** 主题色键,映射到 theme.palette[key].main */
|
|
colorKey: PaletteColorKey;
|
|
/** 图标组件引用 */
|
|
icon: ComponentType<SvgIconProps>;
|
|
/** 卡片点击事件处理函数 */
|
|
onClick: () => void;
|
|
}
|
|
|
|
/**
|
|
* ToolCard 组件
|
|
*
|
|
* @param props - ToolCardProps 属性对象
|
|
* @returns 工具卡片 JSX 元素
|
|
*/
|
|
export default function ToolCard({
|
|
title,
|
|
description,
|
|
snapshot,
|
|
colorKey,
|
|
icon: IconComponent,
|
|
onClick,
|
|
}: ToolCardProps) {
|
|
const theme = useTheme();
|
|
const colorCode = theme.palette[colorKey].main;
|
|
|
|
return (
|
|
<Card
|
|
elevation={0}
|
|
sx={{
|
|
position: 'relative',
|
|
borderRadius: 4,
|
|
border: '1px solid',
|
|
borderColor: 'divider',
|
|
height: '100%',
|
|
boxSizing: 'border-box',
|
|
transition:
|
|
'border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
'&:hover': {
|
|
borderColor: colorCode,
|
|
transform: 'translateY(-4px)',
|
|
boxShadow: `0 12px 24px -10px ${alpha(colorCode, 0.2)}`,
|
|
},
|
|
}}
|
|
>
|
|
<CardActionArea
|
|
onClick={onClick}
|
|
sx={{
|
|
height: '100%',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'stretch',
|
|
justifyContent: 'flex-start',
|
|
p: 2.5,
|
|
gap: 1.5,
|
|
'&:hover .arrow-icon': {
|
|
transform: 'translateX(4px)',
|
|
color: colorCode,
|
|
},
|
|
}}
|
|
>
|
|
<Stack direction="row" justifyContent="space-between" alignItems="flex-start" width="100%">
|
|
<Stack direction="row" spacing={1.5} alignItems="center">
|
|
<Box
|
|
sx={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: 3,
|
|
bgcolor: alpha(colorCode, 0.07),
|
|
color: colorCode,
|
|
}}
|
|
>
|
|
<IconComponent sx={{ fontSize: 20 }} />
|
|
</Box>
|
|
<Box>
|
|
<Typography
|
|
variant="subtitle1"
|
|
sx={{
|
|
fontWeight: 700,
|
|
lineHeight: 1.2,
|
|
color: 'text.primary',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: 0.5,
|
|
}}
|
|
>
|
|
{title}
|
|
</Typography>
|
|
{description && (
|
|
<Typography
|
|
variant="caption"
|
|
sx={{
|
|
color: 'text.secondary',
|
|
fontWeight: 500,
|
|
mt: 0.5,
|
|
display: '-webkit-box',
|
|
WebkitBoxOrient: 'vertical',
|
|
WebkitLineClamp: 1,
|
|
overflow: 'hidden',
|
|
textOverflow: 'ellipsis',
|
|
wordBreak: 'break-word',
|
|
}}
|
|
>
|
|
{description}
|
|
</Typography>
|
|
)}
|
|
</Box>
|
|
</Stack>
|
|
<ArrowForwardIosIcon
|
|
className="arrow-icon"
|
|
sx={{
|
|
fontSize: 12,
|
|
color: 'text.disabled',
|
|
mt: 0.5,
|
|
transition: 'all 0.3s ease',
|
|
}}
|
|
/>
|
|
</Stack>
|
|
|
|
{snapshot != null && (
|
|
<Box
|
|
sx={{
|
|
mt: 'auto',
|
|
pt: 1.5,
|
|
borderTop: '1px dashed',
|
|
borderColor: 'divider',
|
|
width: '100%',
|
|
}}
|
|
>
|
|
{snapshot}
|
|
</Box>
|
|
)}
|
|
</CardActionArea>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
ToolCard.displayName = 'ToolCard';
|