214 lines
7.5 KiB
Plaintext
214 lines
7.5 KiB
Plaintext
---
|
|
import type { BlogPost } from '@/lib/posts';
|
|
import SiteInfoCard from '@/components/SiteInfoCard.astro';
|
|
import { siteConfig } from '../../site.config.mjs';
|
|
|
|
interface Props {
|
|
posts: BlogPost[];
|
|
tags: string[];
|
|
categories: string[];
|
|
}
|
|
|
|
const { posts, tags, categories } = Astro.props;
|
|
const avatar = siteConfig.author.avatar;
|
|
const recentPosts = posts.slice(0, 5);
|
|
---
|
|
|
|
<aside class="home-sidebar" aria-label="首页侧边栏">
|
|
<section class="sidebar-card profile-card">
|
|
<img class="profile-avatar" src={avatar} alt={siteConfig.author.name} loading="lazy" />
|
|
<div class="profile-name">{siteConfig.author.name}</div>
|
|
<div class="profile-stats">
|
|
<a href="/archives/">
|
|
<span>文章</span>
|
|
<strong>{posts.length}</strong>
|
|
</a>
|
|
<a href="/tags/">
|
|
<span>标签</span>
|
|
<strong>{tags.length}</strong>
|
|
</a>
|
|
<a href="/categories/">
|
|
<span>分类</span>
|
|
<strong>{categories.length}</strong>
|
|
</a>
|
|
</div>
|
|
<a class="follow-button" href={siteConfig.author.github} target="_blank" rel="noreferrer">
|
|
<span class="github-mark" aria-hidden="true">GitHub</span>
|
|
<span>Follow Me</span>
|
|
</a>
|
|
<div class="profile-links" aria-label="联系方式">
|
|
<a href={siteConfig.author.github} target="_blank" rel="noreferrer" aria-label="GitHub">GitHub</a>
|
|
<a href={`mailto:${siteConfig.author.email}`} aria-label="Email">Email</a>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="sticky-sidebar">
|
|
<section class="sidebar-card notice-card">
|
|
<h2><span aria-hidden="true">!</span> 公告</h2>
|
|
<div id="welcome-ip-location-info">
|
|
欢迎来自 <strong>远方</strong> 的小友。<br />
|
|
烧烤三件套,灵魂蘸料。<br />
|
|
当前位置距离博主约 <strong>473</strong> 公里!
|
|
</div>
|
|
<p id="aside-weather">当前天气加载中...</p>
|
|
</section>
|
|
|
|
<section class="sidebar-card calendar-card">
|
|
<h2>日历</h2>
|
|
<div class="calendar-widget">
|
|
<div class="calendar-summary">
|
|
<strong id="calendar-week">第--周 周--</strong>
|
|
<span id="calendar-date">--</span>
|
|
<small id="calendar-solar">----年--月 第--天</small>
|
|
<small id="calendar-lunar">今天也很好</small>
|
|
</div>
|
|
<div class="calendar-grid" id="calendar-grid" aria-label="本月日历"></div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="sidebar-card schedule-card">
|
|
<h2>剩余时间</h2>
|
|
<div class="schedule-widget">
|
|
<div class="schedule-left">
|
|
<strong>距离除夕</strong>
|
|
<span id="new-year-days">--</span>
|
|
<small id="new-year-date">---- -- --</small>
|
|
</div>
|
|
<div class="schedule-bars">
|
|
<div class="schedule-row">
|
|
<span>本年</span>
|
|
<progress id="year-progress" max="100" value="0"></progress>
|
|
<em id="year-left">--天</em>
|
|
</div>
|
|
<div class="schedule-row">
|
|
<span>本月</span>
|
|
<progress id="month-progress" max="100" value="0"></progress>
|
|
<em id="month-left">--天</em>
|
|
</div>
|
|
<div class="schedule-row">
|
|
<span>本周</span>
|
|
<progress id="week-progress" max="100" value="0"></progress>
|
|
<em id="week-left">--天</em>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="sidebar-card recent-card">
|
|
<h2>最新文章</h2>
|
|
<div class="sidebar-posts">
|
|
{recentPosts.map((post) => (
|
|
<a class="sidebar-post" href={post.href}>
|
|
{post.cover ? (
|
|
<img src={post.cover} alt="" loading="lazy" />
|
|
) : (
|
|
<span class="post-thumb-fallback" aria-hidden="true"></span>
|
|
)}
|
|
<span>
|
|
<strong>{post.title}</strong>
|
|
<time datetime={post.date.toISOString()}>{post.dateText}</time>
|
|
</span>
|
|
</a>
|
|
))}
|
|
</div>
|
|
</section>
|
|
|
|
<SiteInfoCard posts={posts} />
|
|
</div>
|
|
</aside>
|
|
|
|
<script is:inline>
|
|
(() => {
|
|
const dayNames = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
|
const pad = (value) => String(value).padStart(2, '0');
|
|
const percent = (used, total) => Math.min(100, Math.max(0, (used / total) * 100));
|
|
const setText = (id, value) => {
|
|
const el = document.getElementById(id);
|
|
if (el) el.textContent = value;
|
|
};
|
|
|
|
function dayOfYear(date) {
|
|
const start = new Date(date.getFullYear(), 0, 1);
|
|
return Math.floor((date - start) / 86400000) + 1;
|
|
}
|
|
|
|
function weekOfYear(date) {
|
|
const firstDay = new Date(date.getFullYear(), 0, 1);
|
|
const pastDays = Math.floor((date - firstDay) / 86400000);
|
|
return Math.ceil((pastDays + firstDay.getDay() + 1) / 7);
|
|
}
|
|
|
|
function renderCalendar(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', '万千风景,五历如一');
|
|
|
|
const grid = document.getElementById('calendar-grid');
|
|
if (!grid) return;
|
|
|
|
const year = now.getFullYear();
|
|
const month = now.getMonth();
|
|
const first = new Date(year, month, 1).getDay();
|
|
const days = new Date(year, month + 1, 0).getDate();
|
|
const previousDays = new Date(year, month, 0).getDate();
|
|
const cells = [];
|
|
|
|
for (let i = 0; i < 42; i += 1) {
|
|
const dayNumber = i - first + 1;
|
|
const isCurrent = dayNumber >= 1 && dayNumber <= days;
|
|
const displayDay = isCurrent
|
|
? dayNumber
|
|
: dayNumber < 1
|
|
? previousDays + dayNumber
|
|
: dayNumber - days;
|
|
const isToday = isCurrent && displayDay === now.getDate();
|
|
cells.push(`<span class="${isCurrent ? '' : 'muted'} ${isToday ? 'today' : ''}">${displayDay}</span>`);
|
|
}
|
|
|
|
grid.innerHTML = cells.join('');
|
|
}
|
|
|
|
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 yearStart = new Date(year, 0, 1);
|
|
const yearEnd = new Date(year + 1, 0, 1);
|
|
const monthStart = new Date(year, now.getMonth(), 1);
|
|
const monthEnd = new Date(year, now.getMonth() + 1, 1);
|
|
const weekStart = new Date(year, now.getMonth(), now.getDate() - ((now.getDay() + 6) % 7));
|
|
const weekEnd = new Date(weekStart.getFullYear(), weekStart.getMonth(), weekStart.getDate() + 7);
|
|
|
|
setText('new-year-days', String(daysToEve));
|
|
setText('new-year-date', `${nextEve.getFullYear()}-${pad(nextEve.getMonth() + 1)}-${pad(nextEve.getDate())}`);
|
|
|
|
const rows = [
|
|
['year', yearStart, yearEnd],
|
|
['month', monthStart, monthEnd],
|
|
['week', weekStart, weekEnd],
|
|
];
|
|
|
|
rows.forEach(([name, start, end]) => {
|
|
const total = end - start;
|
|
const used = now - start;
|
|
const left = Math.max(0, Math.ceil((end - now) / 86400000));
|
|
const bar = document.getElementById(`${name}-progress`);
|
|
if (bar) bar.value = percent(used, total);
|
|
setText(`${name}-left`, `还剩${left}天`);
|
|
});
|
|
}
|
|
|
|
function initSidebar() {
|
|
const now = new Date();
|
|
renderCalendar(now);
|
|
renderSchedule(now);
|
|
}
|
|
|
|
initSidebar();
|
|
})();
|
|
</script>
|
|
<script is:inline src={siteConfig.cdn.jqueryJs}></script>
|
|
<script is:inline src={siteConfig.cdn.welcomeMessageJs}></script>
|
|
<script is:inline src="/js/weather.js"></script>
|