/** * HealthRing 组件 * * Apple 健康风格的圆环进度指示器,用于可视化配额使用情况。 * 中心显示百分比和倒计时,颜色根据剩余配额自动变化。 */ import { useMemo } from 'react' import { useCountdown } from '@/hooks/useCountdown' /** 组件 Props 类型定义 */ export interface HealthRingProps { /** 账户名称 */ account: string /** 模型名称(可选) */ model?: string /** 模型显示名称 */ displayName?: string /** 剩余配额百分比 (0-1) */ remainingFraction: number /** 配额重置时间 (ISO 8601) */ resetTime?: string /** 圆环尺寸 (像素),默认 160 */ size?: number } /** * 颜色阈值配置 * 根据剩余配额百分比决定显示颜色 */ const COLOR_THRESHOLDS = [ { threshold: 0.05, color: '#FF3B30', bgColor: 'rgba(255, 59, 48, 0.2)' }, // 红色 - 紧急 { threshold: 0.2, color: '#FF9500', bgColor: 'rgba(255, 149, 0, 0.2)' }, // 橙色 - 警告 { threshold: 0.5, color: '#FFCC00', bgColor: 'rgba(255, 204, 0, 0.2)' }, // 黄色 - 注意 ] as const /** 默认颜色 (绿色 - 正常状态) */ const DEFAULT_COLOR = { color: '#34C759', bgColor: 'rgba(52, 199, 89, 0.2)' } /** * 根据剩余配额获取对应的颜色配置 * * @param fraction - 剩余配额百分比 (0-1) * @returns 前景色和背景色配置 */ const getColorConfig = ( fraction: number, ): { color: string; bgColor: string } => { for (const { threshold, color, bgColor } of COLOR_THRESHOLDS) { if (fraction < threshold) return { color, bgColor } } return DEFAULT_COLOR } export const HealthRing = ({ account, model, displayName, remainingFraction, resetTime, size = 160, }: HealthRingProps) => { const countdown = useCountdown(resetTime) // 使用 useMemo 缓存 SVG 计算值,避免不必要的重新计算 const svgParams = useMemo(() => { const strokeWidth = size * 0.1 const radius = (size - strokeWidth) / 2 const circumference = 2 * Math.PI * radius const strokeDashoffset = circumference * (1 - remainingFraction) return { strokeWidth, radius, circumference, strokeDashoffset } }, [size, remainingFraction]) const percentage = Math.round(remainingFraction * 100) const { color: ringColor, bgColor: ringBgColor } = getColorConfig(remainingFraction) const { strokeWidth, radius, circumference, strokeDashoffset } = svgParams return (