Ver Fonte

♻️ refactor(advertisement): 重构广告表单逻辑与UI

- 分离创建和编辑表单为独立组件,提高代码可读性和可维护性
- 拆分handleSubmit为handleCreateSubmit和handleUpdateSubmit两个独立方法
- 移除重复的表单控制逻辑,简化代码结构
- 为提交按钮添加加载状态disabled属性,优化用户体验

📝 docs: 删除冗余的对话框集成模板

- 移除.shadcn-manage-form.md中过时的对话框集成模板代码
- 清理文档内容,保持与实际代码同步
yourname há 3 meses atrás
pai
commit
3258c0a757

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

@@ -169,35 +169,6 @@ 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>
-```
-
 ## 高级表单组件模板
 
 ### 头像选择器集成

+ 351 - 158
src/client/admin-shadcn/pages/Advertisements.tsx

@@ -183,15 +183,26 @@ export const AdvertisementsPage = () => {
     }
   };
 
-  // 处理表单提交
-  const handleSubmit = (data: CreateRequest | UpdateRequest) => {
-    if (isCreateForm) {
-      createMutation.mutate(data as CreateRequest);
-    } else if (editingAdvertisement) {
-      updateMutation.mutate({ 
-        id: editingAdvertisement.id, 
-        data: data as UpdateRequest 
+  // 处理创建表单提交
+  const handleCreateSubmit = async (data: CreateRequest) => {
+    try {
+      await createMutation.mutateAsync(data);
+    } catch (error) {
+      throw error;
+    }
+  };
+
+  // 处理编辑表单提交
+  const handleUpdateSubmit = async (data: UpdateRequest) => {
+    if (!editingAdvertisement) return;
+    
+    try {
+      await updateMutation.mutateAsync({
+        id: editingAdvertisement.id,
+        data
       });
+    } catch (error) {
+      throw error;
     }
   };
 
@@ -350,123 +361,168 @@ export const AdvertisementsPage = () => {
             </DialogDescription>
           </DialogHeader>
           
-          <Form {...(isCreateForm ? createForm : updateForm)}>
-            <form onSubmit={(isCreateForm ? createForm : updateForm).handleSubmit(handleSubmit)} className="space-y-4">
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="title"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel className="flex items-center">
-                      标题 <span className="text-red-500 ml-1">*</span>
-                    </FormLabel>
-                    <FormControl>
-                      <Input placeholder="请输入广告标题" {...field} />
-                    </FormControl>
-                    <FormDescription>广告显示的标题文本,最多30个字符</FormDescription>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="typeId"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel className="flex items-center">
-                      广告类型 <span className="text-red-500 ml-1">*</span>
-                    </FormLabel>
-                    <FormControl>
-                      <select 
-                        {...field} 
-                        className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
-                        value={field.value || ''}
-                        onChange={(e) => field.onChange(parseInt(e.target.value))}
-                      >
-                        <option value="">请选择广告类型</option>
-                        {advertisementTypes?.data.map(type => (
-                          <option key={type.id} value={type.id}>{type.name}</option>
-                        ))}
-                      </select>
-                    </FormControl>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="code"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel className="flex items-center">
-                      调用别名 <span className="text-red-500 ml-1">*</span>
-                    </FormLabel>
-                    <FormControl>
-                      <Input placeholder="请输入调用别名" {...field} />
-                    </FormControl>
-                    <FormDescription>用于程序调用的唯一标识,最多20个字符</FormDescription>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="imageFileId"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel>广告图片</FormLabel>
-                    <FormControl>
-                      <AvatarSelector
-                        value={field.value || undefined}
-                        onChange={field.onChange}
-                        maxSize={2}
-                        uploadPath="/advertisements"
-                        uploadButtonText="上传广告图片"
-                        previewSize="medium"
-                        placeholder="选择广告图片"
-                      />
-                    </FormControl>
-                    <FormDescription>推荐尺寸:1200x400px,支持jpg、png格式</FormDescription>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="url"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel>跳转链接</FormLabel>
-                    <FormControl>
-                      <Input placeholder="请输入跳转链接" {...field} />
-                    </FormControl>
-                    <FormDescription>点击广告后跳转的URL地址</FormDescription>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <div className="grid grid-cols-2 gap-4">
+          {isCreateForm ? (
+            // 创建表单(独立渲染)
+            <Form {...createForm}>
+              <form onSubmit={createForm.handleSubmit(handleCreateSubmit)} className="space-y-4">
+                <FormField
+                  control={createForm.control}
+                  name="title"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel className="flex items-center">
+                        标题 <span className="text-red-500 ml-1">*</span>
+                      </FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入广告标题" {...field} />
+                      </FormControl>
+                      <FormDescription>广告显示的标题文本,最多30个字符</FormDescription>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
                 <FormField
-                  control={(isCreateForm ? createForm : updateForm).control}
-                  name="actionType"
+                  control={createForm.control}
+                  name="typeId"
                   render={({ field }) => (
                     <FormItem>
-                      <FormLabel>跳转类型</FormLabel>
+                      <FormLabel className="flex items-center">
+                        广告类型 <span className="text-red-500 ml-1">*</span>
+                      </FormLabel>
                       <FormControl>
-                        <select 
-                          {...field} 
+                        <select
+                          {...field}
+                          className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
+                          value={field.value || ''}
+                          onChange={(e) => field.onChange(parseInt(e.target.value))}
+                        >
+                          <option value="">请选择广告类型</option>
+                          {advertisementTypes?.data.map(type => (
+                            <option key={type.id} value={type.id}>{type.name}</option>
+                          ))}
+                        </select>
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={createForm.control}
+                  name="code"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel className="flex items-center">
+                        调用别名 <span className="text-red-500 ml-1">*</span>
+                      </FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入调用别名" {...field} />
+                      </FormControl>
+                      <FormDescription>用于程序调用的唯一标识,最多20个字符</FormDescription>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={createForm.control}
+                  name="imageFileId"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>广告图片</FormLabel>
+                      <FormControl>
+                        <AvatarSelector
+                          value={field.value || undefined}
+                          onChange={field.onChange}
+                          maxSize={2}
+                          uploadPath="/advertisements"
+                          uploadButtonText="上传广告图片"
+                          previewSize="medium"
+                          placeholder="选择广告图片"
+                        />
+                      </FormControl>
+                      <FormDescription>推荐尺寸:1200x400px,支持jpg、png格式</FormDescription>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={createForm.control}
+                  name="url"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>跳转链接</FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入跳转链接" {...field} />
+                      </FormControl>
+                      <FormDescription>点击广告后跳转的URL地址</FormDescription>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={createForm.control}
+                    name="actionType"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>跳转类型</FormLabel>
+                        <FormControl>
+                          <select
+                            {...field}
+                            className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
+                            value={field.value || 1}
+                            onChange={(e) => field.onChange(parseInt(e.target.value))}
+                          >
+                            <option value={0}>不跳转</option>
+                            <option value={1}>Web页面</option>
+                            <option value={2}>小程序页面</option>
+                          </select>
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="sort"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>排序值</FormLabel>
+                        <FormControl>
+                          <Input
+                            type="number"
+                            placeholder="排序值"
+                            {...field}
+                            onChange={(e) => field.onChange(parseInt(e.target.value))}
+                          />
+                        </FormControl>
+                        <FormDescription>数值越大排序越靠前</FormDescription>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <FormField
+                  control={createForm.control}
+                  name="status"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>状态</FormLabel>
+                      <FormControl>
+                        <select
+                          {...field}
                           className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
                           value={field.value || 1}
                           onChange={(e) => field.onChange(parseInt(e.target.value))}
                         >
-                          <option value={0}>不跳转</option>
-                          <option value={1}>Web页面</option>
-                          <option value={2}>小程序页面</option>
+                          <option value={1}>启用</option>
+                          <option value={0}>禁用</option>
                         </select>
                       </FormControl>
                       <FormMessage />
@@ -474,59 +530,196 @@ export const AdvertisementsPage = () => {
                   )}
                 />
 
+                <DialogFooter>
+                  <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+                    取消
+                  </Button>
+                  <Button type="submit" disabled={createMutation.isPending}>
+                    创建
+                  </Button>
+                </DialogFooter>
+              </form>
+            </Form>
+          ) : (
+            // 编辑表单(独立渲染)
+            <Form {...updateForm}>
+              <form onSubmit={updateForm.handleSubmit(handleUpdateSubmit)} className="space-y-4">
                 <FormField
-                  control={(isCreateForm ? createForm : updateForm).control}
-                  name="sort"
+                  control={updateForm.control}
+                  name="title"
                   render={({ field }) => (
                     <FormItem>
-                      <FormLabel>排序值</FormLabel>
+                      <FormLabel className="flex items-center">
+                        标题 <span className="text-red-500 ml-1">*</span>
+                      </FormLabel>
                       <FormControl>
-                        <Input 
-                          type="number" 
-                          placeholder="排序值" 
-                          {...field} 
+                        <Input placeholder="请输入广告标题" {...field} />
+                      </FormControl>
+                      <FormDescription>广告显示的标题文本,最多30个字符</FormDescription>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={updateForm.control}
+                  name="typeId"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel className="flex items-center">
+                        广告类型 <span className="text-red-500 ml-1">*</span>
+                      </FormLabel>
+                      <FormControl>
+                        <select
+                          {...field}
+                          className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
+                          value={field.value || ''}
                           onChange={(e) => field.onChange(parseInt(e.target.value))}
+                        >
+                          <option value="">请选择广告类型</option>
+                          {advertisementTypes?.data.map(type => (
+                            <option key={type.id} value={type.id}>{type.name}</option>
+                          ))}
+                        </select>
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={updateForm.control}
+                  name="code"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel className="flex items-center">
+                        调用别名 <span className="text-red-500 ml-1">*</span>
+                      </FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入调用别名" {...field} />
+                      </FormControl>
+                      <FormDescription>用于程序调用的唯一标识,最多20个字符</FormDescription>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={updateForm.control}
+                  name="imageFileId"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>广告图片</FormLabel>
+                      <FormControl>
+                        <AvatarSelector
+                          value={field.value || undefined}
+                          onChange={field.onChange}
+                          maxSize={2}
+                          uploadPath="/advertisements"
+                          uploadButtonText="上传广告图片"
+                          previewSize="medium"
+                          placeholder="选择广告图片"
                         />
                       </FormControl>
-                      <FormDescription>数值越大排序越靠前</FormDescription>
+                      <FormDescription>推荐尺寸:1200x400px,支持jpg、png格式</FormDescription>
                       <FormMessage />
                     </FormItem>
                   )}
                 />
-              </div>
 
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="status"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel>状态</FormLabel>
-                    <FormControl>
-                      <select 
-                        {...field} 
-                        className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
-                        value={field.value || 1}
-                        onChange={(e) => field.onChange(parseInt(e.target.value))}
-                      >
-                        <option value={1}>启用</option>
-                        <option value={0}>禁用</option>
-                      </select>
-                    </FormControl>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <DialogFooter>
-                <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
-                  取消
-                </Button>
-                <Button type="submit" disabled={(isCreateForm ? createMutation : updateMutation).isPending}>
-                  {isCreateForm ? '创建' : '更新'}
-                </Button>
-              </DialogFooter>
-            </form>
-          </Form>
+                <FormField
+                  control={updateForm.control}
+                  name="url"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>跳转链接</FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入跳转链接" {...field} />
+                      </FormControl>
+                      <FormDescription>点击广告后跳转的URL地址</FormDescription>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={updateForm.control}
+                    name="actionType"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>跳转类型</FormLabel>
+                        <FormControl>
+                          <select
+                            {...field}
+                            className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
+                            value={field.value || 1}
+                            onChange={(e) => field.onChange(parseInt(e.target.value))}
+                          >
+                            <option value={0}>不跳转</option>
+                            <option value={1}>Web页面</option>
+                            <option value={2}>小程序页面</option>
+                          </select>
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={updateForm.control}
+                    name="sort"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>排序值</FormLabel>
+                        <FormControl>
+                          <Input
+                            type="number"
+                            placeholder="排序值"
+                            {...field}
+                            onChange={(e) => field.onChange(parseInt(e.target.value))}
+                          />
+                        </FormControl>
+                        <FormDescription>数值越大排序越靠前</FormDescription>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <FormField
+                  control={updateForm.control}
+                  name="status"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>状态</FormLabel>
+                      <FormControl>
+                        <select
+                          {...field}
+                          className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
+                          value={field.value || 1}
+                          onChange={(e) => field.onChange(parseInt(e.target.value))}
+                        >
+                          <option value={1}>启用</option>
+                          <option value={0}>禁用</option>
+                        </select>
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <DialogFooter>
+                  <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+                    取消
+                  </Button>
+                  <Button type="submit" disabled={updateMutation.isPending}>
+                    更新
+                  </Button>
+                </DialogFooter>
+              </form>
+            </Form>
+          )}
         </DialogContent>
       </Dialog>