Skip to content

Commit bdd576b

Browse files
committed
feat(blog): 添加文章不存在时的处理逻辑
引入 `notFound` 函数以处理文章不存在的情况。 在 `BlogPostPage` 中添加逻辑,当 `post` 为空时调用 `notFound()` 函数。 在 `BlogPage` 中添加获取文章列表的逻辑,并将文章列表作为 prop 传递给 `BlogPosts` 组件。 在 `BlogPost` 组件中,移除原有的数据获取逻辑,改为直接接受 `post` 作为 prop。 在 `BlogPosts` 组件中,移除原有的数据获取逻辑,改为直接接受 `posts` 作为 prop。
1 parent 02e61bd commit bdd576b

4 files changed

Lines changed: 44 additions & 139 deletions

File tree

frontend/app/(site)/blog/[id]/page.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Metadata } from "next";
2+
import { notFound } from "next/navigation";
23
import BlogPost from "@/components/BlogPost";
34
import type { PostDetail } from "@/types/api";
45

@@ -108,17 +109,19 @@ export default async function BlogPostPage({
108109
const { id } = await params;
109110
const post = await getPost(id);
110111

112+
if (!post) {
113+
notFound();
114+
}
115+
111116
return (
112117
<>
113-
{post && (
114-
<script
115-
type="application/ld+json"
116-
dangerouslySetInnerHTML={{
117-
__html: JSON.stringify(generateArticleJsonLd(post)),
118-
}}
119-
/>
120-
)}
121-
<BlogPost id={id} />
118+
<script
119+
type="application/ld+json"
120+
dangerouslySetInnerHTML={{
121+
__html: JSON.stringify(generateArticleJsonLd(post)),
122+
}}
123+
/>
124+
<BlogPost post={post} />
122125
</>
123126
);
124127
}

frontend/app/(site)/blog/page.tsx

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,43 @@
11
import { Metadata } from "next";
22
import BlogPosts from "@/components/BlogPosts";
3+
import type { PostSummary } from "@/types/api";
34

45
export const metadata: Metadata = {
56
title: "博客",
67
description: "ExquisiteCore的技术博客,分享全栈开发、游戏开发和技术思考",
78
};
89

9-
export default function BlogPage() {
10+
function getApiBaseUrl(): string {
11+
return process.env.INTERNAL_API_BASE_URL || "http://localhost:8080/api";
12+
}
13+
14+
async function getPosts(): Promise<PostSummary[]> {
15+
try {
16+
const response = await fetch(`${getApiBaseUrl()}/posts`, {
17+
next: { revalidate: 60 },
18+
});
19+
20+
if (response.ok) {
21+
const body = await response.json();
22+
const data = body?.data ?? body;
23+
if (Array.isArray(data)) return data;
24+
}
25+
} catch (error) {
26+
console.error("获取文章列表失败:", error);
27+
}
28+
return [];
29+
}
30+
31+
export default async function BlogPage() {
32+
const posts = await getPosts();
33+
1034
return (
1135
<div className="max-w-6xl mx-auto">
1236
<h1 className="text-4xl font-bold mb-4 text-center">博客</h1>
1337
<p className="text-center text-base-content/70 mb-12 max-w-2xl mx-auto">
1438
这里是我的技术博客,分享全栈开发、游戏开发和各种有趣的技术探索
1539
</p>
16-
<BlogPosts />
40+
<BlogPosts posts={posts} />
1741
</div>
1842
);
1943
}

frontend/components/BlogPost.tsx

Lines changed: 2 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
'use client';
22

3-
import { useState, useEffect } from 'react';
4-
import http from '@/lib/axios';
53
import MarkdownRenderer from './MarkdownRenderer';
64
import type { PostDetail } from '@/types/api';
75

@@ -15,70 +13,10 @@ function formatDate(dateString: string) {
1513
}
1614

1715
interface BlogPostProps {
18-
id: string;
16+
post: PostDetail;
1917
}
2018

21-
export default function BlogPost({ id }: BlogPostProps) {
22-
const [post, setPost] = useState<PostDetail | null>(null);
23-
const [loading, setLoading] = useState(true);
24-
const [error, setError] = useState<string | null>(null);
25-
26-
useEffect(() => {
27-
async function fetchPost() {
28-
try {
29-
setLoading(true);
30-
const response = await http.get<PostDetail>(`/posts/id/${id}`, undefined, {
31-
withToken: false,
32-
});
33-
34-
if (response && response.id) {
35-
setPost(response);
36-
} else {
37-
console.error('API响应格式不符合预期:', response);
38-
setError('文章不存在');
39-
}
40-
} catch (err) {
41-
console.error(`获取文章 ${id} 失败:`, err);
42-
setError('获取文章失败');
43-
} finally {
44-
setLoading(false);
45-
}
46-
}
47-
48-
fetchPost();
49-
}, [id]);
50-
51-
if (loading) {
52-
return (
53-
<div className="py-12 text-center">
54-
<span className="loading loading-spinner loading-lg"></span>
55-
</div>
56-
);
57-
}
58-
59-
if (error || !post) {
60-
return (
61-
<div className="py-12 text-center">
62-
<div className="alert alert-error shadow-lg max-w-md mx-auto">
63-
<svg
64-
xmlns="http://www.w3.org/2000/svg"
65-
className="stroke-current shrink-0 h-6 w-6"
66-
fill="none"
67-
viewBox="0 0 24 24"
68-
>
69-
<path
70-
strokeLinecap="round"
71-
strokeLinejoin="round"
72-
strokeWidth="2"
73-
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
74-
/>
75-
</svg>
76-
<span>{error || '文章不存在或已被删除'}</span>
77-
</div>
78-
</div>
79-
);
80-
}
81-
19+
export default function BlogPost({ post }: BlogPostProps) {
8220
const coverImage = post.cover_images && post.cover_images.length > 0 ? post.cover_images[0] : null;
8321

8422
return (

frontend/components/BlogPosts.tsx

Lines changed: 4 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
'use client';
2-
3-
import { useState, useEffect } from 'react';
41
import Link from 'next/link';
5-
import http from '@/lib/axios';
62
import type { PostSummary } from '@/types/api';
73

84
function formatDate(dateString: string) {
@@ -14,67 +10,11 @@ function formatDate(dateString: string) {
1410
});
1511
}
1612

17-
export default function BlogPosts() {
18-
const [posts, setPosts] = useState<PostSummary[]>([]);
19-
const [loading, setLoading] = useState(true);
20-
const [error, setError] = useState<string | null>(null);
21-
22-
useEffect(() => {
23-
async function fetchPosts() {
24-
try {
25-
setLoading(true);
26-
const response = await http.get<PostSummary[]>('/posts', undefined, {
27-
withToken: false,
28-
});
29-
30-
if (Array.isArray(response)) {
31-
setPosts(response);
32-
} else {
33-
console.error('API响应格式不符合预期:', response);
34-
setError('获取文章列表失败');
35-
}
36-
} catch (err) {
37-
console.error('获取文章列表失败:', err);
38-
setError('获取文章列表失败,请稍后重试');
39-
} finally {
40-
setLoading(false);
41-
}
42-
}
43-
44-
fetchPosts();
45-
}, []);
46-
47-
if (loading) {
48-
return (
49-
<div className="py-12 text-center">
50-
<span className="loading loading-spinner loading-lg"></span>
51-
</div>
52-
);
53-
}
54-
55-
if (error) {
56-
return (
57-
<div className="py-12 text-center">
58-
<div className="alert alert-error shadow-lg max-w-md mx-auto">
59-
<svg
60-
xmlns="http://www.w3.org/2000/svg"
61-
className="stroke-current shrink-0 h-6 w-6"
62-
fill="none"
63-
viewBox="0 0 24 24"
64-
>
65-
<path
66-
strokeLinecap="round"
67-
strokeLinejoin="round"
68-
strokeWidth="2"
69-
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
70-
/>
71-
</svg>
72-
<span>{error}</span>
73-
</div>
74-
</div>
75-
);
76-
}
13+
interface BlogPostsProps {
14+
posts: PostSummary[];
15+
}
7716

17+
export default function BlogPosts({ posts }: BlogPostsProps) {
7818
if (posts.length === 0) {
7919
return (
8020
<div className="py-12 text-center">

0 commit comments

Comments
 (0)