diff --git a/src/components/anniversary/AnniversaryCard.tsx b/src/components/anniversary/AnniversaryCard.tsx index f733cb0..1739d4d 100644 --- a/src/components/anniversary/AnniversaryCard.tsx +++ b/src/components/anniversary/AnniversaryCard.tsx @@ -1,6 +1,8 @@ -import { Card, Text, Badge, Group, ActionIcon, Stack } from '@mantine/core'; -import { IconHeart, IconRepeat } from '@tabler/icons-react'; +import { Card, Text, Badge, Group, Stack, ThemeIcon } from '@mantine/core'; +import { IconHeart, IconRepeat, IconCalendar } from '@tabler/icons-react'; import type { Event } from '../../types'; +import { calculateCountdown, formatCountdown } from '../../utils/countdown'; +import { getHolidayById } from '../../constants/holidays'; interface AnniversaryCardProps { event: Event; @@ -10,6 +12,9 @@ interface AnniversaryCardProps { export function AnniversaryCard({ event, onClick }: AnniversaryCardProps) { const isLunar = event.is_lunar; const repeatType = event.repeat_type; + const countdown = calculateCountdown(event.date, repeatType, isLunar); + const formattedCountdown = formatCountdown(countdown); + const holiday = event.is_holiday ? getHolidayById(event.title) || event.is_holiday : false; return ( (e.currentTarget.style.transform = 'translateY(0)')} > - + - + {event.title} - - {new Date(event.date).toLocaleDateString('zh-CN')} - {isLunar && ' (农历)'} - + + + {/* Countdown badge */} + } + > + {formattedCountdown} + + + {/* Date display */} + + {countdown.isPast + ? '已过' + : `${countdown.nextDate.getMonth() + 1}月${countdown.nextDate.getDate()}日`} + {isLunar && ' (农历)'} + + @@ -46,7 +67,7 @@ export function AnniversaryCard({ event, onClick }: AnniversaryCardProps) { {repeatType === 'yearly' ? '每年' : '每月'} )} - {event.is_holiday && ( + {(event.is_holiday || holiday) && ( 节假日 diff --git a/src/components/anniversary/AnniversaryList.tsx b/src/components/anniversary/AnniversaryList.tsx index 4e806e0..063c002 100644 --- a/src/components/anniversary/AnniversaryList.tsx +++ b/src/components/anniversary/AnniversaryList.tsx @@ -1,7 +1,11 @@ -import { Stack, Text, Paper, Group, Button } from '@mantine/core'; -import { IconPlus } from '@tabler/icons-react'; +import { useMemo } from 'react'; +import { Stack, Text, Paper, Group, Button, Badge, ThemeIcon, Card } from '@mantine/core'; +import { IconPlus, IconHeart, IconCalendar } from '@tabler/icons-react'; import { AnniversaryCard } from './AnniversaryCard'; +import { getHolidaysForYear, getUpcomingHolidays } from '../../constants/holidays'; +import { calculateCountdown, formatCountdown } from '../../utils/countdown'; import type { Event } from '../../types'; +import { Lunar, Solar } from 'lunar-javascript'; interface AnniversaryListProps { events: Event[]; @@ -9,10 +13,77 @@ interface AnniversaryListProps { 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'); - if (anniversaries.length === 0) { + // 获取内置节假日 + const builtInHolidays = useMemo(() => { + 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() + ); + + // 只取未来30天内的节假日 + const cutoffDate = new Date(now); + cutoffDate.setDate(cutoffDate.getDate() + 30); + + return allHolidays + .filter((h) => h.date >= now && h.date <= cutoffDate) + .slice(0, 5) + .map((h): BuiltInHolidayEvent => ({ + id: `builtin-${h.id}`, + title: h.name, + date: h.date.toISOString(), + is_holiday: true, + is_lunar: false, + repeat_type: 'yearly', + type: 'anniversary', + is_builtin: true, + })); + }, []); + + // 合并用户纪念日和内置节假日 + 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 ( @@ -35,9 +106,14 @@ export function AnniversaryList({ events, onEventClick, onAddClick }: Anniversar return ( - - 纪念日 - + + + 纪念日 + + + {anniversaries.length} + +