diff --git a/src/components/blog/Calendar.astro b/src/components/blog/Calendar.astro
deleted file mode 100644
index a46e529..0000000
--- a/src/components/blog/Calendar.astro
+++ /dev/null
@@ -1,266 +0,0 @@
----
-import { getFormattedDate } from '@/utils'
-import type { CollectionEntry } from 'astro:content'
-
-interface Props {
- posts: CollectionEntry<'post'>[]
- currentDate?: Date
-}
-
-const { posts, currentDate = new Date() } = Astro.props
-
-// 将日记按日期分组
-const postsByDate = new Map()
-posts.forEach((post) => {
- const date = getFormattedDate(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 = Array.from(postsByDate.keys()).map((date) => {
- const [year, month] = date.split('-')
- return `${year}-${month}`
-})
-const uniqueMonths = Array.from(new Set(months)).sort().reverse()
-
-const weekDays = ['一', '二', '三', '四', '五', '六', '日']
----
-
-
-
-
-
- {currentDate.getFullYear()}年{currentDate.getMonth() + 1}月
-
-
-
-
-
- {weekDays.map((day) =>
{day}
)}
-
-
-
-
diff --git a/src/components/blog/Calendar.tsx b/src/components/blog/Calendar.tsx
new file mode 100644
index 0000000..ffd17a8
--- /dev/null
+++ b/src/components/blog/Calendar.tsx
@@ -0,0 +1,197 @@
+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 && (
+
+ )}
+
+ )
+ }
+
+ 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()}
+
+
+ )
+}
diff --git a/src/pages/diary/[...page].astro b/src/pages/diary/[...page].astro
index 5ffdd85..7626131 100644
--- a/src/pages/diary/[...page].astro
+++ b/src/pages/diary/[...page].astro
@@ -5,10 +5,10 @@ import type { GetStaticPaths, Page } from 'astro'
import type { CollectionEntry } from 'astro:content'
import Button from '@/components/Button.astro'
-import Calendar from '@/components/blog/Calendar.astro'
import PostPreview from '@/components/blog/PostPreview.astro'
import PageLayout from '@/layouts/BaseLayout.astro'
import { getallDiaries, getUniqueCategories, getUniqueTags, sortMDByDate } from '@/utils'
+import { Calendar } from '@/components/blog/Calendar'
export const getStaticPaths = (async ({ paginate }) => {
const allPosts = await getallDiaries()
@@ -82,7 +82,7 @@ const serializedPosts = allPosts.map((post) => ({
set:html={JSON.stringify(serializedPosts)}
/>
-
+