Files
admin/src/app/(root)/articles/[id]/article-editor.tsx
2026-06-09 16:37:40 +08:00

136 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import { Loader2 } from "lucide-react"
import dynamic from "next/dynamic"
import Link from "next/link"
import { useRouter } from "next/navigation"
import { useCallback, useEffect, useState } from "react"
import { toast } from "sonner"
import { createArticle, getArticle, updateArticle } from "@/actions/article"
import type { ArticleGroup } from "@/models/article-group"
const Editor = dynamic(() => import("@/components/editor/richTextEditor"), {
ssr: false,
loading: () => (
<div className="flex items-center justify-center h-64 text-muted-foreground">
...
</div>
),
})
interface ArticleEditorProps {
articleId: string
groups: ArticleGroup[]
}
export default function ArticleEditor({
articleId,
groups,
}: ArticleEditorProps) {
const router = useRouter()
const isNew = articleId === "new"
const [loading, setLoading] = useState(!isNew)
const [title, setTitle] = useState("")
const [initialContent, setInitialContent] = useState("")
const [groupId, setGroupId] = useState<string>(
groups.length > 0 ? String(groups[0].id) : "",
)
useEffect(() => {
if (!isNew) {
const id = Number(articleId)
if (Number.isNaN(id)) {
toast.error("无效的文章 ID")
router.push("/articles")
return
}
getArticle(id).then(resp => {
if (resp.success && resp.data) {
setTitle(resp.data.title || "")
setInitialContent(resp.data.content || "")
if (resp.data.group_id) {
setGroupId(String(resp.data.group_id))
}
} else {
toast.error(
resp.success ? "文章数据为空" : resp.message || "文章不存在",
)
router.push("/articles")
}
setLoading(false)
})
}
}, [articleId, isNew, router])
const handleSave = useCallback(
async (data: { title: string; content: string }) => {
if (!groupId) {
toast.error("请选择文章分组")
return
}
if (isNew) {
const resp = await createArticle({
title: data.title,
content: data.content,
group_id: Number(groupId),
status: 1,
})
if (resp.success) {
toast.success("文章创建成功")
router.push("/articles")
} else {
toast.error(resp.message || "创建失败")
}
} else {
const id = Number(articleId)
const resp = await updateArticle({
id,
title: data.title,
content: data.content,
group_id: Number(groupId),
status: 1,
})
if (resp.success) {
toast.success("文章保存成功")
router.push("/articles")
} else {
toast.error(resp.message || "保存失败")
}
}
},
[isNew, articleId, groupId, router],
)
if (loading) {
return (
<div className="flex items-center justify-center h-full">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
</div>
)
}
if (groups.length === 0) {
return (
<div className="flex flex-col items-center justify-center h-full gap-4 text-muted-foreground">
<p></p>
<Link href="/article-groups">
<span className="text-primary hover:underline"></span>
</Link>
</div>
)
}
return (
<div className="flex-1 h-full min-h-0">
<Editor
content={initialContent}
title={title}
groups={groups}
groupId={groupId}
onGroupChange={setGroupId}
onSave={handleSave}
/>
</div>
)
}