日历优化

This commit is contained in:
2026-06-19 09:24:19 +08:00 Unverified
parent 2f834a0eb7
commit 3adca3d226
2 changed files with 139 additions and 18 deletions
+115 -8
View File
@@ -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) => ({
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
})[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(`<span class="${isCurrent ? '' : 'muted'} ${isToday ? 'today' : ''}">${displayDay}</span>`);
const title = `${toDateKey(cellDate)} 农历${lunar.month}${lunar.day}日`;
cells.push(`
<span
class="${isCurrent ? '' : 'muted'} ${isToday ? 'today' : ''}"
title="${escapeHtml(title)}"
aria-label="${escapeHtml(title)}"
>
<b>${displayDay}</b>
</span>
`);
}
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}天`);
});
}
+24 -10
View File
@@ -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 {