import { useMemo, useRef } from 'react'; import { Stack, Text, Paper, Group, Button, Box } from '@mantine/core'; import { IconPlus } from '@tabler/icons-react'; import { AnniversaryCard } from './AnniversaryCard'; import { getHolidaysForYear } from '../../constants/holidays'; import { calculateCountdown, formatCountdown } from '../../utils/countdown'; import { useAppStore } from '../../stores'; import type { Event } from '../../types'; interface AnniversaryListProps { events: Event[]; onEventClick: (event: Event) => void; onAddClick: () => void; } // 内置节假日事件类型 interface BuiltInHolidayEvent { id: string; title: string; date: string; is_holiday: boolean; is_lunar: boolean; repeat_type: 'yearly'; type: 'anniversary'; is_builtin: true; } export function AnniversaryList({ events, onEventClick, onAddClick }: AnniversaryListProps) { const anniversaries = events.filter((e) => e.type === 'anniversary'); const showHolidays = useAppStore((state) => state.settings?.showHolidays ?? true); const scrollContainerRef = useRef(null); // 滚动条样式 - 仅在悬停时显示 const scrollbarStyle = ` .anniversary-scroll::-webkit-scrollbar { width: 4px; height: 4px; } .anniversary-scroll::-webkit-scrollbar-track { background: transparent; } .anniversary-scroll::-webkit-scrollbar-thumb { background: transparent; border-radius: 2px; } .anniversary-scroll:hover::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.15); } `; // 处理滚轮事件,实现列表独立滚动 const handleWheel = (e: React.WheelEvent) => { const container = scrollContainerRef.current; if (!container) return; const { scrollTop, scrollHeight, clientHeight } = container; // 使用 1px 缓冲避免浮点数精度问题 const isAtTop = scrollTop <= 0; const isAtBottom = scrollTop + clientHeight >= scrollHeight - 1; // 如果已经滚动到顶部且向下滚动,或者已经滚动到底部且向上滚动,则阻止事件冒泡 if ((isAtTop && e.deltaY > 0) || (isAtBottom && e.deltaY < 0)) { e.stopPropagation(); } // 如果在滚动范围内,允许事件继续传递以实现正常滚动 }; // 获取内置节假日 const builtInHolidays = useMemo(() => { if (!showHolidays) return []; const now = new Date(); const year = now.getFullYear(); const holidays = getHolidaysForYear(year); const nextYear = getHolidaysForYear(year + 1); // 合并今年和明年的节假日,按日期排序 const allHolidays = [...holidays, ...nextYear].sort( (a, b) => a.date.getTime() - b.date.getTime() ); // 只取未来90天内的节假日,显示最近3个 const cutoffDate = new Date(now); cutoffDate.setDate(cutoffDate.getDate() + 90); return allHolidays .filter((h) => h.date >= now && h.date <= cutoffDate) .slice(0, 3) .map((h): BuiltInHolidayEvent => ({ id: `builtin-${h.id}`, title: h.name, date: h.date.toISOString(), is_holiday: true, is_lunar: h.isLunar, repeat_type: 'yearly', type: 'anniversary', is_builtin: true, })); }, [showHolidays]); // 合并用户纪念日和内置节假日 const allAnniversaries = useMemo(() => { // 用户创建的节假日按倒计时排序 const sortedUser = [...anniversaries].sort((a, b) => { const countdownA = calculateCountdown(a.date, a.repeat_type, a.is_lunar); const countdownB = calculateCountdown(b.date, b.repeat_type, b.is_lunar); return countdownA.nextDate.getTime() - countdownB.nextDate.getTime(); }); // 内置节假日按日期排序 const sortedBuiltIn = [...builtInHolidays].sort((a, b) => { const countdownA = calculateCountdown(a.date, a.repeat_type, a.is_lunar); const countdownB = calculateCountdown(b.date, b.repeat_type, b.is_lunar); return countdownA.nextDate.getTime() - countdownB.nextDate.getTime(); }); return { user: sortedUser, builtIn: sortedBuiltIn, total: sortedUser.length + sortedBuiltIn.length, }; }, [anniversaries, builtInHolidays]); // 空状态 if (allAnniversaries.total === 0) { return ( 暂无纪念日 ); } return ( <> 纪念日 {anniversaries.length} {/* 内置节假日 */} {allAnniversaries.builtIn.length > 0 && ( <> 即将到来 {allAnniversaries.builtIn.map((holiday) => { const countdown = calculateCountdown(holiday.date, holiday.repeat_type, holiday.is_lunar); return ( onEventClick(holiday as unknown as Event)} > {holiday.title} {formatCountdown(countdown)} ); })} )} {/* 用户纪念日 */} {allAnniversaries.user.length > 0 && ( <> {allAnniversaries.builtIn.length > 0 && ( 我的纪念日 )} {allAnniversaries.user.map((event) => ( onEventClick(event)} /> ))} )} ); }