|
|
@@ -0,0 +1,243 @@
|
|
|
+---
|
|
|
+description: "Shadcn-ui 管理页表单开发指令"
|
|
|
+---
|
|
|
+
|
|
|
+## 概述
|
|
|
+基于 `src/client/admin-shadcn/pages/Users.tsx` 中用户管理表单的实现,提取可复用的表单开发模式和最佳实践,适用于基于 Shadcn-ui 的管理后台表单开发。
|
|
|
+
|
|
|
+## 核心特性
|
|
|
+
|
|
|
+### 1. 类型安全表单
|
|
|
+- **后端Schema复用**:直接使用后端定义的 Zod Schema
|
|
|
+- **RPC类型提取**:从 Hono 客户端自动推断类型
|
|
|
+- **一致的类型定义**:前后端类型完全同步
|
|
|
+
|
|
|
+### 2. 表单状态管理
|
|
|
+- **创建/编辑模式切换**:单一表单处理两种状态
|
|
|
+- **智能默认值**:根据模式自动设置表单初始值
|
|
|
+- **表单重置**:模式切换时自动重置表单状态
|
|
|
+
|
|
|
+### 3. 统一的UI组件模式
|
|
|
+- **Shadcn-ui组件集成**:使用标准的 Shadcn-ui 表单组件
|
|
|
+- **响应式布局**:适配不同屏幕尺寸
|
|
|
+- **无障碍支持**:完整的 ARIA 属性支持
|
|
|
+
|
|
|
+## 开发模板
|
|
|
+
|
|
|
+### 基础结构模板
|
|
|
+```typescript
|
|
|
+// 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: {/* 更新时的默认值 */},
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+### 表单字段模板
|
|
|
+
|
|
|
+#### 文本输入框
|
|
|
+```typescript
|
|
|
+<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>
|
|
|
+ )}
|
|
|
+/>
|
|
|
+```
|
|
|
+
|
|
|
+#### 邮箱输入框
|
|
|
+```typescript
|
|
|
+<FormField
|
|
|
+ control={form.control}
|
|
|
+ name="email"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>邮箱</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input type="email" placeholder="请输入邮箱" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+/>
|
|
|
+```
|
|
|
+
|
|
|
+#### 密码输入框
|
|
|
+```typescript
|
|
|
+<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>
|
|
|
+ )}
|
|
|
+/>
|
|
|
+```
|
|
|
+
|
|
|
+#### 开关控件(布尔值)
|
|
|
+```typescript
|
|
|
+<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>
|
|
|
+ )}
|
|
|
+/>
|
|
|
+```
|
|
|
+
|
|
|
+#### 可选字段处理
|
|
|
+```typescript
|
|
|
+// 创建时:必须提供值
|
|
|
+nickname: z.string().optional()
|
|
|
+
|
|
|
+// 更新时:完全可选
|
|
|
+nickname: z.string().optional()
|
|
|
+```
|
|
|
+
|
|
|
+### 对话框集成模板
|
|
|
+```typescript
|
|
|
+<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>
|
|
|
+```
|
|
|
+
|
|
|
+## 最佳实践
|
|
|
+
|
|
|
+### 1. 表单验证
|
|
|
+- 使用 Zod Schema 进行类型验证
|
|
|
+- 必填字段标记红色星号
|
|
|
+- 提供清晰的错误提示
|
|
|
+
|
|
|
+### 2. 用户体验
|
|
|
+- 表单提交时显示加载状态
|
|
|
+- 操作成功后显示 toast 通知
|
|
|
+- 支持键盘导航和提交
|
|
|
+
|
|
|
+### 3. 数据管理
|
|
|
+- 创建后自动刷新数据列表
|
|
|
+- 编辑时回填现有数据
|
|
|
+- 支持表单重置功能
|
|
|
+
|
|
|
+### 4. 响应式设计
|
|
|
+- 对话框最大宽度 `sm:max-w-[500px]`
|
|
|
+- 表单间距统一使用 `space-y-4`
|
|
|
+- 移动端友好的布局
|
|
|
+
|
|
|
+## 使用示例
|
|
|
+
|
|
|
+### 完整实现参考
|
|
|
+```typescript
|
|
|
+// 创建用户
|
|
|
+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';
|