61 lines
2.0 KiB
TypeScript
61 lines
2.0 KiB
TypeScript
"use client"
|
|
import { gql, useQuery } from '@apollo/client'
|
|
import { use, useState } from 'react'
|
|
import BlogViewer from '@/components/blog-viewer'
|
|
import TableOfContents from '@/components/table-of-contents'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
const BLOG = gql`
|
|
query Blog($slug: String!) {
|
|
blogBySlug(slug: $slug) {
|
|
id
|
|
title
|
|
content
|
|
}
|
|
}
|
|
`
|
|
|
|
export default function Page({ params }: { params: Promise<{ slug: string }> }) {
|
|
// 使用 React.use() 来解包 params Promise
|
|
const resolvedParams = use(params);
|
|
const { slug } = resolvedParams;
|
|
const [contentReady, setContentReady] = useState(false);
|
|
|
|
const { data, loading, error } = useQuery(BLOG, {
|
|
variables: { slug },
|
|
})
|
|
|
|
// 条件渲染处理
|
|
if (loading) return <div className="flex items-center justify-center min-h-[400px]">Loading...</div>;
|
|
if (error) return <div className="flex items-center justify-center min-h-[400px] text-red-500">Error: {error.message}</div>;
|
|
if (!data?.blogBySlug) return <div className="flex items-center justify-center min-h-[400px]">Blog not found</div>;
|
|
|
|
const content = data.blogBySlug.content;
|
|
console.log('Blog content:', content);
|
|
|
|
return (
|
|
<div className="relative">
|
|
|
|
{contentReady && (
|
|
<div className={cn(
|
|
"fixed left-4 top-1/2 -translate-y-1/2 z-40 hidden xl:block",
|
|
"animate-in slide-in-from-left-8 fade-in duration-500 ease-out"
|
|
)}>
|
|
<TableOfContents />
|
|
</div>
|
|
)}
|
|
|
|
{/* 主内容区域 */}
|
|
<div className="container mx-auto py-8 max-w-4xl">
|
|
<main>
|
|
<h1 className="text-5xl font-bold my-12">{data.blogBySlug.title}</h1>
|
|
<BlogViewer
|
|
content={content}
|
|
onContentReady={() => setContentReady(true)}
|
|
/>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|