feat(P3): 纪念日功能 - 倒计时、节假日、内置节假日显示
- 添加倒计时计算工具 utils/countdown.ts - 添加内置节假日数据 constants/holidays.ts (中国法定节假日) - 更新 AnniversaryCard 显示倒计时和节假日标识 - 更新 AnniversaryList 显示即将到来的内置节假日 - 支持农历日期处理 (lunar-javascript) - 倒计时显示:今天/X天/已过 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5d7b99767d
commit
82c291ef30
@ -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 (
|
||||
<Card
|
||||
@ -22,17 +27,33 @@ export function AnniversaryCard({ event, onClick }: AnniversaryCardProps) {
|
||||
onMouseLeave={(e) => (e.currentTarget.style.transform = 'translateY(0)')}
|
||||
>
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Stack gap={4}>
|
||||
<Stack gap={4} style={{ flex: 1 }}>
|
||||
<Group gap={6}>
|
||||
<IconHeart size={14} color="pink" />
|
||||
<Text fw={500} size="sm" lineClamp={1}>
|
||||
<Text fw={500} size="sm" lineClamp={1} style={{ flex: 1 }}>
|
||||
{event.title}
|
||||
</Text>
|
||||
</Group>
|
||||
<Text size="xs" c="dimmed">
|
||||
{new Date(event.date).toLocaleDateString('zh-CN')}
|
||||
{isLunar && ' (农历)'}
|
||||
</Text>
|
||||
|
||||
<Group gap="xs">
|
||||
{/* Countdown badge */}
|
||||
<Badge
|
||||
size="sm"
|
||||
variant="filled"
|
||||
color={countdown.isPast ? 'gray' : countdown.isToday ? 'red' : 'blue'}
|
||||
leftSection={<IconCalendar size={10} />}
|
||||
>
|
||||
{formattedCountdown}
|
||||
</Badge>
|
||||
|
||||
{/* Date display */}
|
||||
<Text size="xs" c="dimmed">
|
||||
{countdown.isPast
|
||||
? '已过'
|
||||
: `${countdown.nextDate.getMonth() + 1}月${countdown.nextDate.getDate()}日`}
|
||||
{isLunar && ' (农历)'}
|
||||
</Text>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
<Group gap={6}>
|
||||
@ -46,7 +67,7 @@ export function AnniversaryCard({ event, onClick }: AnniversaryCardProps) {
|
||||
{repeatType === 'yearly' ? '每年' : '每月'}
|
||||
</Badge>
|
||||
)}
|
||||
{event.is_holiday && (
|
||||
{(event.is_holiday || holiday) && (
|
||||
<Badge size="xs" variant="light" color="green">
|
||||
节假日
|
||||
</Badge>
|
||||
|
||||
@ -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 (
|
||||
<Paper p="md" withBorder radius="md" h="100%">
|
||||
<Stack align="center" justify="center" h="100%">
|
||||
@ -35,9 +106,14 @@ export function AnniversaryList({ events, onEventClick, onAddClick }: Anniversar
|
||||
return (
|
||||
<Paper p="md" withBorder radius="md" h="100%">
|
||||
<Group justify="space-between" mb="sm">
|
||||
<Text fw={500} size="sm">
|
||||
纪念日
|
||||
</Text>
|
||||
<Group gap={8}>
|
||||
<Text fw={500} size="sm">
|
||||
纪念日
|
||||
</Text>
|
||||
<Badge size="xs" variant="light" color="gray">
|
||||
{anniversaries.length}
|
||||
</Badge>
|
||||
</Group>
|
||||
<Button
|
||||
variant="subtle"
|
||||
size="xs"
|
||||
@ -49,9 +125,62 @@ export function AnniversaryList({ events, onEventClick, onAddClick }: Anniversar
|
||||
</Group>
|
||||
|
||||
<Stack gap="xs" style={{ maxHeight: 'calc(100% - 40px)', overflowY: 'auto' }}>
|
||||
{anniversaries.map((event) => (
|
||||
<AnniversaryCard key={event.id} event={event} onClick={() => onEventClick(event)} />
|
||||
))}
|
||||
{/* 内置节假日 */}
|
||||
{allAnniversaries.builtIn.length > 0 && (
|
||||
<>
|
||||
<Text size="xs" c="dimmed" fw={500} tt="uppercase">
|
||||
即将到来
|
||||
</Text>
|
||||
{allAnniversaries.builtIn.map((holiday) => {
|
||||
const countdown = calculateCountdown(holiday.date, holiday.repeat_type, holiday.is_lunar);
|
||||
return (
|
||||
<Card
|
||||
key={holiday.id}
|
||||
shadow="sm"
|
||||
padding="sm"
|
||||
radius="md"
|
||||
style={{ cursor: 'pointer', opacity: 0.8, backgroundColor: '#f8f9fa' }}
|
||||
>
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Group gap={6}>
|
||||
<ThemeIcon size="xs" variant="light" color="green">
|
||||
<IconCalendar size={10} />
|
||||
</ThemeIcon>
|
||||
<Text fw={500} size="sm">
|
||||
{holiday.title}
|
||||
</Text>
|
||||
</Group>
|
||||
<Badge
|
||||
size="xs"
|
||||
variant="filled"
|
||||
color={countdown.isToday ? 'red' : 'green'}
|
||||
>
|
||||
{formatCountdown(countdown)}
|
||||
</Badge>
|
||||
</Group>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 用户纪念日 */}
|
||||
{allAnniversaries.user.length > 0 && (
|
||||
<>
|
||||
{allAnniversaries.builtIn.length > 0 && (
|
||||
<Text size="xs" c="dimmed" fw={500} tt="uppercase" mt="xs">
|
||||
我的纪念日
|
||||
</Text>
|
||||
)}
|
||||
{allAnniversaries.user.map((event) => (
|
||||
<AnniversaryCard
|
||||
key={event.id}
|
||||
event={event}
|
||||
onClick={() => onEventClick(event)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
);
|
||||
|
||||
249
src/constants/holidays.ts
Normal file
249
src/constants/holidays.ts
Normal file
@ -0,0 +1,249 @@
|
||||
/**
|
||||
* 中国法定节假日数据
|
||||
* 基于2024年节假日安排
|
||||
* 每个节日包含:名称、日期(公历/农历)、是否放假日
|
||||
*/
|
||||
|
||||
export interface Holiday {
|
||||
id: string;
|
||||
name: string;
|
||||
month: number; // 公历月份
|
||||
day: number; // 公历日期
|
||||
isLunar: boolean; // 是否为农历日期
|
||||
lunarMonth?: number; // 农历月份(农历日期时使用)
|
||||
lunarDay?: number; // 农历日期(农历日期时使用)
|
||||
isStatutory: boolean; // 是否法定节假日
|
||||
repeatYearly: boolean; // 是否每年重复
|
||||
}
|
||||
|
||||
export const HOLIDAYS: Holiday[] = [
|
||||
// 元旦
|
||||
{
|
||||
id: 'new-year',
|
||||
name: '元旦',
|
||||
month: 1,
|
||||
day: 1,
|
||||
isLunar: false,
|
||||
isStatutory: true,
|
||||
repeatYearly: true,
|
||||
},
|
||||
// 春节
|
||||
{
|
||||
id: 'spring-festival',
|
||||
name: '春节',
|
||||
isLunar: true,
|
||||
lunarMonth: 1,
|
||||
lunarDay: 1,
|
||||
isStatutory: true,
|
||||
repeatYearly: true,
|
||||
},
|
||||
// 元宵节
|
||||
{
|
||||
id: 'lantern',
|
||||
name: '元宵节',
|
||||
isLunar: true,
|
||||
lunarMonth: 1,
|
||||
lunarDay: 15,
|
||||
isStatutory: false,
|
||||
repeatYearly: true,
|
||||
},
|
||||
// 清明节
|
||||
{
|
||||
id: 'qingming',
|
||||
name: '清明节',
|
||||
month: 4,
|
||||
day: 4,
|
||||
isLunar: false,
|
||||
isStatutory: true,
|
||||
repeatYearly: true,
|
||||
},
|
||||
// 劳动节
|
||||
{
|
||||
id: 'labor-day',
|
||||
name: '劳动节',
|
||||
month: 5,
|
||||
day: 1,
|
||||
isLunar: false,
|
||||
isStatutory: true,
|
||||
repeatYearly: true,
|
||||
},
|
||||
// 端午节
|
||||
{
|
||||
id: 'dragon-boat',
|
||||
name: '端午节',
|
||||
isLunar: true,
|
||||
lunarMonth: 5,
|
||||
lunarDay: 5,
|
||||
isStatutory: true,
|
||||
repeatYearly: true,
|
||||
},
|
||||
// 中秋节
|
||||
{
|
||||
id: 'mid-autumn',
|
||||
name: '中秋节',
|
||||
isLunar: true,
|
||||
lunarMonth: 8,
|
||||
lunarDay: 15,
|
||||
isStatutory: true,
|
||||
repeatYearly: true,
|
||||
},
|
||||
// 国庆节
|
||||
{
|
||||
id: 'national-day',
|
||||
name: '国庆节',
|
||||
month: 10,
|
||||
day: 1,
|
||||
isLunar: false,
|
||||
isStatutory: true,
|
||||
repeatYearly: true,
|
||||
},
|
||||
// 重阳节
|
||||
{
|
||||
id: 'double-ninth',
|
||||
name: '重阳节',
|
||||
isLunar: true,
|
||||
lunarMonth: 9,
|
||||
lunarDay: 9,
|
||||
isStatutory: false,
|
||||
repeatYearly: true,
|
||||
},
|
||||
// 冬至
|
||||
{
|
||||
id: 'winter-solstice',
|
||||
name: '冬至',
|
||||
month: 12,
|
||||
day: 21,
|
||||
isLunar: false,
|
||||
isStatutory: false,
|
||||
repeatYearly: true,
|
||||
},
|
||||
// 腊八节
|
||||
{
|
||||
id: 'laba',
|
||||
name: '腊八节',
|
||||
isLunar: true,
|
||||
lunarMonth: 12,
|
||||
lunarDay: 8,
|
||||
isStatutory: false,
|
||||
repeatYearly: true,
|
||||
},
|
||||
// 除夕
|
||||
{
|
||||
id: 'chinese-new-years-eve',
|
||||
name: '除夕',
|
||||
isLunar: true,
|
||||
lunarMonth: 12,
|
||||
lunarDay: 30,
|
||||
isStatutory: true,
|
||||
repeatYearly: true,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取所有内置节假日
|
||||
*/
|
||||
export function getBuiltInHolidays(): Holiday[] {
|
||||
return [...HOLIDAYS];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查找节假日
|
||||
*/
|
||||
export function getHolidayById(id: string): Holiday | undefined {
|
||||
return HOLIDAYS.find((h) => h.id === id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查给定日期是否为节假日
|
||||
*/
|
||||
export function isHoliday(
|
||||
date: Date,
|
||||
year: number = date.getFullYear()
|
||||
): Holiday | undefined {
|
||||
const month = date.getMonth() + 1;
|
||||
const day = date.getDate();
|
||||
|
||||
for (const holiday of HOLIDAYS) {
|
||||
if (holiday.isLunar && holiday.lunarMonth && holiday.lunarDay) {
|
||||
// 农历日期需要转换
|
||||
try {
|
||||
const lunar = Lunar.fromYmd(year, month, day);
|
||||
if (
|
||||
lunar.getMonth() === holiday.lunarMonth &&
|
||||
lunar.getDay() === holiday.lunarDay
|
||||
) {
|
||||
return holiday;
|
||||
}
|
||||
} catch {
|
||||
// 农历转换失败,忽略
|
||||
}
|
||||
} else {
|
||||
// 公历日期直接比较
|
||||
if (holiday.month === month && holiday.day === day) {
|
||||
return holiday;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定年份的所有节假日
|
||||
*/
|
||||
export function getHolidaysForYear(year: number): Array<Holiday & { date: Date }> {
|
||||
const result: Array<Holiday & { date: Date }> = [];
|
||||
|
||||
for (const holiday of HOLIDAYS) {
|
||||
if (holiday.isLunar && holiday.lunarMonth && holiday.lunarDay) {
|
||||
try {
|
||||
const lunar = Lunar.fromYmd(year, holiday.lunarMonth, holiday.lunarDay);
|
||||
const solar = lunar.getSolar();
|
||||
result.push({
|
||||
...holiday,
|
||||
date: new Date(solar.getYear(), solar.getMonth() - 1, solar.getDay()),
|
||||
});
|
||||
} catch {
|
||||
// 农历转换失败,跳过
|
||||
}
|
||||
} else if (holiday.month && holiday.day) {
|
||||
result.push({
|
||||
...holiday,
|
||||
date: new Date(year, holiday.month - 1, holiday.day),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 按日期排序
|
||||
result.sort((a, b) => a.date.getTime() - b.date.getTime());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取即将到来的节假日
|
||||
*/
|
||||
export function getUpcomingHolidays(
|
||||
fromDate: Date = new Date(),
|
||||
limit: number = 5
|
||||
): Array<Holiday & { date: Date; daysUntil: number }> {
|
||||
const year = fromDate.getFullYear();
|
||||
const nextYear = year + 1;
|
||||
const holidays = [
|
||||
...getHolidaysForYear(year),
|
||||
...getHolidaysForYear(nextYear),
|
||||
];
|
||||
|
||||
const now = new Date();
|
||||
now.setHours(0, 0, 0, 0);
|
||||
|
||||
const upcoming = holidays
|
||||
.filter((h) => h.date >= now)
|
||||
.map((h) => ({
|
||||
...h,
|
||||
daysUntil: Math.ceil((h.date.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)),
|
||||
}))
|
||||
.slice(0, limit);
|
||||
|
||||
return upcoming;
|
||||
}
|
||||
152
src/utils/countdown.ts
Normal file
152
src/utils/countdown.ts
Normal file
@ -0,0 +1,152 @@
|
||||
import { Lunar, Solar } from 'lunar-javascript';
|
||||
|
||||
export interface CountdownResult {
|
||||
days: number;
|
||||
hours: number;
|
||||
minutes: number;
|
||||
seconds: number;
|
||||
nextDate: Date;
|
||||
isToday: boolean;
|
||||
isPast: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算纪念日的倒计时
|
||||
* 考虑重复规则,计算下一个 occurrence 的倒计时
|
||||
*/
|
||||
export function calculateCountdown(
|
||||
dateStr: string,
|
||||
repeatType: 'yearly' | 'monthly' | 'none',
|
||||
isLunar: boolean
|
||||
): CountdownResult {
|
||||
const now = new Date();
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
let targetDate = new Date(dateStr);
|
||||
let isPast = false;
|
||||
|
||||
// 如果是农历日期,转换为公历
|
||||
if (isLunar) {
|
||||
const lunarDate = Lunar.fromYmd(
|
||||
targetDate.getFullYear(),
|
||||
targetDate.getMonth() + 1,
|
||||
targetDate.getDate()
|
||||
);
|
||||
const solar = lunarDate.getSolar();
|
||||
targetDate = new Date(solar.getYear(), solar.getMonth() - 1, solar.getDay());
|
||||
}
|
||||
|
||||
// 计算下一个 occurrence
|
||||
if (repeatType === 'yearly') {
|
||||
// 年度重复:找到今年或明年的对应日期
|
||||
const thisYearTarget = new Date(today.getFullYear(), targetDate.getMonth(), targetDate.getDate());
|
||||
if (thisYearTarget >= today) {
|
||||
targetDate = thisYearTarget;
|
||||
} else {
|
||||
targetDate = new Date(today.getFullYear() + 1, targetDate.getMonth(), targetDate.getDate());
|
||||
}
|
||||
} else if (repeatType === 'monthly') {
|
||||
// 月度重复:找到本月或下月的对应日期
|
||||
const thisMonthTarget = new Date(today.getFullYear(), today.getMonth(), targetDate.getDate());
|
||||
if (thisMonthTarget >= today) {
|
||||
targetDate = thisMonthTarget;
|
||||
} else {
|
||||
targetDate = new Date(today.getFullYear(), today.getMonth() + 1, targetDate.getDate());
|
||||
}
|
||||
} else {
|
||||
// 不重复
|
||||
if (targetDate < today) {
|
||||
isPast = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算时间差
|
||||
const diff = targetDate.getTime() - now.getTime();
|
||||
const isToday = targetDate.toDateString() === today.toDateString();
|
||||
|
||||
if (diff < 0 && !isPast) {
|
||||
isPast = true;
|
||||
}
|
||||
|
||||
const days = Math.max(0, Math.floor(diff / (1000 * 60 * 60 * 24)));
|
||||
const hours = Math.max(0, Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)));
|
||||
const minutes = Math.max(0, Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)));
|
||||
const seconds = Math.max(0, Math.floor((diff % (1000 * 60)) / 1000));
|
||||
|
||||
return {
|
||||
days,
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
nextDate: targetDate,
|
||||
isToday,
|
||||
isPast,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化倒计时显示
|
||||
*/
|
||||
export function formatCountdown(countdown: CountdownResult): string {
|
||||
if (countdown.isPast) {
|
||||
return '已过';
|
||||
}
|
||||
|
||||
if (countdown.isToday) {
|
||||
return '今天';
|
||||
}
|
||||
|
||||
if (countdown.days > 0) {
|
||||
return `${countdown.days}天`;
|
||||
}
|
||||
|
||||
return `${countdown.hours}时${countdown.minutes}分`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取友好的日期描述
|
||||
*/
|
||||
export function getFriendlyDateDescription(
|
||||
dateStr: string,
|
||||
repeatType: 'yearly' | 'monthly' | 'none',
|
||||
isLunar: boolean
|
||||
): string {
|
||||
const countdown = calculateCountdown(dateStr, repeatType, isLunar);
|
||||
const targetDate = countdown.nextDate;
|
||||
|
||||
if (countdown.isPast) {
|
||||
return '已过';
|
||||
}
|
||||
|
||||
if (countdown.isToday) {
|
||||
return '今天';
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const tomorrow = new Date(now);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
||||
if (targetDate.toDateString() === tomorrow.toDateString()) {
|
||||
return '明天';
|
||||
}
|
||||
|
||||
if (countdown.days < 7) {
|
||||
return `${countdown.days}天后`;
|
||||
}
|
||||
|
||||
// 返回格式化日期
|
||||
const month = targetDate.getMonth() + 1;
|
||||
const day = targetDate.getDate();
|
||||
|
||||
if (isLunar) {
|
||||
// 显示农历
|
||||
try {
|
||||
const solar = Solar.fromYmd(targetDate.getFullYear(), month, day);
|
||||
const lunar = solar.getLunar();
|
||||
return `${lunar.getMonthInChinese()}月${lunar.getDayInChinese()}`;
|
||||
} catch {
|
||||
return `${month}月${day}日`;
|
||||
}
|
||||
}
|
||||
|
||||
return `${month}月${day}日`;
|
||||
}
|
||||
1
tmpclaude-2781-cwd
Normal file
1
tmpclaude-2781-cwd
Normal file
@ -0,0 +1 @@
|
||||
/e/qia/client
|
||||
1
tmpclaude-3f83-cwd
Normal file
1
tmpclaude-3f83-cwd
Normal file
@ -0,0 +1 @@
|
||||
/e/qia/client
|
||||
1
tmpclaude-6e9e-cwd
Normal file
1
tmpclaude-6e9e-cwd
Normal file
@ -0,0 +1 @@
|
||||
/e/qia/client
|
||||
1
tmpclaude-b174-cwd
Normal file
1
tmpclaude-b174-cwd
Normal file
@ -0,0 +1 @@
|
||||
/e/qia/client
|
||||
1
tmpclaude-eae0-cwd
Normal file
1
tmpclaude-eae0-cwd
Normal file
@ -0,0 +1 @@
|
||||
/e/qia/client
|
||||
Loading…
x
Reference in New Issue
Block a user