'use client'; import { useEffect, useState } from 'react'; import useSWR from 'swr'; import { Plus, Pencil, Trash2, KeyRound, Loader2, ShieldCheck, Crown, } from 'lucide-react'; import { AdminHeader } from '@/components/admin/AdminHeader'; import { PaginationTable, type Column } from '@/components/admin/PaginationTable'; import { TableToolbar } from '@/components/admin/TableToolbar'; import { Avatar } from '@/components/admin/Avatar'; import { ImageUpload } from '@/components/admin/ImageUpload'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Badge } from '@/components/ui/badge'; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { adminApi } from '@/lib/admin-services'; import { useAdminStore } from '@/store/adminStore'; import { formatDate } from '@/lib/utils'; import type { AdminRole, AdminUser } from '@/lib/types'; interface CreateForm { username: string; password: string; nickname: string; avatar: string; role: AdminRole; } interface EditForm { nickname: string; avatar: string; role: AdminRole; } const CREATE_DEFAULT: CreateForm = { username: '', password: '', nickname: '', avatar: '', role: 'normal', }; const ROLE_LABEL: Record = { super_admin: '超级管理员', normal: '普通管理员', }; export default function AdminUserPage() { const [hydrated, setHydrated] = useState(false); const [page, setPage] = useState(1); const [keyword, setKeyword] = useState(''); const [searchKw, setSearchKw] = useState(''); // 创建/编辑 const [open, setOpen] = useState(false); const [editId, setEditId] = useState(null); const [createForm, setCreateForm] = useState(CREATE_DEFAULT); const [editForm, setEditForm] = useState({ nickname: '', avatar: '', role: 'normal', }); const [saving, setSaving] = useState(false); // 重置密码 const [pwdOpen, setPwdOpen] = useState(false); const [pwdId, setPwdId] = useState(null); const [newPassword, setNewPassword] = useState(''); const [pwdConfirm, setPwdConfirm] = useState(''); const [pwdSaving, setPwdSaving] = useState(false); const currentAdminId = useAdminStore((s) => s.admin?.id); const currentAdminRole = useAdminStore((s) => s.admin?.role); const isSuperAdmin = currentAdminRole === 'super_admin'; useEffect(() => setHydrated(true), []); const { data, isLoading, mutate } = useSWR( hydrated ? ['/admin/admin-user', page, searchKw] : null, () => adminApi.adminUserPaginate({ page, pageSize: 10, keyword: searchKw || undefined, }), ); const openCreate = () => { setEditId(null); setCreateForm(CREATE_DEFAULT); setOpen(true); }; const openEdit = async (id: number) => { const detail = await adminApi.adminUserDetail(id); setEditId(id); setEditForm({ nickname: detail.nickname, avatar: detail.avatar ?? '', role: detail.role ?? 'normal', }); setOpen(true); }; const onSave = async () => { if (editId) { if (!editForm.nickname.trim()) { alert('请填写名称'); return; } setSaving(true); try { await adminApi.adminUserUpdate(editId, { nickname: editForm.nickname.trim(), avatar: editForm.avatar, role: editForm.role, }); setOpen(false); await mutate(); } catch (e) { alert((e as Error).message); } finally { setSaving(false); } return; } // create if (!createForm.username.trim() || !createForm.password || !createForm.nickname.trim()) { alert('请填写登录账号、初始密码和名称'); return; } setSaving(true); try { await adminApi.adminUserCreate({ username: createForm.username.trim(), password: createForm.password, nickname: createForm.nickname.trim(), avatar: createForm.avatar, role: createForm.role, }); setOpen(false); await mutate(); } catch (e) { alert((e as Error).message); } finally { setSaving(false); } }; const onDelete = async (id: number) => { if (!confirm('确认删除该管理员账号?此操作不可恢复。')) return; try { await adminApi.adminUserDelete(id); await mutate(); } catch (e) { alert((e as Error).message); } }; const openResetPassword = (id: number) => { setPwdId(id); setNewPassword(''); setPwdConfirm(''); setPwdOpen(true); }; const onResetPassword = async () => { if (!pwdId) return; if (newPassword.length < 6) { alert('新密码至少 6 位'); return; } if (newPassword !== pwdConfirm) { alert('两次输入的密码不一致'); return; } setPwdSaving(true); try { await adminApi.adminUserResetPassword(pwdId, newPassword); setPwdOpen(false); } catch (e) { alert((e as Error).message); } finally { setPwdSaving(false); } }; const columns: Column[] = [ { key: 'id', title: 'ID', width: 60 }, { key: 'avatar', title: '头像', width: 80, render: (r) => , }, { key: 'username', title: '登录账号', width: 200, render: (r) => (
{r.username} {r.id === currentAdminId && ( 当前 )}
), }, { key: 'nickname', title: '名称', width: 140 }, { key: 'role', title: '角色', width: 130, render: (r) => r.role === 'super_admin' ? ( {ROLE_LABEL[r.role]} ) : ( {ROLE_LABEL[r.role]} ), }, { key: 'createdAt', title: '创建时间', width: 160, render: (r) => formatDate(r.createdAt, 'YYYY-MM-DD HH:mm'), }, { key: '_op', title: '操作', width: 240, render: (r) => isSuperAdmin ? (
{r.id !== currentAdminId && ( )}
) : ( 无操作权限 ), }, ]; return (
setKeyword(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { setSearchKw(keyword); setPage(1); } }} className="max-w-xs" /> } right={ isSuperAdmin ? ( ) : null } /> columns={columns} rows={data?.list ?? []} total={data?.total ?? 0} page={page} pageSize={10} loading={isLoading} onPageChange={setPage} rowKey={(r) => String(r.id)} /> {/* 创建 / 编辑弹窗 */} {editId ? '编辑管理员账号' : '新增管理员账号'}
{editId ? ( <>
x.id === editId)?.username ?? '' } disabled />

登录账号创建后不可修改,如需修改请删除后重建

setEditForm({ ...editForm, nickname: e.target.value }) } />
setEditForm({ ...editForm, avatar: url }) } hint="建议正方形头像,单图 ≤ 2M" />
{editId === currentAdminId && (

不能将自己降级为普通管理员

)}
) : ( <>
setCreateForm({ ...createForm, username: e.target.value, }) } />

2-50 个字符,创建后不可修改

setCreateForm({ ...createForm, password: e.target.value, }) } />

创建后请通过安全渠道告知该员工

setCreateForm({ ...createForm, nickname: e.target.value, }) } />
setCreateForm({ ...createForm, avatar: url }) } hint="可选,建议正方形头像,单图 ≤ 2M" />

超级管理员可管理所有账号及删除内容

)}
{/* 重置密码弹窗 */} 重置密码
setNewPassword(e.target.value)} />
setPwdConfirm(e.target.value)} />

重置后请通过安全渠道告知该员工

); }