This commit is contained in:
1 parent
c1541b80df
commit
b4c7908029
97 files changed
+12403
-154
No files matched your search
@@ -0,0 +1,234 @@
|
||||
import type { Metadata } from "next";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
import {
|
||||
Briefcase,
|
||||
MapPin,
|
||||
Mail,
|
||||
Code2,
|
||||
Gauge,
|
||||
Palette,
|
||||
Wrench,
|
||||
} from "lucide-react";
|
||||
import { Container, Section } from "@/components/ui/layout";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { buttonClassName } from "@/components/ui/button-variants";
|
||||
import { Reveal } from "@/components/motion/reveal";
|
||||
import { siteConfig } from "@/lib/site-config";
|
||||
|
||||
/**
|
||||
* 关于页渲染策略:ISR 缓存 60 秒。
|
||||
* 路由段配置必须是字面量(Next.js 会静态分析)。
|
||||
*/
|
||||
export const revalidate = 60;
|
||||
|
||||
type AboutPageProps = {
|
||||
params: Promise<{ locale: string }>;
|
||||
};
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: AboutPageProps): Promise<Metadata> {
|
||||
const { locale } = await params;
|
||||
const t = await getTranslations({ locale, namespace: "About" });
|
||||
|
||||
return {
|
||||
title: t("title"),
|
||||
description: t("description"),
|
||||
alternates: { canonical: `/${locale}/about` },
|
||||
openGraph: {
|
||||
type: "profile",
|
||||
title: t("title"),
|
||||
description: t("description"),
|
||||
url: `${siteConfig.url}/${locale}/about`,
|
||||
images: [{ url: siteConfig.ogImage }],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** 关注方向 —— 图标仅为装饰,语义由文本承载 */
|
||||
const FOCUS_AREAS = [
|
||||
{
|
||||
icon: Gauge,
|
||||
title: "Web 性能",
|
||||
description:
|
||||
"关注核心网页指标、渲染路径与打包体积,把「快」当作可度量的工程目标而不是感觉。",
|
||||
},
|
||||
{
|
||||
icon: Code2,
|
||||
title: "开发者体验",
|
||||
description:
|
||||
"好的工具应该让正确的事情变简单。投入最多的地方是构建流程、类型系统与文档。",
|
||||
},
|
||||
{
|
||||
icon: Palette,
|
||||
title: "设计系统",
|
||||
description:
|
||||
"把设计规范翻译成可执行的代码约束,让一致性由类型系统保证,而不是靠代码评审。",
|
||||
},
|
||||
{
|
||||
icon: Wrench,
|
||||
title: "工程化实践",
|
||||
description:
|
||||
"测试策略、CI/CD、可观测性——那些看不见但决定了项目能走多远的基建。",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export default async function AboutPage({ params }: AboutPageProps) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
|
||||
const t = await getTranslations("About");
|
||||
const { author } = siteConfig;
|
||||
|
||||
const facts = [
|
||||
{ icon: Briefcase, label: t("role"), value: author.jobTitle },
|
||||
{ icon: MapPin, label: t("location"), value: author.location },
|
||||
{ icon: Mail, label: t("email"), value: author.email },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 页头 */}
|
||||
<Section
|
||||
spacing="sm"
|
||||
className="border-b border-hairline-soft bg-surface-soft"
|
||||
>
|
||||
<Container size="narrow">
|
||||
<p className="text-micro-uppercase font-semibold uppercase tracking-[0.5px] text-steel">
|
||||
{t("eyebrow")}
|
||||
</p>
|
||||
<h1 className="mt-sm text-heading-1 font-semibold text-ink">
|
||||
{t("title")}
|
||||
</h1>
|
||||
<p className="mt-md max-w-[640px] text-subtitle text-steel text-balance-pretty">
|
||||
{t("description")}
|
||||
</p>
|
||||
</Container>
|
||||
</Section>
|
||||
|
||||
<Section spacing="default">
|
||||
<Container size="narrow">
|
||||
<div className="grid grid-cols-1 gap-xxl lg:grid-cols-[minmax(0,1fr)_280px]">
|
||||
{/* 主栏:简介 + 关注方向 */}
|
||||
<div className="flex flex-col gap-section">
|
||||
<section aria-labelledby="intro-heading">
|
||||
<h2
|
||||
id="intro-heading"
|
||||
className="text-heading-3 font-semibold text-ink"
|
||||
>
|
||||
{t("introTitle")}
|
||||
</h2>
|
||||
|
||||
<div className="mt-md flex flex-col gap-md text-body-md leading-[1.75] text-slate">
|
||||
<p>{author.bio}</p>
|
||||
<p>
|
||||
这个博客用来记录我在日常工作中遇到的问题和解决思路。
|
||||
我倾向于写下那些「当时找不到答案、后来自己想明白了」的内容,
|
||||
因为这类经验对别人往往也最有价值。
|
||||
</p>
|
||||
<p>
|
||||
写作风格上,我更在意准确而不是完整:与其罗列所有可能性,
|
||||
不如把一条路径讲透,并说清楚它为什么成立、在什么情况下会失效。
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section aria-labelledby="focus-heading">
|
||||
<h2
|
||||
id="focus-heading"
|
||||
className="text-heading-3 font-semibold text-ink"
|
||||
>
|
||||
{t("techTitle")}
|
||||
</h2>
|
||||
<p className="mt-xs text-body-sm text-steel">
|
||||
{t("techDescription")}
|
||||
</p>
|
||||
|
||||
<div className="mt-lg grid grid-cols-1 gap-md sm:grid-cols-2">
|
||||
{FOCUS_AREAS.map((area, index) => (
|
||||
<Reveal key={area.title} delay={index * 0.05}>
|
||||
<Card className="flex h-full flex-col gap-xs" padding="md">
|
||||
<area.icon
|
||||
className="size-5 text-brand-green-deep"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<h3 className="text-heading-5 font-semibold text-ink">
|
||||
{area.title}
|
||||
</h3>
|
||||
<p className="text-body-sm text-steel">
|
||||
{area.description}
|
||||
</p>
|
||||
</Card>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/* 侧栏:基本信息 + 联系方式 */}
|
||||
<aside className="flex flex-col gap-xl">
|
||||
<Card
|
||||
padding="md"
|
||||
variant="feature"
|
||||
className="flex flex-col gap-md"
|
||||
>
|
||||
<h2 className="text-heading-5 font-semibold text-ink">
|
||||
基本信息
|
||||
</h2>
|
||||
|
||||
<dl className="flex flex-col gap-sm">
|
||||
{facts.map((fact) => (
|
||||
<div key={fact.label} className="flex items-start gap-sm">
|
||||
<fact.icon
|
||||
className="mt-[2px] size-4 shrink-0 text-steel"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div className="min-w-0">
|
||||
<dt className="text-caption text-steel">
|
||||
{fact.label}
|
||||
</dt>
|
||||
<dd className="break-words text-body-sm text-ink">
|
||||
{fact.value}
|
||||
</dd>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</Card>
|
||||
|
||||
<Card padding="md" className="flex flex-col gap-sm">
|
||||
<h2 className="text-heading-5 font-semibold text-ink">
|
||||
{t("contactTitle")}
|
||||
</h2>
|
||||
<p className="text-body-sm text-steel">
|
||||
{t("contactDescription")}
|
||||
</p>
|
||||
|
||||
<ul className="mt-xs flex flex-col gap-xs">
|
||||
{siteConfig.social.map((item) => (
|
||||
<li key={item.label}>
|
||||
<a
|
||||
href={item.href}
|
||||
target={
|
||||
item.href.startsWith("mailto:") ? undefined : "_blank"
|
||||
}
|
||||
rel="noopener noreferrer"
|
||||
className={buttonClassName({
|
||||
variant: "secondary",
|
||||
size: "sm",
|
||||
className: "w-full justify-start",
|
||||
})}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Card>
|
||||
</aside>
|
||||
</div>
|
||||
</Container>
|
||||
</Section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user