95 lines
3.5 KiB
TypeScript
95 lines
3.5 KiB
TypeScript
"use client";
|
|
|
|
import { use, useEffect, useState } from "react";
|
|
import { Link, useLocale } from "@/i18n/navigation";
|
|
import { apiFetch } from "@/app/lib/api-client";
|
|
import Footer from "@/app/components/Footer";
|
|
|
|
interface ContentItem {
|
|
slug: string;
|
|
title: string;
|
|
titleEn?: string;
|
|
subtitle: string;
|
|
subtitleEn?: string;
|
|
description: string;
|
|
descriptionEn?: string;
|
|
coverImage: string;
|
|
mediaUrl: string;
|
|
chapters?: { title: string; time?: string }[];
|
|
relatedProfiles?: string[];
|
|
durationMinutes?: number;
|
|
}
|
|
|
|
interface CityLite {
|
|
slug: string;
|
|
name: string;
|
|
nameEn?: string;
|
|
icon: string;
|
|
nomadsNow: number;
|
|
}
|
|
|
|
export default function VideoPage({ params }: { params: Promise<{ slug: string }> }) {
|
|
const { slug } = use(params);
|
|
const locale = useLocale();
|
|
const [item, setItem] = useState<ContentItem | null>(null);
|
|
const [cities, setCities] = useState<CityLite[]>([]);
|
|
|
|
useEffect(() => {
|
|
apiFetch<{ item: ContentItem; cities: CityLite[] }>(`/api/content/${slug}`)
|
|
.then((data) => {
|
|
setItem(data.item);
|
|
setCities(data.cities || []);
|
|
})
|
|
.catch(() => setItem(null));
|
|
}, [slug]);
|
|
|
|
if (!item) {
|
|
return <div className="min-h-screen bg-[#0f172a] p-10 text-gray-300">加载中...</div>;
|
|
}
|
|
|
|
const title = locale === "zh" ? item.title : item.titleEn || item.title;
|
|
const subtitle = locale === "zh" ? item.subtitle : item.subtitleEn || item.subtitle;
|
|
const description = locale === "zh" ? item.description : item.descriptionEn || item.description;
|
|
|
|
return (
|
|
<div className="min-h-screen bg-[#0f172a] text-white">
|
|
<main className="mx-auto max-w-6xl px-4 py-8 sm:px-6 sm:py-12">
|
|
<Link href="/" className="text-sm text-white/60 hover:text-white">← 返回首页</Link>
|
|
<div className="mt-6 overflow-hidden rounded-2xl bg-black shadow-2xl">
|
|
<video src={item.mediaUrl} poster={item.coverImage} controls playsInline className="aspect-video w-full bg-black object-contain" />
|
|
</div>
|
|
<div className="mt-8 grid gap-8 lg:grid-cols-[1fr_320px]">
|
|
<section>
|
|
<p className="text-sm font-semibold text-[#ff7a45]">个人访谈 / 现实纪录片</p>
|
|
<h1 className="mt-2 text-3xl font-bold sm:text-4xl">{title}</h1>
|
|
<p className="mt-2 text-lg text-white/60">{subtitle}</p>
|
|
<p className="mt-6 leading-8 text-white/75">{description}</p>
|
|
</section>
|
|
<aside className="rounded-2xl bg-white/10 p-5 backdrop-blur">
|
|
<h2 className="font-bold">章节</h2>
|
|
<div className="mt-4 space-y-3">
|
|
{(item.chapters || []).map((chapter) => (
|
|
<div key={chapter.title} className="rounded-xl bg-white/10 px-4 py-3">
|
|
<p className="text-sm">{chapter.title}</p>
|
|
<p className="mt-1 text-xs text-white/50">{chapter.time || "00:00"}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
<section className="mt-10">
|
|
<h2 className="mb-4 text-xl font-bold">片中城市</h2>
|
|
<div className="flex flex-wrap gap-3">
|
|
{cities.map((city) => (
|
|
<Link key={city.slug} href={`/city/${city.slug}`} className="rounded-full bg-white/10 px-4 py-2 text-sm hover:bg-white/20">
|
|
{city.icon} {locale === "zh" ? city.name : city.nameEn || city.name} · {city.nomadsNow} 人在此
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</section>
|
|
</main>
|
|
<Footer />
|
|
</div>
|
|
);
|
|
}
|