289 lines
9.2 KiB
TypeScript
289 lines
9.2 KiB
TypeScript
"use client"
|
||
|
||
import type React from "react"
|
||
|
||
import { useState, useEffect } from "react"
|
||
import { Button } from "@/components/ui/button"
|
||
import {
|
||
Dialog,
|
||
DialogContent,
|
||
DialogDescription,
|
||
DialogFooter,
|
||
DialogHeader,
|
||
DialogTitle,
|
||
DialogTrigger,
|
||
} from "@/components/ui/dialog"
|
||
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
|
||
import { Input } from "@/components/ui/input"
|
||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||
import { Loader2, UserPlus } from "lucide-react"
|
||
import { useForm } from "react-hook-form"
|
||
import { zodResolver } from "@hookform/resolvers/zod"
|
||
import * as z from "zod"
|
||
|
||
// 表单验证模式
|
||
const formSchema = z.object({
|
||
name: z
|
||
.string()
|
||
.min(2, {
|
||
message: "用户名至少需要2个字符",
|
||
})
|
||
.max(30, {
|
||
message: "用户名不能超过30个字符",
|
||
}),
|
||
email: z.string().email({
|
||
message: "请输入有效的邮箱地址",
|
||
}),
|
||
phone: z.string().optional(),
|
||
role: z.string({
|
||
required_error: "请选择用户角色",
|
||
}),
|
||
status: z.enum(["活跃", "未激活", "已禁用"], {
|
||
required_error: "请选择用户状态",
|
||
}),
|
||
address: z.string().optional(),
|
||
})
|
||
|
||
type UserFormDialogProps = {
|
||
open?: boolean
|
||
onOpenChange?: (open: boolean) => void
|
||
onSubmit: (data: z.infer<typeof formSchema>) => Promise<void>
|
||
defaultValues?: Partial<z.infer<typeof formSchema>>
|
||
mode: "add" | "edit"
|
||
roles: Array<{ id: string; name: string }>
|
||
trigger?: React.ReactNode
|
||
}
|
||
|
||
export function UserFormDialog({
|
||
open,
|
||
onOpenChange,
|
||
onSubmit,
|
||
defaultValues,
|
||
mode,
|
||
roles,
|
||
trigger,
|
||
}: UserFormDialogProps) {
|
||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||
const [dialogOpen, setDialogOpen] = useState(open || false)
|
||
|
||
const form = useForm<z.infer<typeof formSchema>>({
|
||
resolver: zodResolver(formSchema),
|
||
defaultValues: {
|
||
name: "",
|
||
email: "",
|
||
phone: "",
|
||
role: "",
|
||
status: "未激活",
|
||
address: "",
|
||
...defaultValues,
|
||
},
|
||
})
|
||
|
||
// 当defaultValues改变时重置表单
|
||
useEffect(() => {
|
||
if (defaultValues) {
|
||
form.reset({
|
||
name: defaultValues.name || "",
|
||
email: defaultValues.email || "",
|
||
phone: defaultValues.phone || "",
|
||
role: defaultValues.role || "",
|
||
status: defaultValues.status || "未激活",
|
||
address: defaultValues.address || "",
|
||
})
|
||
}
|
||
}, [defaultValues, form])
|
||
|
||
// 同步内部状态和外部状态
|
||
useEffect(() => {
|
||
if (open !== undefined) {
|
||
setDialogOpen(open)
|
||
}
|
||
}, [open])
|
||
|
||
const handleOpenChange = (newOpen: boolean) => {
|
||
setDialogOpen(newOpen)
|
||
if (onOpenChange) {
|
||
onOpenChange(newOpen)
|
||
}
|
||
|
||
// 如果对话框关闭,重置表单
|
||
if (!newOpen) {
|
||
form.reset()
|
||
}
|
||
}
|
||
|
||
const handleSubmit = async (data: z.infer<typeof formSchema>) => {
|
||
setIsSubmitting(true)
|
||
try {
|
||
await onSubmit(data)
|
||
handleOpenChange(false)
|
||
} catch (error) {
|
||
console.error("提交失败:", error)
|
||
} finally {
|
||
setIsSubmitting(false)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<Dialog open={dialogOpen} onOpenChange={handleOpenChange}>
|
||
<DialogTrigger asChild>
|
||
{trigger || (
|
||
<Button className="bg-gradient-to-r from-pink-500 to-purple-600 hover:from-pink-600 hover:to-purple-700 transition-all duration-300 shadow-md hover:shadow-lg">
|
||
<UserPlus className="mr-2 h-4 w-4" />
|
||
添加用户
|
||
</Button>
|
||
)}
|
||
</DialogTrigger>
|
||
<DialogContent className="sm:max-w-[550px]">
|
||
<DialogHeader>
|
||
<DialogTitle className="text-xl font-bold flex items-center">
|
||
<span className="bg-clip-text text-transparent bg-gradient-to-r from-purple-600 to-pink-600">
|
||
{mode === "add" ? "添加用户" : "编辑用户"}
|
||
</span>
|
||
</DialogTitle>
|
||
<DialogDescription>{mode === "add" ? "创建新的系统用户" : "修改用户信息"}</DialogDescription>
|
||
</DialogHeader>
|
||
<Form {...form}>
|
||
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4 py-4">
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<FormField
|
||
control={form.control}
|
||
name="name"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>用户名</FormLabel>
|
||
<FormControl>
|
||
<Input placeholder="输入用户名" {...field} />
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
|
||
<FormField
|
||
control={form.control}
|
||
name="email"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>邮箱地址</FormLabel>
|
||
<FormControl>
|
||
<Input placeholder="输入邮箱地址" {...field} />
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<FormField
|
||
control={form.control}
|
||
name="phone"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>电话号码</FormLabel>
|
||
<FormControl>
|
||
<Input placeholder="输入电话号码(可选)" {...field} />
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
|
||
<FormField
|
||
control={form.control}
|
||
name="role"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>用户角色</FormLabel>
|
||
<Select onValueChange={field.onChange} defaultValue={field.value} value={field.value}>
|
||
<FormControl>
|
||
<SelectTrigger>
|
||
<SelectValue placeholder="选择用户角色" />
|
||
</SelectTrigger>
|
||
</FormControl>
|
||
<SelectContent>
|
||
{roles.map((role) => (
|
||
<SelectItem key={role.id} value={role.name}>
|
||
{role.name}
|
||
</SelectItem>
|
||
))}
|
||
</SelectContent>
|
||
</Select>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
</div>
|
||
|
||
<FormField
|
||
control={form.control}
|
||
name="status"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>用户状态</FormLabel>
|
||
<Select onValueChange={field.onChange} defaultValue={field.value} value={field.value}>
|
||
<FormControl>
|
||
<SelectTrigger>
|
||
<SelectValue placeholder="选择用户状态" />
|
||
</SelectTrigger>
|
||
</FormControl>
|
||
<SelectContent>
|
||
<SelectItem value="活跃">活跃</SelectItem>
|
||
<SelectItem value="未激活">未激活</SelectItem>
|
||
<SelectItem value="已禁用">已禁用</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
<FormDescription>未激活的用户需要完成邮箱验证,已禁用的用户无法登录系统</FormDescription>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
|
||
<FormField
|
||
control={form.control}
|
||
name="address"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>地址</FormLabel>
|
||
<FormControl>
|
||
<Input placeholder="输入地址(可选)" {...field} />
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
|
||
{mode === "add" && (
|
||
<div className="bg-blue-50 p-3 rounded-md text-sm text-blue-700">
|
||
<p className="font-medium">注意事项</p>
|
||
<p>创建用户后,系统将自动发送邮件通知用户设置密码。</p>
|
||
</div>
|
||
)}
|
||
|
||
<DialogFooter className="pt-4">
|
||
<Button type="button" variant="outline" onClick={() => handleOpenChange(false)} disabled={isSubmitting}>
|
||
取消
|
||
</Button>
|
||
<Button
|
||
type="submit"
|
||
disabled={isSubmitting}
|
||
className="bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700"
|
||
>
|
||
{isSubmitting ? (
|
||
<>
|
||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||
提交中...
|
||
</>
|
||
) : (
|
||
<>{mode === "add" ? "创建用户" : "保存修改"}</>
|
||
)}
|
||
</Button>
|
||
</DialogFooter>
|
||
</form>
|
||
</Form>
|
||
</DialogContent>
|
||
</Dialog>
|
||
)
|
||
}
|