feat: 完善纪念日农历日期功能
- 修复农历年度重复的计算bug:正确使用农历月/日查找对应公历日期 - 增强 FixedCalendar:显示农历日期(初一、十五等特殊日期) - 增强 AnniversaryCard:显示详细农历信息(如"正月十五") Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e7b6864b42
commit
eb7aeb586b
@ -1,8 +1,9 @@
|
||||
import { Paper, Text, Group, Stack, Badge } from '@mantine/core';
|
||||
import { IconRepeat, IconCalendar, IconFlag } from '@tabler/icons-react';
|
||||
import { IconRepeat } from '@tabler/icons-react';
|
||||
import type { Event } from '../../types';
|
||||
import { calculateCountdown } from '../../utils/countdown';
|
||||
import { getHolidayById } from '../../constants/holidays';
|
||||
import { getLunarInfo } from '../../utils/lunar';
|
||||
|
||||
interface AnniversaryCardProps {
|
||||
event: Event;
|
||||
@ -19,7 +20,17 @@ export function AnniversaryCard({ event, onClick }: AnniversaryCardProps) {
|
||||
const getNextDateText = () => {
|
||||
if (countdown.isPast) return '已过';
|
||||
if (countdown.isToday) return '今天';
|
||||
return `${countdown.nextDate.getMonth() + 1}月${countdown.nextDate.getDate()}日${isLunar ? ' (农历)' : ''}`;
|
||||
|
||||
const month = countdown.nextDate.getMonth() + 1;
|
||||
const day = countdown.nextDate.getDate();
|
||||
|
||||
if (isLunar) {
|
||||
// 显示农历信息
|
||||
const lunarInfo = getLunarInfo(countdown.nextDate);
|
||||
return `${lunarInfo.monthInChinese}${lunarInfo.dayInChinese}`;
|
||||
}
|
||||
|
||||
return `${month}月${day}日`;
|
||||
};
|
||||
|
||||
// 获取循环icon颜色
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
IconChevronLeft,
|
||||
IconChevronRight,
|
||||
} from '@tabler/icons-react';
|
||||
import { Lunar } from 'lunar-javascript';
|
||||
|
||||
interface FixedCalendarProps {
|
||||
value: Date | null;
|
||||
@ -45,6 +46,32 @@ function isDateInRange(date: Date, min?: Date, max?: Date): boolean {
|
||||
const WEEKDAYS = ['日', '一', '二', '三', '四', '五', '六'];
|
||||
const MONTH_NAMES = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
|
||||
|
||||
// 获取农历日期简写(用于日历显示)
|
||||
function getLunarDayText(date: Date): string {
|
||||
try {
|
||||
const lunar = Lunar.fromYmd(date.getFullYear(), date.getMonth() + 1, date.getDate());
|
||||
const day = lunar.getDay();
|
||||
// 初一显示为"初一",其他日期只显示日期数字
|
||||
if (day === 1) {
|
||||
return lunar.getMonthInChinese();
|
||||
}
|
||||
// 初一、十五、廿五等特殊日子
|
||||
const specialDays: Record<number, string> = {
|
||||
1: '初一',
|
||||
15: '十五',
|
||||
20: '廿',
|
||||
25: '廿五',
|
||||
30: '卅',
|
||||
};
|
||||
if (specialDays[day]) {
|
||||
return specialDays[day];
|
||||
}
|
||||
return lunar.getDayInChinese();
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function FixedCalendar({ value, onChange, minDate, maxDate }: FixedCalendarProps) {
|
||||
const [currentMonth, setCurrentMonth] = useState(value || new Date());
|
||||
|
||||
@ -62,30 +89,35 @@ export function FixedCalendar({ value, onChange, minDate, maxDate }: FixedCalend
|
||||
isToday: boolean;
|
||||
isSelected: boolean;
|
||||
isDisabled: boolean;
|
||||
lunarText?: string; // 农历日期简写
|
||||
}> = [];
|
||||
|
||||
// 上月剩余的天数
|
||||
const prevMonthDays = getDaysInMonth(new Date(year, month - 1, 1));
|
||||
for (let i = firstDay - 1; i >= 0; i--) {
|
||||
const date = new Date(year, month - 1, prevMonthDays - i);
|
||||
const lunarText = getLunarDayText(date);
|
||||
days.push({
|
||||
date,
|
||||
isCurrentMonth: false,
|
||||
isToday: isSameDay(date, new Date()),
|
||||
isSelected: isSameDay(date, value),
|
||||
isDisabled: !isDateInRange(date, minDate, maxDate),
|
||||
lunarText,
|
||||
});
|
||||
}
|
||||
|
||||
// 当月的天数
|
||||
for (let i = 1; i <= daysInMonth; i++) {
|
||||
const date = new Date(year, month, i);
|
||||
const lunarText = getLunarDayText(date);
|
||||
days.push({
|
||||
date,
|
||||
isCurrentMonth: true,
|
||||
isToday: isSameDay(date, new Date()),
|
||||
isSelected: isSameDay(date, value),
|
||||
isDisabled: !isDateInRange(date, minDate, maxDate),
|
||||
lunarText,
|
||||
});
|
||||
}
|
||||
|
||||
@ -93,12 +125,14 @@ export function FixedCalendar({ value, onChange, minDate, maxDate }: FixedCalend
|
||||
const remainingDays = 42 - days.length;
|
||||
for (let i = 1; i <= remainingDays; i++) {
|
||||
const date = new Date(year, month + 1, i);
|
||||
const lunarText = getLunarDayText(date);
|
||||
days.push({
|
||||
date,
|
||||
isCurrentMonth: false,
|
||||
isToday: isSameDay(date, new Date()),
|
||||
isSelected: isSameDay(date, value),
|
||||
isDisabled: !isDateInRange(date, minDate, maxDate),
|
||||
lunarText,
|
||||
});
|
||||
}
|
||||
|
||||
@ -200,6 +234,7 @@ export function FixedCalendar({ value, onChange, minDate, maxDate }: FixedCalend
|
||||
flex: 1,
|
||||
aspectRatio: '1',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: 13,
|
||||
@ -233,7 +268,19 @@ export function FixedCalendar({ value, onChange, minDate, maxDate }: FixedCalend
|
||||
}
|
||||
}}
|
||||
>
|
||||
{day.date.getDate()}
|
||||
<span>{day.date.getDate()}</span>
|
||||
{day.lunarText && day.isCurrentMonth && !day.isDisabled && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: 9,
|
||||
color: day.isSelected ? 'rgba(255,255,255,0.8)' : '#FF9500',
|
||||
fontWeight: 400,
|
||||
lineHeight: 1.2,
|
||||
}}
|
||||
>
|
||||
{day.lunarText}
|
||||
</span>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
|
||||
@ -75,10 +75,17 @@ export function calculateCountdown(
|
||||
const originalHours = parseInt(timeParts[0]) || 0;
|
||||
const originalMinutes = parseInt(timeParts[1]) || 0;
|
||||
|
||||
// 保存农历月份和日期(用于年度重复计算)
|
||||
let lunarMonth: number | null = null;
|
||||
let lunarDay: number | null = null;
|
||||
|
||||
if (isLunar) {
|
||||
// 农历日期:使用安全方法创建,处理月末边界
|
||||
const result = safeCreateLunarDate(originalYear, originalMonth + 1, originalDay);
|
||||
if (result) {
|
||||
// 保存农历日期
|
||||
lunarMonth = result.lunar.getMonth();
|
||||
lunarDay = result.lunar.getDay();
|
||||
targetDate = new Date(result.solar.getYear(), result.solar.getMonth() - 1, result.solar.getDay(), originalHours, originalMinutes);
|
||||
} else {
|
||||
// 无法解析农历日期,使用原始公历日期作为后备
|
||||
@ -94,12 +101,27 @@ export function calculateCountdown(
|
||||
|
||||
// 计算下一个 occurrence
|
||||
if (repeatType === 'yearly') {
|
||||
// 年度重复:找到今年或明年的对应日期
|
||||
targetDate = safeCreateDate(today.getFullYear(), targetDate.getMonth(), targetDate.getDate());
|
||||
targetDate.setHours(originalHours, originalMinutes, 0, 0);
|
||||
if (targetDate < today) {
|
||||
targetDate = safeCreateDate(today.getFullYear() + 1, targetDate.getMonth(), targetDate.getDate());
|
||||
if (isLunar && lunarMonth !== null && lunarDay !== null) {
|
||||
// 农历年度重复:根据农历日期查找今年或明年的对应公历日期
|
||||
const thisYearResult = safeCreateLunarDate(today.getFullYear(), lunarMonth, lunarDay);
|
||||
if (thisYearResult) {
|
||||
targetDate = new Date(thisYearResult.solar.getYear(), thisYearResult.solar.getMonth() - 1, thisYearResult.solar.getDay(), originalHours, originalMinutes);
|
||||
}
|
||||
// 如果今年的农历日期已过,查找明年
|
||||
if (!targetDate || targetDate < today) {
|
||||
const nextYearResult = safeCreateLunarDate(today.getFullYear() + 1, lunarMonth, lunarDay);
|
||||
if (nextYearResult) {
|
||||
targetDate = new Date(nextYearResult.solar.getYear(), nextYearResult.solar.getMonth() - 1, nextYearResult.solar.getDay(), originalHours, originalMinutes);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 公历年度重复:找到今年或明年的对应日期
|
||||
targetDate = safeCreateDate(today.getFullYear(), targetDate.getMonth(), targetDate.getDate());
|
||||
targetDate.setHours(originalHours, originalMinutes, 0, 0);
|
||||
if (targetDate < today) {
|
||||
targetDate = safeCreateDate(today.getFullYear() + 1, targetDate.getMonth(), targetDate.getDate());
|
||||
targetDate.setHours(originalHours, originalMinutes, 0, 0);
|
||||
}
|
||||
}
|
||||
} else if (repeatType === 'monthly') {
|
||||
// 月度重复:找到本月或之后月份的对应日期
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user