import { useState, useEffect, useCallback } from 'react'; import { Plus, Search, Edit2, Trash2, XCircle } from 'lucide-react'; import { Card, CardContent } from '@/components/ui/card'; import { roles } from '@/lib/mock'; import { userApi } from '@/api/user'; interface UserItem { id: number; username: string; display_name: string; email: string | null; role: string; is_active: boolean; created_at: string; } const roleNameMap: Record = { admin: '超级管理员', dba: '数据库管理员', developer: '开发人员', guest: '访客', }; const roleTagStyle: Record = { admin: 'bg-purple-500/15 text-purple-400', dba: 'bg-blue-500/15 text-blue-400', developer: 'bg-green-500/15 text-green-400', guest: 'bg-gray-500/15 text-gray-400', }; export default function UsersPage() { const [search, setSearch] = useState(''); const [roleFilter, setRoleFilter] = useState(''); const [statusFilter, setStatusFilter] = useState(''); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(false); const [showCreate, setShowCreate] = useState(false); const fetchUsers = useCallback(async () => { setLoading(true); try { const res = await userApi.list(); const data = (res as any)?.items || res; setUsers(Array.isArray(data) ? data : []); } catch { // keep existing data } finally { setLoading(false); } }, []); useEffect(() => { fetchUsers(); }, [fetchUsers]); const filtered = users.filter(u => { if (roleFilter && u.role !== roleFilter) return false; if (statusFilter === 'active' && !u.is_active) return false; if (statusFilter === 'disabled' && u.is_active) return false; if (search && !u.username.includes(search) && !(u.email || '').includes(search)) return false; return true; }); const handleToggleActive = async (user: UserItem) => { try { await userApi.update(user.id, { is_active: !user.is_active }); fetchUsers(); } catch {} }; const handleDelete = async (id: number) => { if (!confirm('确定删除该用户?')) return; try { await userApi.delete(id); fetchUsers(); } catch {} }; return (

用户管理

管理系统用户,分配角色和数据库访问权限

{/* 筛选 */}
setSearch(e.target.value)} />
{/* 用户表格 */} {loading && users.length === 0 ? (
加载中...
) : ( <>
{filtered.map(user => ( ))}
用户 角色 邮箱 状态 操作
{(user.display_name || user.username).charAt(0)}
{user.display_name || user.username}
@{user.username}
{roleNameMap[user.role] || user.role} {user.email || '-'}
共 {filtered.length} 条记录
)}
{/* 新建用户弹窗 */} {showCreate && ( setShowCreate(false)} onCreated={() => { setShowCreate(false); fetchUsers(); }} /> )}
); } function CreateUserModal({ onClose, onCreated }: { onClose: () => void; onCreated: () => void }) { const [username, setUsername] = useState(''); const [displayName, setDisplayName] = useState(''); const [password, setPassword] = useState(''); const [email, setEmail] = useState(''); const [role, setRole] = useState('developer'); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(''); const handleSubmit = async () => { if (!username || !password) { setError('用户名和密码必填'); return; } setSubmitting(true); setError(''); try { await userApi.create({ username, display_name: displayName, password, email: email || undefined, role }); onCreated(); } catch (e: any) { setError(e?.response?.data?.detail || '创建失败'); } finally { setSubmitting(false); } }; return (

新建用户

setUsername(e.target.value)} className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none" />
setDisplayName(e.target.value)} className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none" />
setPassword(e.target.value)} className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none" />
setEmail(e.target.value)} className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none" />
{error &&
{error}
}
); }