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 && ( +
+ {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()} +
+
+ ) +} 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)} />
- +