feat: 添加每日签到功能,新增帖子排序与社区面板优化

本次提交完成了多个核心功能迭代:
1.  新增用户每日签到系统,包含签到状态查询、连续天数统计、积分累计逻辑
2.  新增"新帖子"排序选项,替换原有的latest排序为新评论排序
3.  重构首页信息流布局,优化移动端与桌面端展示样式
4.  新增最新评论、最新注册用户模块,完善社区面板数据展示
5.  新增静态友情链接配置模块
6.  提取通用时间格式化工具函数,统一全站相对时间展示
7.  补充数据库签到模型与后端API接口,完善前后端交互链路
This commit is contained in:
2026-09-13 06:03:40 +08:00
parent 95921d24fa
commit 9bae6f2513
19 changed files with 790 additions and 280 deletions

View File

@@ -1,25 +1,11 @@
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";
/** 发帖/回复时间统一为相对时间:刚刚 / N分钟前 / N小时前 / N天前 / N个月前 / N年前 */
function formatRelative(dateStr: string) {
const t = new Date(dateStr).getTime();
const diff = Date.now() - t;
const min = 60_000,
hour = 60 * min,
day = 24 * hour;
if (diff < min) return "刚刚";
if (diff < hour) return `${Math.floor(diff / min)}分钟前`;
if (diff < day) return `${Math.floor(diff / hour)}小时前`;
if (diff < 30 * day) return `${Math.floor(diff / day)}天前`;
if (diff < 365 * day) return `${Math.floor(diff / (30 * day))}个月前`;
return `${Math.floor(diff / (365 * day))}年前`;
}
/**
* 帖子列表行(参考社区信息流样式):
* 大头像 + 加粗标题(徽章内联)+ 第二行「板块 · 作者 · 发布时间 ← 最后回复」+
@@ -45,11 +31,13 @@ export default function PostRow({
return (
<div
className="group relative flex items-center gap-3.5 sm:gap-4 px-4 sm:px-5 border-t transition-colors hover:bg-[var(--panel-2)] j13-fade-up"
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 ? 15 : 18,
paddingBottom: compact ? 15 : 18,
paddingTop: compact ? 11 : 18,
paddingBottom: compact ? 11 : 18,
animationDelay: `${Math.min(index, 9) * 35}ms`,
}}
>
@@ -59,7 +47,7 @@ export default function PostRow({
aria-label={post.user.nickname}
tabIndex={-1}
>
<Avatar name={post.user.nickname || post.user.username} size={compact ? 44 : 46} />
<Avatar name={post.user.nickname || post.user.username} size={compact ? 38 : 46} />
</Link>
<div className="flex-1 min-w-0">
@@ -72,14 +60,20 @@ export default function PostRow({
aria-label={post.title}
>
<h3
className="font-bold text-[16px] sm:text-[17px] leading-snug truncate transition-colors group-hover:text-[var(--accent)]"
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 mt-1.5 flex-wrap text-[13px]">
<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}`}
@@ -109,6 +103,11 @@ export default function PostRow({
<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>