lovart3/app/lovart/news/page.tsx
Your Name d089d3a37d
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: continue lovart rebuild with public data integration
2026-02-12 12:03:35 +08:00

193 lines
8.0 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 Link from "next/link";
import { Calendar, Newspaper, PlayCircle, TrendingUp } from "lucide-react";
import { MarketingShell } from "@/components/lovart/marketing-shell";
import { Reveal } from "@/components/lovart/reveal";
import {
getNavLinks,
getPublicFeaturedImages,
getPublicFeaturedRanking,
getSiteSettings,
getTutorialVideos,
} from "@/lib/db/queries";
function formatDate(value: Date | null | undefined) {
if (!value) {
return "最近更新";
}
return new Intl.DateTimeFormat("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
}).format(new Date(value));
}
export default async function LovartNewsPage() {
const [settings, navLinks, videos, featuredImages, ranking] = await Promise.all([
getSiteSettings(),
getNavLinks(),
getTutorialVideos(),
getPublicFeaturedImages(12),
getPublicFeaturedRanking(6),
]);
const [headline, ...restVideos] = videos;
return (
<MarketingShell
active="news"
bannerText={settings.bannerEnabled ? settings.bannerText : null}
navLinks={navLinks}
siteLogo={settings.siteLogo || settings.siteLogoDark}
siteName={settings.siteName || "AI Chatbot"}
stats={[
{ label: "新闻/教程", value: String(videos.length) },
{ label: "精选更新", value: String(featuredImages.length) },
{ label: "榜单作者", value: String(ranking.length) },
]}
>
<Reveal>
<section className="rounded-3xl border border-white/15 bg-white/[0.04] p-6 sm:p-10">
<p className="inline-flex items-center gap-2 rounded-full border border-white/20 bg-white/5 px-3 py-1 text-xs text-zinc-200">
<Newspaper className="h-3.5 w-3.5 text-cyan-300" />
News & Tutorials Feed
</p>
<h1 className="mt-4 text-3xl font-semibold text-white sm:text-5xl">Lovart 新闻中心</h1>
<p className="mt-4 max-w-2xl text-sm text-zinc-300 sm:text-base">
页面内容实时读取后台教程管理与精选图流,运营发布后无需改代码即可同步到前台。
</p>
</section>
</Reveal>
<Reveal delay={0.06}>
<section className="mt-8 grid gap-4 lg:grid-cols-[1.3fr_1fr]">
<article className="rounded-2xl border border-white/15 bg-black/30 p-5">
<div className="mb-2 inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-2.5 py-1 text-xs text-zinc-300">
<Calendar className="h-3.5 w-3.5" />
{formatDate(headline?.updatedAt || headline?.createdAt)}
</div>
<h2 className="text-2xl font-semibold text-white">
{headline?.title || "等待发布第一条动态"}
</h2>
<p className="mt-3 text-sm leading-6 text-zinc-300">
{headline?.description ||
"你可以在后台添加教程视频、标题和描述,前台会自动展示为新闻中心头条。"}
</p>
{headline ? (
<a
href={headline.videoUrl}
target="_blank"
rel="noreferrer"
className="mt-5 inline-flex items-center gap-2 rounded-xl bg-white px-4 py-2 text-sm font-semibold text-zinc-900 hover:bg-zinc-200"
>
<PlayCircle className="h-4 w-4" />
观看头条内容
</a>
) : null}
</article>
<article className="rounded-2xl border border-white/15 bg-black/30 p-5">
<h3 className="mb-3 text-lg font-semibold text-white">精选更新流</h3>
{featuredImages.length > 0 ? (
<div className="grid grid-cols-3 gap-2">
{featuredImages.slice(0, 9).map((image) => (
<img
key={image.id}
src={image.thumbnailUrl || image.originalUrl}
alt={image.prompt || "featured"}
className="h-20 w-full rounded-lg object-cover"
/>
))}
</div>
) : (
<p className="text-sm text-zinc-300">暂无精选更新</p>
)}
</article>
</section>
</Reveal>
<Reveal delay={0.12}>
<section className="mt-8">
<h2 className="mb-4 text-xl font-semibold text-white">全部新闻</h2>
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
{restVideos.length > 0 ? (
restVideos.map((video, index) => (
<article key={video.id} className="rounded-2xl border border-white/15 bg-black/30 p-4">
<div className="mb-3 flex items-center justify-between text-xs text-zinc-400">
<span className="inline-flex items-center gap-1">
<Calendar className="h-3.5 w-3.5" />
{formatDate(video.updatedAt || video.createdAt)}
</span>
<span>#{String(index + 1).padStart(2, "0")}</span>
</div>
<p className="line-clamp-2 text-base font-medium text-zinc-100">{video.title}</p>
<p className="mt-2 line-clamp-3 text-sm text-zinc-300">{video.description || "暂无描述"}</p>
<a
href={video.videoUrl}
target="_blank"
rel="noreferrer"
className="mt-4 inline-flex items-center gap-2 text-sm text-cyan-300 hover:text-cyan-200"
>
<PlayCircle className="h-4 w-4" />
立即观看
</a>
</article>
))
) : (
<div className="col-span-full rounded-2xl border border-dashed border-white/20 bg-white/[0.03] p-8 text-sm text-zinc-300">
暂无更多新闻内容,可在后台「教程设置」中继续发布。
</div>
)}
</div>
</section>
</Reveal>
{settings.rankingEnabled ? (
<Reveal delay={0.16}>
<section className="mt-8 rounded-2xl border border-white/15 bg-black/25 p-5">
<div className="mb-4 flex items-center gap-2">
<TrendingUp className="h-4 w-4 text-cyan-300" />
<h2 className="text-lg font-semibold text-white">创作者热度榜</h2>
</div>
<div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
{ranking.length > 0 ? (
ranking.map((item, index) => (
<div
key={item.userId}
className="flex items-center justify-between rounded-lg border border-white/10 bg-white/[0.03] px-3 py-2 text-sm"
>
<span className="text-zinc-200">
{index + 1}. {item.userName || item.userEmail?.split("@")[0] || "未知用户"}
</span>
<span className="font-medium text-amber-200">{item.featuredCount}</span>
</div>
))
) : (
<p className="text-sm text-zinc-300">暂无榜单数据</p>
)}
</div>
</section>
</Reveal>
) : null}
<Reveal delay={0.2}>
<section className="mt-10 rounded-2xl border border-white/15 bg-black/25 p-5">
<h2 className="text-lg font-semibold text-white">下一步开发建议</h2>
<div className="mt-3 flex flex-wrap gap-3 text-sm text-zinc-300">
<Link className="rounded-lg border border-white/20 px-3 py-1.5 hover:border-white/40" href="/lovart">
回到首页调样式
</Link>
<Link className="rounded-lg border border-white/20 px-3 py-1.5 hover:border-white/40" href="/lovart/pricing">
定价页联动支付
</Link>
<Link className="rounded-lg border border-white/20 px-3 py-1.5 hover:border-white/40" href="/admin/settings">
去后台更新内容
</Link>
</div>
</section>
</Reveal>
</MarketingShell>
);
}