This commit is contained in:
z.ai committed 2026-10-08 11:09:37 +08:00
1 parent c1541b80df
commit b4c7908029
97 files changed
+12403 -154

No files matched your search

+79
View File
@@ -0,0 +1,79 @@
import { Link } from "@/i18n/navigation";
import { getLocale, getTranslations } from "next-intl/server";
import { CalendarDays, Clock } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { formatDate, toDateTimeAttribute } from "@/lib/utils";
import type { PostMeta } from "@/lib/posts/types";
/**
* 文章卡片(服务端组件)。
*
* DESIGN.md 的 card-base:白底 + 1px hairline 描边 + rounded-lg,
* 内部留白 {spacing.xl}。展示标题、日期、阅读时长、摘要与标签。
*/
export interface PostCardProps {
post: PostMeta;
/** 是否展示摘要;紧凑列表可关闭 */
showSummary?: boolean;
className?: string;
}
export async function PostCard({
post,
showSummary = true,
className,
}: PostCardProps) {
const t = await getTranslations("Post");
const locale = await getLocale();
return (
<article className={className}>
<Link
href={`/posts/${post.slug}`}
className="group flex h-full flex-col gap-sm rounded-lg border border-hairline bg-canvas p-xl transition-colors duration-150 hover:border-hairline-soft hover:bg-surface-soft active:bg-surface"
>
{/* 元信息:日期 + 阅读时长 */}
<div className="flex flex-wrap items-center gap-sm text-caption text-steel">
<span className="inline-flex items-center gap-[6px]">
<CalendarDays className="size-3.5" aria-hidden="true" />
<time dateTime={toDateTimeAttribute(post.date)}>
{formatDate(post.date, locale)}
</time>
</span>
<span aria-hidden="true" className="text-muted">
·
</span>
<span className="inline-flex items-center gap-[6px]">
<Clock className="size-3.5" aria-hidden="true" />
{t("readingTime", { minutes: post.readingMinutes })}
</span>
</div>
{/* 标题 */}
<h3 className="text-heading-4 font-semibold text-ink transition-colors duration-150 group-hover:text-brand-green-deep">
{post.title}
</h3>
{/* 摘要 */}
{showSummary ? (
<p className="flex-1 text-body-sm text-steel text-balance-pretty">
{post.summary}
</p>
) : null}
{/* 标签 */}
{post.tags && post.tags.length > 0 ? (
<ul className="mt-xs flex flex-wrap gap-xs">
{post.tags.map((tag) => (
<li key={tag}>
<Badge variant="tag">{tag}</Badge>
</li>
))}
</ul>
) : null}
</Link>
</article>
);
}