This commit is contained in:
1 parent
c1541b80df
commit
b4c7908029
97 files changed
+12403
-154
No files matched your search
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user