fix(archives): 置顶文章不再把整月拽到顶部——置顶浮出分组流自成首节

用户实测:某月有一篇置顶时,该月全部文章跟着上浮。根因:sortPosts
置顶优先后,年/月分组按顺次建桶——置顶文章让所在月提前建桶到顶部。
修复:置顶文章单独渲染为分组流之前的一节(带徽标与计数),年/月分组
只对非置顶文章按自然时序建桶,月份各归各位。

验证: pnpm astro build exit 0;产物断言分组头顺序=置顶,2026-11,2026-04,2026-03,2026-02 且置顶文仅出现一次;E2E 61/61

Spec: none (用户实测报告的排序分组 bug 紧急修复)

Co-authored-by: GLM-5.3
This commit is contained in:
wait4xandGLM-5.3 committed 2026-11-05 19:20:02 +08:00
1 parent 3ec7e48727
commit 9b11070dd4
1 file changed
+66 -1
+66 -1
View File
@@ -33,8 +33,13 @@ const TRACK_LABEL: Record<PostCategory, string> = {
};
const hrefOf = (id: string) => (locale === 'en' ? `/en/archives/${id}/` : `/archives/${id}/`);
// 置顶浮出分组流(2026-11-05 用户实测修复):置顶文章若留在分组流里,
// 它所在月会因「顺次建桶」被提前建到顶部,整月文章跟着上浮——
// 所以置顶单独一节渲染在年/月分组之前,分组只对非置顶文章按自然时序建桶。
const pinned = visible.filter((p) => p.data.pinned);
const flow = visible.filter((p) => !p.data.pinned);
const byYear = new Map<number, CollectionEntry<'posts'>[]>();
for (const post of visible) {
for (const post of flow) {
const year = post.data.pubDate.getUTCFullYear();
const bucket = byYear.get(year);
if (bucket) bucket.push(post);
@@ -107,6 +112,37 @@ const byMonth = (list: CollectionEntry<'posts'>[]) => {
</div>
) : fold ? (
<div class="post-list post-list-fold">
{pinned.length > 0 && (
<section class="month-block pinned-block">
<h3 class="month-head">
<span>{t('pinned_label', locale)}</span>
<span class="mono month-count">· {pinned.length} {units}</span>
</h3>
<ul>
{pinned.map((post) => (
<li>
<a class="row tap" href={hrefOf(post.id)}>
{post.data.category === 'reading' && (
<i
class="emo"
style={`background:${post.data.mood ?? 'var(--mood)'}`}
title="情绪色"
aria-hidden="true"
/>
)}
<span class="row-title">{titleOverrides?.[post.id] ?? post.data.title}</span>
<span class="pin" title={t('pinned_label', locale)}>{t('pinned_label', locale)}</span>
{post.data.category !== 'reading' &&
post.data.tags.map((tag) => <code class="tag">{`#${tag}`}</code>)}
<time class="row-date" datetime={post.data.pubDate.toISOString()}>
{mmdd(post.data.pubDate)}
</time>
</a>
</li>
))}
</ul>
</section>
)}
{[...byYear.entries()].map(([year, list], i) => (
<details class="year-fold" open={i === 0}>
<summary class="year-summary">
@@ -153,6 +189,33 @@ const byMonth = (list: CollectionEntry<'posts'>[]) => {
</div>
) : (
<div class="post-list">
{pinned.length > 0 && (
<section class="year-block pinned-block" aria-label={t('pinned_label', locale)}>
<h2 class="year">{t('pinned_label', locale)}</h2>
<ul>
{pinned.map((post) => (
<li>
<a class="row tap" href={hrefOf(post.id)}>
{post.data.category === 'reading' && (
<i
class="emo"
style={`background:${post.data.mood ?? 'var(--mood)'}`}
title="情绪色"
aria-hidden="true"
/>
)}
<span class="row-title">{titleOverrides?.[post.id] ?? post.data.title}</span>
{post.data.category !== 'reading' &&
post.data.tags.map((tag) => <code class="tag">{`#${tag}`}</code>)}
<time class="row-date" datetime={post.data.pubDate.toISOString()}>
{mmdd(post.data.pubDate)}
</time>
</a>
</li>
))}
</ul>
</section>
)}
{[...byYear.entries()].map(([year, list]) => (
<section class="year-block" aria-label={`${year} 年文章`}>
<h2 class="year">{year}</h2>
@@ -363,6 +426,8 @@ const byMonth = (list: CollectionEntry<'posts'>[]) => {
.year-count { font-size: 12px; color: var(--ink-4); }
.year-body { padding-bottom: var(--space-2); }
.month-block + .month-block { margin-top: var(--space-3); }
/* 置顶节与第一个年折叠块之间的间隔 */
.pinned-block + .year-fold { margin-top: var(--space-4); }
.month-head {
display: flex;
align-items: baseline;