From 82c291ef305e6da0857814fe656466744a7411b3 Mon Sep 17 00:00:00 2001 From: ddshi <8811906+ddshi@user.noreply.gitee.com> Date: Thu, 29 Jan 2026 17:10:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(P3):=20=E7=BA=AA=E5=BF=B5=E6=97=A5?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20-=20=E5=80=92=E8=AE=A1=E6=97=B6=E3=80=81?= =?UTF-8?q?=E8=8A=82=E5=81=87=E6=97=A5=E3=80=81=E5=86=85=E7=BD=AE=E8=8A=82?= =?UTF-8?q?=E5=81=87=E6=97=A5=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加倒计时计算工具 utils/countdown.ts - 添加内置节假日数据 constants/holidays.ts (中国法定节假日) - 更新 AnniversaryCard 显示倒计时和节假日标识 - 更新 AnniversaryList 显示即将到来的内置节假日 - 支持农历日期处理 (lunar-javascript) - 倒计时显示:今天/X天/已过 Co-Authored-By: Claude --- .../anniversary/AnniversaryCard.tsx | 39 ++- .../anniversary/AnniversaryList.tsx | 147 ++++++++++- src/constants/holidays.ts | 249 ++++++++++++++++++ src/utils/countdown.ts | 152 +++++++++++ tmpclaude-2781-cwd | 1 + tmpclaude-3f83-cwd | 1 + tmpclaude-6e9e-cwd | 1 + tmpclaude-b174-cwd | 1 + tmpclaude-eae0-cwd | 1 + 9 files changed, 574 insertions(+), 18 deletions(-) create mode 100644 src/constants/holidays.ts create mode 100644 src/utils/countdown.ts create mode 100644 tmpclaude-2781-cwd create mode 100644 tmpclaude-3f83-cwd create mode 100644 tmpclaude-6e9e-cwd create mode 100644 tmpclaude-b174-cwd create mode 100644 tmpclaude-eae0-cwd 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} + +