"use client"; import { useState, useEffect, useRef } from "react"; import { useRouter } from "next/navigation"; import Link from "next/link"; import { Pencil, Trash2, Pin, Star, SlidersHorizontal, ChevronDown, Check, BadgeCheck } from "lucide-react"; import { apiMe, apiDeletePost, apiTogglePin, apiToggleRecommend, apiAdminApprovePost, type User } from "@/lib/api"; import { canModerateBoard, isAdminOrAbove } from "@/lib/roles"; import { toast } from "@/lib/toast"; import ConfirmDialog from "@/components/ConfirmDialog"; import StaffDeletePostModal from "@/components/StaffDeletePostModal"; interface PostActionsProps { postId: string; authorId: number; boardId: number; pinned: number; recommended: boolean; /** 帖子审核状态:pending 时对可审者显示「通过评审」 */ status: string; /** 供删除确认展示 */ title?: string; } export default function PostActions({ postId, authorId, boardId, pinned, recommended, status, title, }: PostActionsProps) { const router = useRouter(); const [user, setUser] = useState(null); const [open, setOpen] = useState(false); const [deleting, setDeleting] = useState(false); const [isPinned, setIsPinned] = useState(pinned > 0); const [isRecommended, setIsRecommended] = useState(recommended); const [acting, setActing] = useState(false); const [confirmDelete, setConfirmDelete] = useState(false); const [staffDeleteOpen, setStaffDeleteOpen] = useState(false); const menuRef = useRef(null); useEffect(() => { apiMe() .then((res) => setUser(res.user || null)) .catch(() => setUser(null)); }, []); useEffect(() => { if (!open) return; const onDown = (e: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(e.target as Node)) setOpen(false); }; const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") setOpen(false); }; document.addEventListener("mousedown", onDown); document.addEventListener("keydown", onKey); return () => { document.removeEventListener("mousedown", onDown); document.removeEventListener("keydown", onKey); }; }, [open]); const canManage = !!user && (user.id === authorId || canModerateBoard(user, boardId)); const isAdmin = !!user && isAdminOrAbove(user.role); const canApprove = !!user && status === "pending" && canModerateBoard(user, boardId); // staff 删他人帖:走类型+理由;作者自删:简单确认 const staffDeletingOther = !!user && user.id !== authorId && canModerateBoard(user, boardId); const runDelete = async (opts?: { delete_type?: string; delete_reason?: string }) => { setDeleting(true); try { const res = await apiDeletePost(postId, opts); if (res.message) { setConfirmDelete(false); setStaffDeleteOpen(false); router.push("/"); toast("帖子已删除", "ok"); } else { toast(res.error || "删除失败"); } } catch { toast("网络错误,删除失败"); } finally { setDeleting(false); } }; const handleApprove = async () => { setActing(true); setOpen(false); try { await apiAdminApprovePost(Number(postId)); toast("已通过评审", "ok"); router.refresh(); } catch (e) { toast(e instanceof Error ? e.message : "通过评审失败"); } finally { setActing(false); } }; const handlePin = async () => { setActing(true); setOpen(false); try { const res = await apiTogglePin(postId); setIsPinned(res.pinned > 0); toast(res.pinned > 0 ? "已置顶" : "已取消置顶", "ok"); router.refresh(); } catch { toast("操作失败,请稍后重试"); } finally { setActing(false); } }; const handleRecommend = async () => { setActing(true); setOpen(false); try { const res = await apiToggleRecommend(postId); setIsRecommended(res.recommended); toast(res.recommended ? "已设为推荐" : "已取消推荐", "ok"); router.refresh(); } catch { toast("操作失败,请稍后重试"); } finally { setActing(false); } }; if (!canManage) return null; const itemCls = "w-full flex items-center gap-2.5 px-4 py-2.5 text-[13px] transition-colors hover:bg-[var(--panel-2)] focus-visible:bg-[var(--panel-2)]"; return (
{open && (
{canApprove && ( <>
)} {isAdmin && ( <>
)} 编辑帖子
)} void runDelete()} onCancel={() => setConfirmDelete(false)} /> setStaffDeleteOpen(false)} onConfirm={(opts) => void runDelete(opts)} />
); }