"use client"; import { useMemo, useState } from "react"; type ManagedPost = { slug: string; title: string; createdAt: string; }; type ManagedUser = { id: string; username: string; displayName: string; role: "admin" | "user"; dailyPostLimit: number; postCount: number; todayPostCount: number; posts: ManagedPost[]; }; export function AdminUserManager({ initialUsers, currentUserId }: { initialUsers: ManagedUser[]; currentUserId: string; }) { const [users, setUsers] = useState(initialUsers); const [query, setQuery] = useState(""); const [savingId, setSavingId] = useState(null); const visibleUsers = useMemo(() => { const keyword = query.trim().toLowerCase(); if (!keyword) return users; return users.filter( (user) => user.username.toLowerCase().includes(keyword) || user.displayName.toLowerCase().includes(keyword) ); }, [query, users]); async function handleDeletePost(slug: string) { if (!window.confirm("确定要删除这条内容吗?")) return; const res = await fetch(`/api/posts/${slug}`, { method: "DELETE" }); const data = await res.json().catch(() => ({})); if (!res.ok) { alert(data.error || "删除失败"); return; } setUsers((prev) => prev.map((user) => { const target = user.posts.find((post) => post.slug === slug); if (!target) return user; return { ...user, postCount: Math.max(0, user.postCount - 1), todayPostCount: toShanghaiDateKey(target.createdAt) === toShanghaiDateKey(new Date().toISOString()) ? Math.max(0, user.todayPostCount - 1) : user.todayPostCount, posts: user.posts.filter((post) => post.slug !== slug) }; }) ); } async function handleSaveLimit(userId: string, dailyPostLimit: number) { setSavingId(userId); try { const res = await fetch(`/api/admin/users/${userId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ dailyPostLimit }) }); const data = await res.json().catch(() => ({})); if (!res.ok) { alert(data.error || "保存失败"); return; } setUsers((prev) => prev.map((user) => user.id === userId ? { ...user, dailyPostLimit: data.dailyPostLimit ?? dailyPostLimit } : user ) ); } finally { setSavingId(null); } } async function handleDeleteUser(userId: string) { if (!window.confirm("确定要删除该用户及其全部内容吗?")) return; const res = await fetch(`/api/admin/users/${userId}`, { method: "DELETE" }); const data = await res.json().catch(() => ({})); if (!res.ok) { alert(data.error || "删除失败"); return; } setUsers((prev) => prev.filter((user) => user.id !== userId)); } return (

用户管理

按用户名搜索,删除指定内容、删除用户,并设置每日发布额度。

setQuery(e.target.value)} placeholder="搜索用户名" className="w-44 rounded-full border border-slate-200 bg-white px-3 py-2 text-sm shadow-inner focus:border-brand-500 focus:outline-none" />
{visibleUsers.length === 0 ? (

没有匹配的用户。

) : ( visibleUsers.map((user) => ( )) )}
); } function toShanghaiDateKey(input: string) { const date = new Date(input); const shifted = new Date(date.getTime() + 8 * 60 * 60 * 1000); return shifted.toISOString().slice(0, 10); } function AdminUserCard({ user, currentUserId, saving, onDeletePost, onDeleteUser, onSaveLimit }: { user: ManagedUser; currentUserId: string; saving: boolean; onDeletePost: (slug: string) => Promise; onDeleteUser: (userId: string) => Promise; onSaveLimit: (userId: string, dailyPostLimit: number) => Promise; }) { const [limit, setLimit] = useState(user.dailyPostLimit); return (

{user.displayName} (@{user.username})

角色:{user.role === "admin" ? "管理员" : "用户"} | 总发布:{user.postCount} | 今日发布:{user.todayPostCount}

setLimit(Number(e.target.value))} className="w-24 rounded-full border border-slate-200 bg-white px-3 py-2 text-sm shadow-inner focus:border-brand-500 focus:outline-none" /> {user.id !== currentUserId ? ( ) : null}
{user.posts.length === 0 ? (

该用户暂无内容。

) : ( user.posts.map((post) => (
{post.title}

{new Date(post.createdAt).toLocaleString("zh-CN", { hour12: false, timeZone: "Asia/Shanghai" })}

)) )}
); }