feat: 支持用户管理

This commit is contained in:
richarjiang
2026-03-15 15:45:09 +08:00
parent 7628768869
commit 3c35f1982f
9 changed files with 738 additions and 82 deletions

View File

@@ -0,0 +1,164 @@
'use client'
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { Spinner } from '@/components/ui/spinner'
import { User, UserFormData } from '@/types'
interface UserDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
user?: User | null
onSubmit: (data: UserFormData) => Promise<void>
}
const defaultFormData: UserFormData = {
email: '',
password: '',
name: '',
}
export function UserDialog({ open, onOpenChange, user, onSubmit }: UserDialogProps) {
const [formData, setFormData] = useState<UserFormData>(defaultFormData)
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState('')
// Reset form when dialog opens/closes or user changes
useEffect(() => {
if (open) {
if (user) {
setFormData({
email: user.email,
password: '',
name: user.name || '',
})
} else {
setFormData(defaultFormData)
}
setError('')
}
}, [open, user])
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
if (!formData.email.trim()) {
setError('请输入邮箱')
return
}
if (!user && !formData.password) {
setError('请输入密码')
return
}
if (formData.password && formData.password.length < 6) {
setError('密码至少需要6个字符')
return
}
setIsLoading(true)
try {
await onSubmit(formData)
onOpenChange(false)
} catch (err) {
setError(err instanceof Error ? err.message : '操作失败,请稍后重试')
} finally {
setIsLoading(false)
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[400px]">
<DialogHeader>
<DialogTitle>{user ? '编辑用户' : '添加用户'}</DialogTitle>
<DialogDescription>
{user ? '修改用户信息,密码留空则不修改' : '创建新的平台用户'}
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
{error && (
<div className="p-3 text-sm text-red-600 bg-red-50 border border-red-200 rounded-md">
{error}
</div>
)}
<div className="space-y-2">
<Label htmlFor="email"> *</Label>
<Input
id="email"
type="email"
value={formData.email}
onChange={(e) =>
setFormData((prev) => ({ ...prev, email: e.target.value }))
}
placeholder="请输入邮箱"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="password">
{user ? '密码 (留空则不修改)' : '密码 *'}
</Label>
<Input
id="password"
type="password"
value={formData.password}
onChange={(e) =>
setFormData((prev) => ({ ...prev, password: e.target.value }))
}
placeholder={user ? '留空则不修改密码' : '请输入密码'}
required={!user}
/>
</div>
<div className="space-y-2">
<Label htmlFor="name"> ()</Label>
<Input
id="name"
value={formData.name}
onChange={(e) =>
setFormData((prev) => ({ ...prev, name: e.target.value }))
}
placeholder="请输入姓名"
/>
</div>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
disabled={isLoading}
>
</Button>
<Button type="submit" disabled={isLoading}>
{isLoading ? (
<>
<Spinner size="sm" className="mr-2" />
...
</>
) : (
'保存'
)}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
)
}