Browse Source

📝 docs(shadcn): add shadcn-ui form development guide

- create form development documentation for Shadcn-ui admin interfaces
- document core features including type-safe forms and state management
- provide component templates for common form elements
- include best practices for validation and user experience

💄 style(login): optimize login page imports

- remove unused React import
- delete unnecessary cn utility import
- clean up import statements for better maintainability
yourname 4 months ago
parent
commit
3a6a374e95
2 changed files with 244 additions and 2 deletions
  1. 243 0
      .roo/commands/shadcn-manage-form.md
  2. 1 2
      src/client/admin-shadcn/pages/Login.tsx

+ 243 - 0
.roo/commands/shadcn-manage-form.md

@@ -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';

+ 1 - 2
src/client/admin-shadcn/pages/Login.tsx

@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import { useState } from 'react';
 import { useNavigate } from 'react-router';
 import { useAuth } from '../hooks/AuthProvider';
 import { useForm } from 'react-hook-form';
@@ -10,7 +10,6 @@ import { Button } from '@/client/components/ui/button';
 import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/client/components/ui/card';
 import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form';
 import { Input } from '@/client/components/ui/input';
-import { cn } from '@/client/lib/utils';
 
 // 表单验证Schema
 const loginSchema = z.object({