Files
jiang13-bbs/frontend/components/PostRow.tsx
freefire 996a870015 feat: 新增站点公告系统与多项体验优化
此提交完成了以下核心功能与优化:
1. 新增站点公告全流程支持:包含公告模型、CRUD接口、管理后台页面、公告详情页与首页展示
2. 实现评论楼层化与回复嵌套:重构评论系统为树状结构,支持回复评论与层级限制
3. 添加用户在线状态追踪:异步更新用户最后活跃时间并做SQL限频
4. 优化用户体验:移动端浮动操作条、统一板块标签样式、用户页签到积分展示、注册即登录优化
5. 其他细节优化:SEO站点地图新增公告页、评论分页与统计、登录/注册表单增强等
2026-09-13 21:38:01 +08:00

142 lines
4.8 KiB
TypeScript

import Link from "next/link";
import { ArrowLeft } from "lucide-react";
import type { PostListItem } from "@/lib/api";
import { formatRelative } from "@/lib/format";
import LikeButton from "./LikeButton";
import Avatar from "./Avatar";
import { IconComment, IconEye } from "./Icons";
/**
* 帖子列表行(参考社区信息流样式):
* 大头像 + 加粗标题(徽章内联)+ 第二行「板块 · 作者 · 发布时间 ← 最后回复」+
* 右侧评论数等统计垂直居中。用于首页/板块页/搜索结果。
*/
export default function PostRow({
post,
boardHref,
index = 0,
compact = false,
showViews = true,
showLike = true,
}: {
post: PostListItem;
index?: number;
boardHref?: boolean;
compact?: boolean;
showViews?: boolean;
showLike?: boolean;
}) {
const lastReply = post.last_reply;
const replied = lastReply && new Date(lastReply.created_at).getTime() > new Date(post.created_at).getTime() + 60_000;
return (
<div
className={`group relative flex items-center px-4 sm:px-5 border-t transition-colors hover:bg-[var(--panel-2)] j13-fade-up ${
compact ? "gap-2.5 sm:gap-3" : "gap-3.5 sm:gap-4"
}`}
style={{
borderColor: "var(--line)",
paddingTop: compact ? 11 : 18,
paddingBottom: compact ? 11 : 18,
animationDelay: `${Math.min(index, 9) * 35}ms`,
}}
>
<Link
href={`/u/${post.user.id}`}
className="shrink-0 above-link"
aria-label={post.user.nickname}
tabIndex={-1}
>
<Avatar name={post.user.nickname || post.user.username} size={compact ? 38 : 46} />
</Link>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 min-w-0">
{post.pinned > 0 && <span className="badge-pin shrink-0">置顶</span>}
{post.recommended && <span className="badge-rec shrink-0">推荐</span>}
<Link
href={`/post/${post.id}`}
className="stretch-link min-w-0 flex-1"
aria-label={post.title}
>
<h3
className={`font-bold leading-snug truncate transition-colors group-hover:text-[var(--accent)] ${
compact ? "text-[15px]" : "text-[16px] sm:text-[17px]"
}`}
style={{ color: "var(--ink)" }}
>
{post.title}
</h3>
</Link>
</div>
<div
className={`meta relative z-[2] flex items-center gap-x-2 gap-y-1 flex-wrap ${
compact ? "mt-1 text-[12.5px]" : "mt-1.5 text-[13px]"
}`}
>
{boardHref && (
<Link href={`/board/${post.board.id}`} className="j13-board-tag">
{post.board.name}
</Link>
)}
<Link
href={`/u/${post.user.id}`}
className="font-semibold hover:text-[var(--accent)] transition-colors"
style={{ color: "var(--ink-2)" }}
>
{post.user.nickname}
</Link>
<span aria-hidden>·</span>
<span className="tabular-nums">{formatRelative(post.created_at)}</span>
{replied && (
<span className="inline-flex items-center gap-1 min-w-0">
<ArrowLeft size={13} className="shrink-0" aria-hidden />
<Link
href={`/u/${lastReply!.user.id}`}
className="font-medium hover:text-[var(--accent)] transition-colors truncate max-w-[8em]"
>
{lastReply!.user.nickname || lastReply!.user.username}
</Link>
<span className="tabular-nums shrink-0">{formatRelative(lastReply!.created_at)}</span>
</span>
)}
{!replied && post.comment_count === 0 && (
<span className="hidden sm:inline" aria-hidden>
· 暂无回复
</span>
)}
</div>
</div>
<div className="above-link flex items-center gap-3 sm:gap-4 shrink-0">
<Link
href={`/post/${post.id}`}
tabIndex={-1}
aria-label={`${post.comment_count} 条评论`}
className={`${showViews ? "hidden sm:inline-flex" : "inline-flex"} items-center gap-1.5 meta tabular-nums text-[13.5px] hover:text-[var(--accent)] transition-colors`}
>
<IconComment size={17} />
{post.comment_count}
</Link>
{showViews && (
<span
className="hidden md:inline-flex items-center gap-1.5 meta tabular-nums"
aria-label={`${post.view_count} 次浏览`}
>
<IconEye size={15} />
{post.view_count}
</span>
)}
{showLike && (
<LikeButton
postId={String(post.id)}
liked={post.liked}
likeCount={post.like_count}
variant="compact"
/>
)}
</div>
</div>
);
}