"use client"; import { useEffect, useState } from "react"; import { Circle } from "lucide-react"; import { cn } from "@/lib/utils"; interface ActiveNowBadgeProps { activeHours: { weekday: string[]; weekend: string[] } | null | undefined; className?: string; showNextLabel?: boolean; } interface ActiveState { active: boolean; nextLabel: string | null; } /** * 根据当前时间判断 Bot 是否处于活跃时段 * 注意:依赖客户端时钟,会在挂载后立即计算并随后每分钟刷新 */ function computeState( activeHours: ActiveNowBadgeProps["activeHours"], now: Date ): ActiveState { if (!activeHours) return { active: false, nextLabel: null }; const day = now.getDay(); const isWeekend = day === 0 || day === 6; const ranges = isWeekend ? activeHours.weekend : activeHours.weekday; if (!ranges || ranges.length === 0) return { active: false, nextLabel: null }; const currentMinutes = now.getHours() * 60 + now.getMinutes(); for (const range of ranges) { if (range === "全天" || range === "全天活跃") return { active: true, nextLabel: null }; const m = range.match(/^(\d{1,2}):(\d{2})-(\d{1,2}):(\d{2})$/); if (!m) continue; const start = parseInt(m[1]) * 60 + parseInt(m[2]); const end = parseInt(m[3]) * 60 + parseInt(m[4]); if (start === end) continue; if (start < end) { if (currentMinutes >= start && currentMinutes < end) { return { active: true, nextLabel: null }; } } else { if (currentMinutes >= start || currentMinutes < end) { return { active: true, nextLabel: null }; } } } // 不在活跃时段,计算下次活跃时间 const tomorrowRanges = isWeekend ? activeHours.weekday : activeHours.weekend; const findNext = (rs: string[], label: string) => { for (const r of rs) { if (r === "全天" || r === "全天活跃") return `${label}全天`; const m = r.match(/^(\d{1,2}):(\d{2})-(\d{1,2}):(\d{2})$/); if (!m) continue; const start = parseInt(m[1]) * 60 + parseInt(m[2]); if (label === "今天" && start <= currentMinutes) continue; return `${label} ${m[1]}:${m[2]}`; } return null; }; return { active: false, nextLabel: findNext(ranges, "今天") || findNext(tomorrowRanges, "明天"), }; } export default function ActiveNowBadge({ activeHours, className, showNextLabel = false }: ActiveNowBadgeProps) { const [state, setState] = useState({ active: false, nextLabel: null }); const [mounted, setMounted] = useState(false); useEffect(() => { setMounted(true); const tick = () => setState(computeState(activeHours, new Date())); tick(); // 每分钟刷新一次 const id = setInterval(tick, 60_000); return () => clearInterval(id); }, [activeHours]); if (!mounted) { // 避免 hydration mismatch,先渲染一个不可见占位 return ; } if (state.active) { return ( 活跃中 ); } if (showNextLabel && state.nextLabel) { return ( {state.nextLabel} 上线 ); } return null; }