'use client'; import { useState, useEffect } from 'react'; import { logger } from '@/lib/logger'; const log = logger.child('AdminUsers'); import { Users as UsersIcon, Plus, Edit, Trash2, Loader2, Search } from 'lucide-react'; interface User { id: string; email: string; name: string; role: 'admin' | 'editor' | 'viewer'; createdAt: string; } const roleLabels = { admin: '管理员', editor: '编辑', viewer: '查看者' }; const roleColors = { admin: 'bg-red-100 text-red-800', editor: 'bg-blue-100 text-blue-800', viewer: 'bg-gray-100 text-gray-800' }; export default function UsersPage() { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(''); const [showCreateModal, setShowCreateModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); const [selectedUser, setSelectedUser] = useState(null); const [saving, setSaving] = useState(false); const [deletingUserId, setDeletingUserId] = useState(null); const [formData, setFormData] = useState({ email: '', name: '', password: '', role: 'viewer' as 'admin' | 'editor' | 'viewer' }); useEffect(() => { fetchUsers(); }, []); const fetchUsers = async () => { try { setLoading(true); const res = await fetch('/api/admin/users'); const data = await res.json(); if (res.ok) { setUsers(data.users || []); } } catch (error) { console.error('获取用户列表失败:', error); } finally { setLoading(false); } }; const handleCreate = async () => { if (!formData.email || !formData.name || !formData.password || !formData.role) { return; } try { setSaving(true); const res = await fetch('/api/admin/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); if (res.ok) { setShowCreateModal(false); setFormData({ email: '', name: '', password: '', role: 'viewer' }); await fetchUsers(); } else { const data = await res.json(); alert(data.error || '创建失败'); } } catch (error) { console.error('创建用户失败:', error); } finally { setSaving(false); } }; const handleDelete = async (userId: string) => { if (deletingUserId) { log.warn('删除操作正在进行中,请勿重复点击'); return; } if (!confirm('确定要删除此用户吗?此操作不可恢复。')) { return; } try { setDeletingUserId(userId); const res = await fetch(`/api/admin/users/${userId}`, { method: 'DELETE' }); if (res.ok) { await fetchUsers(); } else { const data = await res.json(); alert(data.error || '删除失败'); } } catch (error) { console.error('删除用户失败:', error); alert('删除失败,请稍后重试'); } finally { setDeletingUserId(null); } }; const filteredUsers = users.filter(user => user.email.toLowerCase().includes(searchTerm.toLowerCase()) || user.name.toLowerCase().includes(searchTerm.toLowerCase()) ); if (loading) { return (
); } return (

用户管理

管理系统用户和权限

setSearchTerm(e.target.value)} className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:border-transparent" />
{filteredUsers.map(user => ( ))}
用户信息 角色 创建时间 操作
{user.name}
{user.email}
{roleLabels[user.role]} {new Date(user.createdAt).toLocaleDateString('zh-CN')}
{filteredUsers.length === 0 && (
暂无用户数据
)}
{/* Create Modal */} {showCreateModal && (

添加用户

setFormData({ ...formData, email: e.target.value })} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#C41E3A]" />
setFormData({ ...formData, name: e.target.value })} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#C41E3A]" />
setFormData({ ...formData, password: e.target.value })} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#C41E3A]" />
)} {/* Edit Modal */} {showEditModal && selectedUser && (

编辑用户

setFormData({ ...formData, email: e.target.value })} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#C41E3A]" />
setFormData({ ...formData, name: e.target.value })} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#C41E3A]" />
setFormData({ ...formData, password: e.target.value })} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#C41E3A]" />
)}
); }