"use client" import * as React from "react" import { useRouter, useSearchParams } from "next/navigation" import { useQuery, useMutation, gql } from '@apollo/client' import { EnhancedSimpleEditor } from '@/components/tiptap-templates/simple/enhanced-simple-editor' import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Textarea } from "@/components/ui/textarea" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Label } from "@/components/ui/label" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { IconLoader, IconDeviceFloppy, IconEye, IconArrowLeft } from "@tabler/icons-react" import { Checkbox } from "@/components/ui/checkbox" import { toast } from "sonner" const GET_BLOG = gql` query GetBlog($id: UUID!) { blog(id: $id) { id title slug excerpt content categoryId status featuredImage metaTitle metaDescription publishedAt viewCount isFeatured isActive createdAt updatedAt } } ` const CREATE_BLOG = gql` mutation CreateBlog($input: CreateBlogInput!) { createBlog(input: $input) { id title slug status } } ` const UPDATE_BLOG = gql` mutation UpdateBlog($id: UUID!, $input: UpdateBlogInput!) { updateBlog(id: $id, input: $input) { id title slug status updatedAt } } ` interface BlogData { title: string slug: string excerpt: string content: any categoryId?: string status: 'draft' | 'published' | 'archived' featuredImage?: string metaTitle?: string metaDescription?: string isFeatured: boolean isActive: boolean } export default function EditorComponent() { const router = useRouter() const searchParams = useSearchParams() const blogId = searchParams.get('id') const isEditing = Boolean(blogId) const [blogData, setBlogData] = React.useState({ title: '', slug: '', excerpt: '', content: null, status: 'draft', isFeatured: false, isActive: true }) const [isLoading, setIsLoading] = React.useState(false) const { data: blogQuery, loading: blogLoading } = useQuery(GET_BLOG, { variables: { id: blogId }, skip: !isEditing, onCompleted: (data) => { if (data?.blog) { setBlogData({ title: data.blog.title || '', slug: data.blog.slug || '', excerpt: data.blog.excerpt || '', content: data.blog.content, categoryId: data.blog.categoryId, status: data.blog.status || 'draft', featuredImage: data.blog.featuredImage, metaTitle: data.blog.metaTitle || '', metaDescription: data.blog.metaDescription || '', isFeatured: data.blog.isFeatured || false, isActive: data.blog.isActive ?? true }) } } }) const [createBlog] = useMutation(CREATE_BLOG) const [updateBlog] = useMutation(UPDATE_BLOG) const generateSlug = React.useCallback((title: string) => { return title .toLowerCase() .replace(/[^\w\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-') .trim() }, []) React.useEffect(() => { if (blogData.title && !isEditing) { const slug = generateSlug(blogData.title) setBlogData(prev => ({ ...prev, slug })) } }, [blogData.title, generateSlug, isEditing]) const handleSave = async (publishStatus?: 'draft' | 'published') => { if (!blogData.title || !blogData.slug || !blogData.content) { toast.error('请填写标题、链接和内容') return } setIsLoading(true) try { const input = { title: blogData.title, slug: blogData.slug, excerpt: blogData.excerpt || undefined, content: blogData.content, categoryId: blogData.categoryId || undefined, status: publishStatus || blogData.status, featuredImage: blogData.featuredImage || undefined, metaTitle: blogData.metaTitle || undefined, metaDescription: blogData.metaDescription || undefined, isFeatured: blogData.isFeatured, isActive: blogData.isActive } if (isEditing) { await updateBlog({ variables: { id: blogId, input } }) toast.success('博客已更新') } else { const result = await createBlog({ variables: { input } }) toast.success('博客已创建') const newBlogId = result.data?.createBlog?.id if (newBlogId) { router.push(`/admin/editor?id=${newBlogId}`) } } } catch (error) { toast.error(isEditing ? '更新失败' : '创建失败') console.error('Save error:', error) } finally { setIsLoading(false) } } const handleContentChange = (content: any) => { setBlogData(prev => ({ ...prev, content })) } if (blogLoading) { return (
) } return (
{/* Main Editor Area */}
{/* Header Toolbar */}
{blogData.status === 'draft' ? '草稿' : blogData.status === 'published' ? '已发布' : '已归档'}
{/* Editor Content */}
{/* Title Input - WordPress Style */}
setBlogData(prev => ({ ...prev, title: e.target.value }))} placeholder="在此处输入标题" className="w-full text-4xl font-bold border-none outline-none placeholder:text-gray-400 bg-transparent resize-none" style={{ lineHeight: '1.2' }} />
{/* Slug Display (read-only for now) */} {blogData.slug && (
永久链接: /blog/{blogData.slug}
)} {/* Editor */}
{/* Sidebar */}
{/* Publish Section */} 发布
setBlogData(prev => ({ ...prev, isFeatured: !!checked })) } />
setBlogData(prev => ({ ...prev, isActive: !!checked })) } />
{/* Featured Image */} 特色图片
{blogData.featuredImage ? (
特色图片
) : (

添加特色图片

setBlogData(prev => ({ ...prev, featuredImage: e.target.value }))} />
)}
{/* Excerpt */} 摘要