import type { CollectionEntry } from 'astro:content' import React, { useState } from 'react' type Post = CollectionEntry<'post'> interface Props { posts: Post[] currentDate?: Date } interface DateTimeFormatOptions { year?: 'numeric' | '2-digit' month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow' day?: 'numeric' | '2-digit' } const weekDays = ['一', '二', '三', '四', '五', '六', '日'] const getFormattedDate = (date: Date, options: DateTimeFormatOptions): string => { return new Intl.DateTimeFormat('zh-CN', options).format(date) } export const Calendar: React.FC = ({ posts, currentDate: initialDate = new Date() }) => { const [currentDate, setCurrentDate] = useState(new Date(initialDate)) // 将日记按日期分组 const postsByDate = new Map() posts.forEach((post) => { const date = getFormattedDate(new Date(post.data.date), { year: 'numeric', month: '2-digit', day: '2-digit' }) if (!postsByDate.has(date)) { postsByDate.set(date, []) } postsByDate.get(date)?.push(post) }) // 获取所有有日记的月份 const months = posts.map((post) => { const date = new Date(post.data.date) return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}` }) const uniqueMonths = Array.from(new Set(months)).sort() const getCurrentMonthStr = (): string => { return `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}` } const canNavigateToMonth = (direction: 'prev' | 'next'): boolean => { const currentMonthStr = getCurrentMonthStr() if (direction === 'prev') { return uniqueMonths.some((m) => m < currentMonthStr) } else { const now = new Date() const currentMonth = new Date(currentDate.getFullYear(), currentDate.getMonth()) const thisMonth = new Date(now.getFullYear(), now.getMonth()) return currentMonth < thisMonth } } const renderCalendarDays = () => { // 获取当月的第一天和最后一天 const firstDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1) const lastDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0) // 获取当月第一天是星期几 const firstDayWeekday = firstDayOfMonth.getDay() const adjustedFirstDayWeekday = firstDayWeekday === 0 ? 7 : firstDayWeekday // 计算日历表格需要显示的天数 const daysInPrevMonth = adjustedFirstDayWeekday - 1 const daysInCurrentMonth = lastDayOfMonth.getDate() const totalDays = Math.ceil((daysInPrevMonth + daysInCurrentMonth) / 7) * 7 // 获取上个月的最后几天 const lastDayOfPrevMonth = new Date( currentDate.getFullYear(), currentDate.getMonth(), 0 ).getDate() const days = [] for (let i = 0; i < totalDays; i++) { const dayNumber = i - daysInPrevMonth + 1 const isCurrentMonth = dayNumber > 0 && dayNumber <= daysInCurrentMonth const displayDay = isCurrentMonth ? dayNumber : dayNumber <= 0 ? lastDayOfPrevMonth + dayNumber : dayNumber - daysInCurrentMonth const date = new Date( currentDate.getFullYear(), isCurrentMonth ? currentDate.getMonth() : dayNumber <= 0 ? currentDate.getMonth() - 1 : currentDate.getMonth() + 1, displayDay ) const formattedDate = getFormattedDate(date, { year: 'numeric', month: '2-digit', day: '2-digit' }) const postsForDay = Array.from(postsByDate.get(formattedDate) || []) const hasPost = postsForDay.length > 0 days.push(
{displayDay}
{hasPost && (
{postsForDay.map((post: Post) => ( {post.data.title} ))}
)}
) } return days } const handlePrevMonth = () => { if (canNavigateToMonth('prev')) { setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1)) } } const handleNextMonth = () => { if (canNavigateToMonth('next')) { setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1)) } } return (

{currentDate.getFullYear()}年{currentDate.getMonth() + 1}月

{weekDays.map((day) => (
{day}
))} {renderCalendarDays()}
) }