移除注册功能,管理员创建用户并新增创建用户弹窗
This commit is contained in:
@@ -1,23 +1,30 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Loader2, Plus, Trash2, Users as UsersIcon, AlertCircle } from 'lucide-react'
|
||||
import { api, Role, UserInfo } from '@/lib/api'
|
||||
import { canDeleteUsers, roleLabel } from '@/lib/auth'
|
||||
import { Plus, Trash2, Users as UsersIcon, AlertCircle, Loader2 } from 'lucide-react'
|
||||
import { api, UserInfo } from '@/lib/api'
|
||||
import { canDeleteUsers, getStoredUser, roleLabel, RoleName } from '@/lib/auth'
|
||||
import { CreateUserDialog } from '@/components/CreateUserDialog'
|
||||
import { cn } from '@/lib/cn'
|
||||
|
||||
const creatableRoles: Record<RoleName, RoleName[]> = {
|
||||
system_admin: ['admin', 'user'],
|
||||
admin: ['user'],
|
||||
user: [],
|
||||
}
|
||||
|
||||
export function Users() {
|
||||
const [users, setUsers] = useState<UserInfo[]>([])
|
||||
const [roles, setRoles] = useState<Role[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState('')
|
||||
const [formOpen, setFormOpen] = useState(false)
|
||||
const [form, setForm] = useState({ username: '', email: '', password: '', role: 'user' })
|
||||
const [dialogOpen, setDialogOpen] = useState(false)
|
||||
|
||||
const currentUser = getStoredUser()
|
||||
const allowedRoles = currentUser ? creatableRoles[currentUser.role] : []
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const [u, r] = await Promise.all([api.listUsers(), api.listRoles()])
|
||||
const u = await api.listUsers()
|
||||
setUsers(u)
|
||||
setRoles(r)
|
||||
setError('')
|
||||
} catch (err: any) {
|
||||
setError(err?.message || '加载失败')
|
||||
@@ -30,23 +37,6 @@ export function Users() {
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const handleCreate = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
await api.createUser({
|
||||
username: form.username,
|
||||
email: form.email || undefined,
|
||||
password: form.password,
|
||||
role: form.role,
|
||||
})
|
||||
setForm({ username: '', email: '', password: '', role: 'user' })
|
||||
setFormOpen(false)
|
||||
fetchData()
|
||||
} catch (err: any) {
|
||||
setError(err?.message || '创建失败')
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
if (!confirm('确定删除该用户?')) return
|
||||
try {
|
||||
@@ -66,7 +56,7 @@ export function Users() {
|
||||
}
|
||||
}
|
||||
|
||||
const currentUser = users.find((u) => u.username === JSON.parse(localStorage.getItem('user') || '{}')?.username)
|
||||
const current = users.find((u) => u.username === currentUser?.username)
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
@@ -78,7 +68,7 @@ export function Users() {
|
||||
<h1 className="text-xl font-bold text-foreground">用户管理</h1>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setFormOpen(!formOpen)}
|
||||
onClick={() => setDialogOpen(true)}
|
||||
className="inline-flex items-center gap-2 px-3 py-2 rounded-btn bg-accent text-white text-sm font-medium hover:bg-accent/90 transition-colors"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
@@ -93,51 +83,6 @@ export function Users() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{formOpen && (
|
||||
<form
|
||||
onSubmit={handleCreate}
|
||||
className="rounded-card border border-border bg-surface p-4 grid grid-cols-1 md:grid-cols-5 gap-3"
|
||||
>
|
||||
<input
|
||||
required
|
||||
placeholder="用户名"
|
||||
value={form.username}
|
||||
onChange={(e) => setForm({ ...form, username: e.target.value })}
|
||||
className="rounded-input bg-base border border-border px-3 py-2 text-sm focus:outline-none focus:border-accent"
|
||||
/>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="邮箱"
|
||||
value={form.email}
|
||||
onChange={(e) => setForm({ ...form, email: e.target.value })}
|
||||
className="rounded-input bg-base border border-border px-3 py-2 text-sm focus:outline-none focus:border-accent"
|
||||
/>
|
||||
<input
|
||||
required
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
value={form.password}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
className="rounded-input bg-base border border-border px-3 py-2 text-sm focus:outline-none focus:border-accent"
|
||||
/>
|
||||
<select
|
||||
value={form.role}
|
||||
onChange={(e) => setForm({ ...form, role: e.target.value })}
|
||||
className="rounded-input bg-base border border-border px-3 py-2 text-sm focus:outline-none focus:border-accent"
|
||||
>
|
||||
{roles.map((r) => (
|
||||
<option key={r.id} value={r.name}>{roleLabel(r.name)}</option>
|
||||
))}
|
||||
</select>
|
||||
<button
|
||||
type="submit"
|
||||
className="md:col-span-5 inline-flex items-center justify-center gap-2 px-4 py-2 rounded-btn bg-accent text-white text-sm font-medium hover:bg-accent/90 transition-colors"
|
||||
>
|
||||
创建
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
|
||||
<div className="rounded-card border border-border bg-surface overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-elevated/50 text-secondary">
|
||||
@@ -173,7 +118,7 @@ export function Users() {
|
||||
<select
|
||||
value={u.status}
|
||||
onChange={(e) => handleStatusChange(u, e.target.value)}
|
||||
disabled={u.id === currentUser?.id}
|
||||
disabled={u.id === current?.id}
|
||||
className="bg-base border border-border rounded-input px-2 py-1 text-xs disabled:opacity-50"
|
||||
>
|
||||
<option value="active">启用</option>
|
||||
@@ -184,7 +129,7 @@ export function Users() {
|
||||
{new Date(u.created_at).toLocaleString()}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
{canDeleteUsers(currentUser?.role || 'user') && u.id !== currentUser?.id && (
|
||||
{canDeleteUsers(current?.role || 'user') && u.id !== current?.id && (
|
||||
<button
|
||||
onClick={() => handleDelete(u.id)}
|
||||
className="p-1.5 rounded-btn text-danger hover:bg-danger/10 transition-colors"
|
||||
@@ -199,6 +144,16 @@ export function Users() {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<CreateUserDialog
|
||||
open={dialogOpen}
|
||||
onClose={() => setDialogOpen(false)}
|
||||
onSuccess={() => {
|
||||
setDialogOpen(false)
|
||||
fetchData()
|
||||
}}
|
||||
allowedRoles={allowedRoles}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user