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} +
+ ) +}