Files
solo-company-feed/app/page.tsx
爱喝水的木子 bfdf4843e1 OPC
2026-03-13 16:28:51 +08:00

144 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { getDb } from "@/lib/mongo";
import { PostCard } from "@/components/PostCard";
import { Post } from "@/types/post";
import { buildSearchFilter } from "@/lib/search";
export const dynamic = "force-dynamic";
const PAGE_SIZE = 10;
async function fetchPosts(params: {
tag?: string;
q?: string;
page: number;
}): Promise<{ posts: Post[]; total: number; page: number; totalPages: number }> {
const db = await getDb();
const filters: Record<string, unknown>[] = [];
if (params.tag) {
filters.push({ tags: params.tag });
}
const searchFilter = buildSearchFilter(params.q);
if (searchFilter) {
filters.push(searchFilter as Record<string, unknown>);
}
const filter = filters.length > 0 ? { $and: filters } : {};
const total = await db.collection("posts").countDocuments(filter);
const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
const page = Math.min(Math.max(params.page, 1), totalPages);
const docs = await db
.collection("posts")
.find(filter, { projection: { markdown: 0 } })
.sort({ createdAt: -1 })
.skip((page - 1) * PAGE_SIZE)
.limit(PAGE_SIZE)
.toArray();
return {
posts: docs.map((d: any) => ({
...d,
_id: d._id?.toString()
})),
total,
page,
totalPages
};
}
export default async function HomePage({
searchParams
}: {
searchParams?: { tag?: string; q?: string; page?: string };
}) {
const tag = searchParams?.tag?.trim();
const q = searchParams?.q?.trim();
const page = Number.parseInt(searchParams?.page || "1", 10) || 1;
const { posts, total, totalPages, page: currentPage } = await fetchPosts({ tag, q, page });
const buildHref = (targetPage: number) => {
const params = new URLSearchParams();
if (tag) params.set("tag", tag);
if (q) params.set("q", q);
if (targetPage > 1) params.set("page", String(targetPage));
const qs = params.toString();
return qs ? `/?${qs}` : "/";
};
return (
<div className="space-y-6">
<div className="rounded-2xl bg-gradient-to-r from-brand-500 to-brand-700 p-6 text-white shadow-lg">
<h1 className="text-2xl font-semibold"> · </h1>
<p className="mt-2 text-sm text-white/80">
Markdown · · · MongoDB Atlas
</p>
{tag ? (
<div className="mt-3 inline-flex items-center gap-2 rounded-full bg-white/15 px-3 py-1 text-xs font-medium">
<span>#{tag}</span>
<a
href="/"
className="rounded-full bg-white/20 px-2 py-1 text-white/90 hover:bg-white/30"
>
</a>
</div>
) : null}
</div>
<form action="/" method="get" className="flex flex-wrap items-center gap-3 rounded-2xl bg-white/80 p-4 shadow-sm ring-1 ring-slate-100">
{tag ? <input type="hidden" name="tag" value={tag} /> : null}
<input
name="q"
defaultValue={q || ""}
placeholder="搜索标题或正文"
className="min-w-[200px] flex-1 rounded-xl border border-slate-200 bg-white px-3 py-2 text-sm shadow-inner focus:border-brand-500 focus:outline-none"
/>
<button
type="submit"
className="rounded-full bg-brand-600 px-4 py-2 text-sm font-medium text-white shadow hover:bg-brand-700"
>
</button>
{q ? (
<a
href={tag ? `/?tag=${encodeURIComponent(tag)}` : "/"}
className="text-sm text-slate-500 hover:text-brand-600"
>
</a>
) : null}
<span className="text-xs text-slate-400"> {total} </span>
</form>
{posts.length === 0 ? (
<p className="rounded-xl bg-white/70 p-4 text-sm text-slate-500 ring-1 ring-slate-100">
</p>
) : (
<div className="grid gap-4">
{posts.map((post) => (
<PostCard key={post.slug} post={post} />
))}
</div>
)}
<div className="flex items-center justify-between text-sm text-slate-600">
<span>
{currentPage} / {totalPages}
</span>
<div className="flex items-center gap-2">
<a
href={buildHref(Math.max(1, currentPage - 1))}
className={`rounded-full px-3 py-1 ${currentPage <= 1 ? "pointer-events-none text-slate-300" : "bg-white/80 ring-1 ring-slate-200 hover:text-brand-600"}`}
>
</a>
<a
href={buildHref(Math.min(totalPages, currentPage + 1))}
className={`rounded-full px-3 py-1 ${currentPage >= totalPages ? "pointer-events-none text-slate-300" : "bg-white/80 ring-1 ring-slate-200 hover:text-brand-600"}`}
>
</a>
</div>
</div>
</div>
);
}