97 lines
3.8 KiB
TypeScript
97 lines
3.8 KiB
TypeScript
import { headers } from 'next/headers'
|
|
import Link from 'next/link'
|
|
import { CreateSceneButton } from '@/components/save-button'
|
|
import type { SceneMeta } from '@/components/scene-loader'
|
|
import { getPascalHostIdentity } from '@/lib/pascal-host-identity'
|
|
import { getSceneOperations } from '@/lib/scene-store-server'
|
|
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
async function fetchScenes(): Promise<SceneMeta[]> {
|
|
const identity = getPascalHostIdentity(await headers())
|
|
if (!identity.allowed) return []
|
|
const operations = await getSceneOperations()
|
|
return operations.listScenes({ limit: 50, ownerId: identity.userId ?? undefined })
|
|
}
|
|
|
|
function formatDate(iso: string): string {
|
|
try {
|
|
return new Date(iso).toLocaleString('zh-CN', { hour12: false })
|
|
} catch {
|
|
return iso
|
|
}
|
|
}
|
|
|
|
export default async function ScenesPage() {
|
|
const scenes = await fetchScenes()
|
|
|
|
return (
|
|
<div className="min-h-screen bg-background">
|
|
<header className="sticky top-0 z-10 border-border border-b bg-background/95 backdrop-blur">
|
|
<div className="container mx-auto flex items-center justify-between gap-4 px-6 py-4">
|
|
<nav className="flex items-center gap-4 text-sm">
|
|
<Link
|
|
className="text-muted-foreground transition-colors hover:text-foreground"
|
|
href="/"
|
|
>
|
|
首页
|
|
</Link>
|
|
<span className="text-muted-foreground">/</span>
|
|
<span className="font-medium text-foreground">场景</span>
|
|
</nav>
|
|
<CreateSceneButton />
|
|
</div>
|
|
</header>
|
|
|
|
<main className="container mx-auto max-w-5xl px-6 py-12">
|
|
<h1 className="mb-2 font-bold text-3xl">我的场景</h1>
|
|
<p className="mb-8 text-muted-foreground text-sm">
|
|
{scenes.length === 0 ? '还没有场景,创建一个开始设计吧。' : `共 ${scenes.length} 个场景`}
|
|
</p>
|
|
|
|
{scenes.length === 0 ? (
|
|
<div className="rounded-xl border border-border/60 border-dashed bg-background p-12 text-center">
|
|
<p className="text-muted-foreground text-sm">尚未保存任何场景。</p>
|
|
<div className="mt-4 flex justify-center">
|
|
<CreateSceneButton />
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<ul className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
{scenes.map((scene) => (
|
|
<li key={scene.id}>
|
|
<Link
|
|
className="group block rounded-xl border border-border/60 bg-background p-4 transition-colors hover:border-border hover:bg-accent/30"
|
|
href={`/scene/${scene.id}`}
|
|
>
|
|
<div className="flex aspect-video items-center justify-center overflow-hidden rounded-lg bg-accent/30">
|
|
{scene.thumbnailUrl ? (
|
|
// eslint-disable-next-line @next/next/no-img-element
|
|
<img
|
|
alt={scene.name}
|
|
className="h-full w-full object-cover"
|
|
src={scene.thumbnailUrl}
|
|
/>
|
|
) : (
|
|
<span className="text-muted-foreground text-xs">暂无缩略图</span>
|
|
)}
|
|
</div>
|
|
<div className="mt-3">
|
|
<h2 className="truncate font-semibold text-sm group-hover:text-foreground">
|
|
{scene.name}
|
|
</h2>
|
|
<div className="mt-1 flex items-center justify-between text-muted-foreground text-xs">
|
|
<span>{scene.nodeCount} 个元素</span>
|
|
<time dateTime={scene.updatedAt}>{formatDate(scene.updatedAt)}</time>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|