import { useCallback, useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; import { createAdminUser, listAdminUsers, updateAdminUser } from '../../api/client'; import type { PagedResult } from '../../api/client'; import type { AdminUserRow } from '../../types'; import { Pagination } from '../../components/Pagination'; import { formatYuan } from '../utils/format'; const PAGE_SIZE = 20; export function UsersPage() { const [result, setResult] = useState | null>(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [message, setMessage] = useState(null); // query params committed on search click — separate from pending inputs const [committed, setCommitted] = useState({ search: '', role: '', status: '' }); const [pending, setPending] = useState({ search: '', role: '', status: '' }); const [showCreate, setShowCreate] = useState(false); const [newUser, setNewUser] = useState({ username: '', password: '', displayName: '', workspaceRoot: '', balanceCents: 500 }); const [creating, setCreating] = useState(false); const load = useCallback(async (p: number, q: { search: string; role: string; status: string }) => { setLoading(true); setError(null); try { const data = await listAdminUsers({ page: p, pageSize: PAGE_SIZE, search: q.search, role: q.role, status: q.status }); setResult(data); } catch (err) { setError(err instanceof Error ? err.message : '加载失败'); } finally { setLoading(false); } }, []); useEffect(() => { void load(1, committed); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const handleQuery = () => { setCommitted(pending); void load(1, pending); }; const handlePage = (p: number) => void load(p, committed); const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') handleQuery(); }; const reload = () => void load(result?.page ?? 1, committed); const handleCreate = async (e: React.FormEvent) => { e.preventDefault(); setMessage(null); setError(null); setCreating(true); try { await createAdminUser({ username: newUser.username, password: newUser.password, displayName: newUser.displayName || undefined, workspaceRoot: newUser.workspaceRoot || undefined, balanceCents: Number(newUser.balanceCents), }); setNewUser({ username: '', password: '', displayName: '', workspaceRoot: '', balanceCents: 500 }); setMessage('用户已创建'); setShowCreate(false); reload(); } catch (err) { setError(err instanceof Error ? err.message : '创建失败'); } finally { setCreating(false); } }; const toggleStatus = async (user: AdminUserRow) => { try { await updateAdminUser(user.id, { status: user.status === 'active' ? 'disabled' : 'active' }); reload(); } catch (err) { setError(err instanceof Error ? err.message : '操作失败'); } }; return (

用户管理

搜索账号、查看列表、进入用户详情配置权限

{error &&

{error}

} {message &&

{message}

} {showCreate && (

创建用户

setNewUser((s) => ({ ...s, username: e.target.value }))} /> setNewUser((s) => ({ ...s, password: e.target.value }))} /> setNewUser((s) => ({ ...s, displayName: e.target.value }))} /> setNewUser((s) => ({ ...s, workspaceRoot: e.target.value }))} /> setNewUser((s) => ({ ...s, balanceCents: Number(e.target.value) }))} />
)}

用户列表 {result && {result.total.toLocaleString()} 人}

setPending((s) => ({ ...s, search: e.target.value }))} onKeyDown={handleKeyDown} />
{!result && !loading && (

输入条件后点击查询,或直接点查询加载全部

)} {loading &&

加载中…

} {!loading && result && ( result.items.length === 0 ? (

没有匹配的用户

) : ( <>
{result.items.map((user) => ( ))}
用户 角色 状态 余额 工作目录 操作
{user.displayName}
@{user.username}
{user.role === 'admin' ? '管理员' : '用户'} {user.status === 'active' ? '正常' : user.status === 'disabled' ? '禁用' : '封禁'} ¥{formatYuan(user.balanceCents)} {user.workspaceRoot || '—'} 详情 {user.role === 'user' && ( )}
) )}
); }