From 3adca3d226647279927a0f88dbedf18262dcf052 Mon Sep 17 00:00:00 2001 From: biss Date: Fri, 19 Jun 2026 09:24:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=A5=E5=8E=86=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/HomeSidebar.astro | 123 +++++++++++++++++++++++++++++-- src/styles/site.css | 34 ++++++--- 2 files changed, 139 insertions(+), 18 deletions(-) diff --git a/src/components/HomeSidebar.astro b/src/components/HomeSidebar.astro index 6aae57f..9a61c80 100644 --- a/src/components/HomeSidebar.astro +++ b/src/components/HomeSidebar.astro @@ -121,11 +121,62 @@ const recentPosts = posts.slice(0, 5); (() => { const dayNames = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; const pad = (value) => String(value).padStart(2, '0'); + const dayMs = 86400000; const percent = (used, total) => Math.min(100, Math.max(0, (used / total) * 100)); + const lunarFormatter = new Intl.DateTimeFormat('zh-CN-u-ca-chinese', { + year: 'numeric', + month: 'long', + day: 'numeric', + }); + const solarFestivals = { + '01-01': '元旦', + '03-08': '妇女节', + '03-12': '植树节', + '05-01': '劳动节', + '05-04': '青年节', + '06-01': '儿童节', + '07-01': '建党节', + '08-01': '建军节', + '09-10': '教师节', + '10-01': '国庆节', + }; + const lunarFestivals = { + '正月-1': '春节', + '正月-15': '元宵', + '五月-5': '端午', + '七月-7': '七夕', + '七月-15': '中元', + '八月-15': '中秋', + '九月-9': '重阳', + '腊月-8': '腊八', + '腊月-23': '北方小年', + '腊月-24': '南方小年', + }; + const springFestivalDates = [ + '2024-02-10', + '2025-01-29', + '2026-02-17', + '2027-02-06', + '2028-01-26', + '2029-02-13', + '2030-02-03', + '2031-01-23', + '2032-02-11', + ]; const setText = (id, value) => { const el = document.getElementById(id); if (el) el.textContent = value; }; + const toDateKey = (date) => `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`; + const toMonthDayKey = (date) => `${pad(date.getMonth() + 1)}-${pad(date.getDate())}`; + const escapeHtml = (value) => + String(value).replace(/[&<>"']/g, (char) => ({ + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + })[char]); function dayOfYear(date) { const start = new Date(date.getFullYear(), 0, 1); @@ -134,15 +185,55 @@ const recentPosts = posts.slice(0, 5); function weekOfYear(date) { const firstDay = new Date(date.getFullYear(), 0, 1); - const pastDays = Math.floor((date - firstDay) / 86400000); + const pastDays = Math.floor((date - firstDay) / dayMs); return Math.ceil((pastDays + firstDay.getDay() + 1) / 7); } + function getLunarInfo(date) { + const parts = lunarFormatter.formatToParts(date); + const month = parts.find((part) => part.type === 'month')?.value || ''; + const day = parts.find((part) => part.type === 'day')?.value || ''; + const yearName = parts.find((part) => part.type === 'yearName')?.value || ''; + return { + yearName, + month, + day, + dayName: day === '1' ? month : `${day}日`, + festival: lunarFestivals[`${month}-${day}`] || '', + }; + } + + function getFestival(date) { + const lunar = getLunarInfo(date); + const solar = solarFestivals[toMonthDayKey(date)] || ''; + return solar || lunar.festival; + } + + function getNextNewYearEve(now) { + const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const nextSpring = springFestivalDates + .map((date) => new Date(`${date}T00:00:00`)) + .find((date) => date > today); + + if (!nextSpring) { + return new Date(now.getFullYear() + 1, 1, 16); + } + + return new Date(nextSpring.getFullYear(), nextSpring.getMonth(), nextSpring.getDate() - 1); + } + function renderCalendar(now) { + const todayLunar = getLunarInfo(now); + const todayFestival = getFestival(now); setText('calendar-week', `第${weekOfYear(now)}周 ${dayNames[now.getDay()]}`); setText('calendar-date', pad(now.getDate())); setText('calendar-solar', `${now.getFullYear()}年${now.getMonth() + 1}月 第${dayOfYear(now)}天`); - setText('calendar-lunar', '万千风景,五历如一'); + setText( + 'calendar-lunar', + todayFestival + ? `${todayLunar.yearName}年 ${todayFestival}` + : `${todayLunar.yearName}年 农历${todayLunar.month}${todayLunar.day}日` + ); const grid = document.getElementById('calendar-grid'); if (!grid) return; @@ -162,8 +253,19 @@ const recentPosts = posts.slice(0, 5); : dayNumber < 1 ? previousDays + dayNumber : dayNumber - days; + const cellDate = new Date(year, month, dayNumber); + const lunar = getLunarInfo(cellDate); const isToday = isCurrent && displayDay === now.getDate(); - cells.push(`${displayDay}`); + const title = `${toDateKey(cellDate)} 农历${lunar.month}${lunar.day}日`; + cells.push(` + + ${displayDay} + + `); } grid.innerHTML = cells.join(''); @@ -171,8 +273,8 @@ const recentPosts = posts.slice(0, 5); function renderSchedule(now) { const year = now.getFullYear(); - const nextEve = new Date(year + 1, 1, 16); - const daysToEve = Math.max(0, Math.ceil((nextEve - now) / 86400000)); + const nextEve = getNextNewYearEve(now); + const daysToEve = Math.max(0, Math.ceil((nextEve - now) / dayMs)); const yearStart = new Date(year, 0, 1); const yearEnd = new Date(year + 1, 0, 1); const monthStart = new Date(year, now.getMonth(), 1); @@ -192,10 +294,15 @@ const recentPosts = posts.slice(0, 5); rows.forEach(([name, start, end]) => { const total = end - start; const used = now - start; - const left = Math.max(0, Math.ceil((end - now) / 86400000)); + const progressValue = percent(used, total); + const left = Math.max(0, Math.ceil((end - now) / dayMs)); const bar = document.getElementById(`${name}-progress`); - if (bar) bar.value = percent(used, total); - setText(`${name}-left`, `还剩${left}天`); + if (bar) { + bar.value = progressValue; + bar.title = `已过 ${progressValue.toFixed(1)}%`; + bar.setAttribute('aria-valuetext', `已过 ${progressValue.toFixed(1)}%`); + } + setText(`${name}-left`, `${progressValue.toFixed(0)}% / ${left}天`); }); } diff --git a/src/styles/site.css b/src/styles/site.css index 217c22d..aa5b6ec 100644 --- a/src/styles/site.css +++ b/src/styles/site.css @@ -800,17 +800,24 @@ a { .calendar-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); - gap: 5px 7px; + gap: 4px 7px; color: #8aa6be; - font-size: 0.78rem; text-align: center; } .calendar-grid span { display: grid; place-items: center; - min-height: 20px; + min-height: 22px; + padding: 0; + border: 1px solid transparent; border-radius: 999px; + line-height: 1; +} + +.calendar-grid b { + font-size: 0.78rem; + font-weight: 700; } .calendar-grid .muted { @@ -820,6 +827,7 @@ a { .calendar-grid .today { color: #fff; background: #66c8f5; + border-color: #66c8f5; } .schedule-bars { @@ -829,7 +837,7 @@ a { .schedule-row { display: grid; - grid-template-columns: 34px 1fr 64px; + grid-template-columns: 34px minmax(52px, 1fr) 74px; align-items: center; gap: 8px; color: var(--muted); @@ -838,27 +846,33 @@ a { .schedule-row progress { width: 100%; - height: 18px; + height: 10px; overflow: hidden; border: 0; - border-radius: 4px; - background: rgba(102, 200, 245, 0.18); + border-radius: 999px; + background: rgba(102, 200, 245, 0.16); } .schedule-row progress::-webkit-progress-bar { - background: rgba(102, 200, 245, 0.18); + border-radius: 999px; + background: rgba(102, 200, 245, 0.16); } .schedule-row progress::-webkit-progress-value { - background: rgba(102, 200, 245, 0.58); + border-radius: 999px; + background: linear-gradient(90deg, #5ec7f4, #42d4c4); } .schedule-row progress::-moz-progress-bar { - background: rgba(102, 200, 245, 0.58); + border-radius: 999px; + background: linear-gradient(90deg, #5ec7f4, #42d4c4); } .schedule-row em { + min-width: 0; font-style: normal; + text-align: right; + white-space: nowrap; } .sidebar-posts {