基于 src/client/admin-shadcn/pages/Users.tsx 中用户管理表单的实现,提取可复用的表单开发模式和最佳实践,适用于基于 Shadcn-ui 的管理后台表单开发。
// 1. 类型定义
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form';
import { Input } from '@/client/components/ui/input';
import { Button } from '@/client/components/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/client/components/ui/dialog';
// 2. 表单配置
const [isCreateForm, setIsCreateForm] = useState(true);
const [editingData, setEditingData] = useState<any>(null);
const createForm = useForm<CreateType>({
resolver: zodResolver(createSchema),
defaultValues: {/* 创建时的默认值 */},
});
const updateForm = useForm<UpdateType>({
resolver: zodResolver(updateSchema),
defaultValues: {/* 更新时的默认值 */},
});
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel className="flex items-center">
用户名
<span className="text-red-500 ml-1">*</span>
</FormLabel>
<FormControl>
<Input placeholder="请输入用户名" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>邮箱</FormLabel>
<FormControl>
<Input type="email" placeholder="请输入邮箱" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel className="flex items-center">
密码
<span className="text-red-500 ml-1">*</span>
</FormLabel>
<FormControl>
<Input type="password" placeholder="请输入密码" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="isDisabled"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<FormLabel className="text-base">状态</FormLabel>
<FormDescription>
禁用后用户将无法登录系统
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value === 1}
onCheckedChange={(checked) => field.onChange(checked ? 1 : 0)}
/>
</FormControl>
</FormItem>
)}
/>
// 创建时:必须提供值
nickname: z.string().optional()
// 更新时:完全可选
nickname: z.string().optional()
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
<DialogContent className="sm:max-w-[500px]">
<DialogHeader>
<DialogTitle>{isCreateForm ? '创建' : '编辑'}用户</DialogTitle>
<DialogDescription>
{isCreateForm ? '创建新记录' : '编辑现有记录'}
</DialogDescription>
</DialogHeader>
<Form {...(isCreateForm ? createForm : updateForm)}>
<form onSubmit={form.handleSubmit(isCreateForm ? handleCreate : handleUpdate)} className="space-y-4">
{/* 表单字段 */}
<DialogFooter>
<Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
取消
</Button>
<Button type="submit">
{isCreateForm ? '创建' : '更新'}
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
sm:max-w-[500px]space-y-4// 创建用户
const handleCreateSubmit = async (data: CreateUserFormData) => {
try {
const res = await apiClient.$post({ json: data });
if (res.status !== 201) throw new Error('创建失败');
toast.success('创建成功');
setIsModalOpen(false);
refetch();
} catch (error) {
toast.error('创建失败,请重试');
}
};
// 更新用户
const handleUpdateSubmit = async (data: UpdateUserFormData) => {
try {
const res = await apiClient[':id']['$put']({
param: { id: editingData.id },
json: data
});
if (res.status !== 200) throw new Error('更新失败');
toast.success('更新成功');
setIsModalOpen(false);
refetch();
} catch (error) {
toast.error('更新失败,请重试');
}
};
```typescript // 表单相关 import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form'; import { Input } from '@/client/components/ui/input'; import { Button } from '@/client/components/ui/button'; import { Switch } from '@/client/components/ui/switch';
// 对话框相关 import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/client/components/ui/dialog';
// 表单工具 import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { toast } from 'sonner';