From 6620be379b8d324856f573dbeb807d1929c411bd Mon Sep 17 00:00:00 2001 From: cc Date: Sat, 27 Jun 2026 16:53:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(blog):=20PostArticle=20=E5=85=A8=E6=96=87?= =?UTF-8?q?=E9=98=85=E8=AF=BB=E9=A1=B5=EF=BC=88=E7=BC=96=E8=BE=91/?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=BA=8C=E6=AC=A1=E7=A1=AE=E8=AE=A4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- Client/src/components/PostArticle.tsx | 123 ++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 Client/src/components/PostArticle.tsx diff --git a/Client/src/components/PostArticle.tsx b/Client/src/components/PostArticle.tsx new file mode 100644 index 0000000..3a9b7df --- /dev/null +++ b/Client/src/components/PostArticle.tsx @@ -0,0 +1,123 @@ +import { useEffect, useState } from 'react' +import { deletePost, getPost, readingTime, type Post } from '../api/posts' +import { Markdown } from './Markdown' + +interface PostArticleProps { + token: string + id: string + onBack: () => void + onEdit: (post: Post) => void + onDeleted: () => void +} + +function formatDate(secs: number): string { + return new Date(secs * 1000).toLocaleDateString('zh-CN', { + year: 'numeric', + month: 'short', + day: 'numeric', + }) +} + +export function PostArticle({ token, id, onBack, onEdit, onDeleted }: PostArticleProps) { + const [post, setPost] = useState(null) + const [status, setStatus] = useState<'loading' | 'ok' | 'notfound'>('loading') + const [confirmDel, setConfirmDel] = useState(false) + const [deleting, setDeleting] = useState(false) + + useEffect(() => { + let alive = true + setStatus('loading') + getPost(token, id) + .then((p) => { + if (alive) { + setPost(p) + setStatus('ok') + } + }) + .catch(() => { + if (alive) setStatus('notfound') + }) + return () => { + alive = false + } + }, [token, id]) + + const doDelete = async () => { + if (!post) return + setDeleting(true) + try { + await deletePost(token, post.id) + onDeleted() + } catch { + setDeleting(false) + setConfirmDel(false) + } + } + + if (status === 'loading') { + return
加载中…
+ } + + if (status === 'notfound' || !post) { + return ( +
+

这篇文章不存在或已被删除。

+ +
+ ) + } + + return ( +
+
+ +
+ + {confirmDel ? ( + + ) : ( + + )} +
+
+ +
+ {post.tag} +
+

{post.title}

+
+ {formatDate(post.createdAt)} · {readingTime(post.body)} read +
+ +
+ + {post.body} +
+ ) +}