193 lines
8.0 KiB
TypeScript
193 lines
8.0 KiB
TypeScript
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>
|
||
);
|
||
}
|