fix(polish): en 首页按 zh 全结构重写——PostList locale 化与版面平权
- PostList 组件加 locale prop(轨道标签走字典、en 链接 /en/ 前缀) - en/index 镜像 zh 结构:名片区三图标(GitHub/RSS/邮箱)+ 文章列表 + 最新影像(latest-head space-between + 4 列瓷砖网格,修复上轮 粘连与整宽竖排两处硬伤)+ 热力图 + 项目墙,区块顺序与 zh 一致 - 闭环截图:zh 滚动至热力图锚点(修复验证缺口) 验证: pnpm astro build 21 页 exit 0;node tests/e2e.mjs 30/30 PASS Spec: 2026-10-25_fix-site-polish Co-authored-by: GLM-5.3
This commit is contained in:
1 parent
ebafa415b2
commit
86e8009f34
2 files changed
+144
-66
No files matched your search
+131
-62
@@ -1,66 +1,91 @@
|
||||
---
|
||||
// /en/ 首页:英文 UI + 双语内容(postsEn 优先,缺则中文回退)
|
||||
// /en/ 首页:与 zh 版结构逐区对齐(名片区/文章列表/最新影像/热力图/项目墙)
|
||||
import { Image } from 'astro:assets';
|
||||
import { getCollection } from 'astro:content';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { t } from '../../i18n/ui';
|
||||
import IdCard from '../../components/IdCard.astro';
|
||||
import { Image } from 'astro:assets';
|
||||
const photos = (await getCollection('photos')).slice(0, 4);
|
||||
import PostList from '../../components/PostList.astro';
|
||||
import ProjectWall from '../../components/geek/ProjectWall.astro';
|
||||
import Heatmap from '../../components/geek/Heatmap.astro';
|
||||
import { POST_TRACKS, trackOf } from '../../content.config';
|
||||
import { t } from '../../i18n/ui';
|
||||
|
||||
const en = await getCollection('postsEn');
|
||||
const zh = await getCollection('posts');
|
||||
const bySlug = new Map(en.map((e) => [e.id, e]));
|
||||
const posts = zh
|
||||
.filter(({ data }) => !data.draft)
|
||||
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())
|
||||
.map((p) => ({ zh: p, en: bySlug.get(p.id) }));
|
||||
|
||||
const years = [...new Set(posts.map((p) => p.zh.data.pubDate.getFullYear()))].sort((a, b) => b - a);
|
||||
const t9 = (cat: keyof typeof POST_TRACKS) => trackOf(cat);
|
||||
const posts = (await getCollection('posts', ({ data }) => !data.draft)).sort(
|
||||
(a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime(),
|
||||
);
|
||||
const photos = (await getCollection('photos')).slice(0, 4);
|
||||
---
|
||||
|
||||
<BaseLayout title="wait4x" description="wait4x's blog: reading · code · photography" track="tech">
|
||||
<h1 class="visually-hidden">wait4x</h1>
|
||||
<BaseLayout title="wait4x" description="wait4x's blog: reading · code · photography">
|
||||
<IdCard>
|
||||
<a href="https://github.com/wait4xx" rel="noopener noreferrer" aria-label="GitHub">↗</a>
|
||||
<a href="/en/rss.xml" aria-label="RSS">RSS</a>
|
||||
<div class="socials">
|
||||
<a
|
||||
class="tap social"
|
||||
href="https://github.com/wait4xx"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="GitHub"
|
||||
title="GitHub"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true">
|
||||
<path
|
||||
d="M12 2C6.48 2 2 6.58 2 12.25c0 4.53 2.87 8.37 6.84 9.73.5.1.68-.22.68-.49 0-.24-.01-.88-.01-1.73-2.78.62-3.37-1.37-3.37-1.37-.45-1.18-1.11-1.5-1.11-1.5-.91-.63.07-.62.07-.62 1 .07 1.53 1.06 1.53 1.06.89 1.57 2.34 1.12 2.91.85.09-.66.35-1.11.63-1.37-2.22-.26-4.56-1.14-4.56-5.07 0-1.12.39-2.03 1.03-2.75-.1-.26-.45-1.3.1-2.7 0 0 .84-.28 2.75 1.05a9.3 9.3 0 0 1 5 0c1.91-1.33 2.75-1.05 2.75-1.05.55 1.4.2 2.44.1 2.7.64.72 1.03 1.63 1.03 2.75 0 3.94-2.34 4.8-4.57 5.06.36.32.68.94.68 1.9 0 1.37-.01 2.47-.01 2.81 0 .27.18.6.69.49A10.26 10.26 0 0 0 22 12.25C22 6.58 17.52 2 12 2Z"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="tap social" href="/en/rss.xml" aria-label="RSS feed" title="RSS feed">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="16"
|
||||
height="16"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.8"
|
||||
stroke-linecap="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M4 11.5a8.5 8.5 0 0 1 8.5 8.5M4 4.5a15.5 15.5 0 0 1 15.5 15.5"></path>
|
||||
<circle cx="5.4" cy="18.6" r="1.5" fill="currentColor" stroke="none"></circle>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="tap social" href="mailto:admin@wait4x.online" aria-label="Email" title="Email">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="16"
|
||||
height="16"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.8"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<rect x="3" y="5.5" width="18" height="13" rx="2.5"></rect>
|
||||
<path d="m4 7.5 8 5.5 8-5.5"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</IdCard>
|
||||
{years.map((year) => (
|
||||
<section class="latest" aria-label={t('latest_photos', 'en')}>
|
||||
|
||||
<PostList {posts} locale="en" />
|
||||
|
||||
<section class="latest" aria-label={t('latest_photos', 'en')}>
|
||||
<div class="latest-head">
|
||||
<span class="lab">{t('latest_photos', 'en')}</span>
|
||||
<a href="/en/gallery/">{t('enter_gallery', 'en')}</a>
|
||||
<div class="strip">
|
||||
{photos.map((p) => (<a href="/en/gallery/"><Image src={p.data.image} alt={p.data.alt ?? p.data.title} widths={[160, 320]} sizes="20vw" loading="lazy" /></a>))}
|
||||
</div>
|
||||
</section>
|
||||
<section class="year">
|
||||
<h2 class="y-label">{year}</h2>
|
||||
{(['reading', 'tech', 'photo'] as const).map((cat) => {
|
||||
const list = posts.filter((p) => p.zh.data.category === cat && p.zh.data.pubDate.getFullYear() === year);
|
||||
return list.length ? (
|
||||
<>
|
||||
<h3 class="track">{t(cat === 'reading' ? 'category_reading' : cat === 'tech' ? 'category_tech' : 'category_photo', 'en')}</h3>
|
||||
<ul class="plist">
|
||||
{list.map(({ zh: p, en: e }) => (
|
||||
<li>
|
||||
<a href={`/en/archives/${p.id}/`} class="plink">
|
||||
<span class="ptitle" style={p.data.mood ? `--mood:${p.data.mood}` : undefined}>
|
||||
{p.data.mood && <i class="mdot" aria-hidden="true" />}
|
||||
{e?.data.title ?? p.data.title}
|
||||
</span>
|
||||
{!e && <i class="fall">zh</i>}
|
||||
<time class="pdate">{p.data.pubDate.toISOString().slice(5, 10)}</time>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
) : null;
|
||||
})}
|
||||
</section>
|
||||
))}
|
||||
<a class="tap more" href="/en/gallery/">{t('enter_gallery', 'en')}</a>
|
||||
</div>
|
||||
<div class="thumbs">
|
||||
{photos.map((photo) => (
|
||||
<a class="thumb" href="/en/gallery/" aria-label={`Gallery: ${photo.data.title}`}>
|
||||
<Image
|
||||
src={photo.data.image}
|
||||
alt={photo.data.alt ?? photo.data.title}
|
||||
widths={[180, 360]}
|
||||
sizes="(min-width: 820px) 20vw, 42vw"
|
||||
/>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Heatmap
|
||||
labels={{
|
||||
title: t('heatmap_title', 'en'),
|
||||
@@ -70,17 +95,61 @@ const t9 = (cat: keyof typeof POST_TRACKS) => trackOf(cat);
|
||||
note: t('heatmap_note', 'en'),
|
||||
}}
|
||||
/>
|
||||
<ProjectWall />
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.year { margin-top: var(--space-5); }
|
||||
.y-label { font-family: var(--font-mono); font-size: 20px; color: var(--ink-1); }
|
||||
.track { font-size: 13px; color: var(--ink-3); margin: var(--space-3) 0 var(--space-2); letter-spacing: 0.06em; }
|
||||
.plist { list-style: none; padding: 0; display: grid; gap: var(--space-1); }
|
||||
.plink { display: flex; align-items: baseline; gap: var(--space-2); padding-block: var(--space-1); }
|
||||
.plink:hover .ptitle { color: var(--accent); }
|
||||
.ptitle { color: var(--ink-1); transition: color var(--dur) var(--ease); }
|
||||
.mdot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--mood, var(--ink-4)); margin-right: 6px; }
|
||||
.fall { font-family: var(--font-mono); font-size: 10px; color: var(--ink-4); border: 1px solid var(--line); border-radius: 3px; padding: 0 3px; font-style: normal; }
|
||||
.pdate { margin-left: auto; font-family: var(--font-mono); font-size: 12px; color: var(--ink-3); }
|
||||
.socials {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
.social {
|
||||
color: var(--ink-3);
|
||||
border-radius: 8px;
|
||||
transition: color var(--dur) var(--ease);
|
||||
}
|
||||
.social:hover { color: var(--accent); }
|
||||
|
||||
.latest { margin-top: var(--space-6); }
|
||||
.latest-head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
.lab {
|
||||
font-size: 12.5px;
|
||||
color: var(--ink-4);
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.more {
|
||||
font-size: 13px;
|
||||
color: var(--accent);
|
||||
padding-inline: var(--space-2);
|
||||
transition: color var(--dur) var(--ease);
|
||||
}
|
||||
.more:hover { color: var(--ink-1); }
|
||||
.thumbs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--space-2);
|
||||
margin-top: var(--space-3);
|
||||
}
|
||||
.thumb {
|
||||
aspect-ratio: 1;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.thumb :global(img) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform var(--dur) var(--ease);
|
||||
}
|
||||
.thumb:hover :global(img) { transform: scale(1.03); }
|
||||
@media (min-width: 820px) {
|
||||
.thumbs { grid-template-columns: repeat(4, 1fr); }
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user