Pārlūkot izejas kodu

♻️ refactor(advertisement): split create and update forms for better maintainability

- 拆分原handleSubmit为handleCreateSubmit和handleUpdateSubmit两个独立函数
- 将创建和编辑表单拆分为独立渲染逻辑,消除条件判断
- 为每个表单添加独立的加载状态和提交文本
- 为表单提交添加错误捕获和提示

🐛 fix(component): handle null values in Textarea component

- 添加TextareaProps接口定义,支持null值处理
- 修复value为null时的显示问题,转换为undefined避免异常
- 保持原有功能的同时增强组件健壮性
yourname 4 mēneši atpakaļ
vecāks
revīzija
e3b41c80b9

+ 192 - 91
src/client/admin-shadcn/pages/AdvertisementTypes.tsx

@@ -176,11 +176,21 @@ export const AdvertisementTypesPage = () => {
     }
   }
 
-  const handleSubmit = (data: CreateRequest | UpdateRequest) => {
-    if (isCreateForm) {
-      createMutation.mutate(data as CreateRequest)
-    } else if (editingType) {
-      updateMutation.mutate({ id: editingType.id, data: data as UpdateRequest })
+  const handleCreateSubmit = async (data: CreateRequest) => {
+    try {
+      createMutation.mutate(data)
+    } catch (error) {
+      toast.error('创建失败,请重试')
+    }
+  }
+
+  const handleUpdateSubmit = async (data: UpdateRequest) => {
+    if (!editingType) return
+    
+    try {
+      updateMutation.mutate({ id: editingType.id, data })
+    } catch (error) {
+      toast.error('更新失败,请重试')
     }
   }
 
@@ -330,92 +340,183 @@ export const AdvertisementTypesPage = () => {
             </DialogDescription>
           </DialogHeader>
 
-          <Form {...(isCreateForm ? createForm : updateForm)}>
-            <form onSubmit={(isCreateForm ? createForm : updateForm).handleSubmit(handleSubmit)} className="space-y-4">
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="name"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel className="flex items-center">
-                      类型名称 <span className="text-red-500 ml-1">*</span>
-                    </FormLabel>
-                    <FormControl>
-                      <Input placeholder="请输入类型名称" {...field} />
-                    </FormControl>
-                    <FormDescription>例如:首页轮播、侧边广告等</FormDescription>
-                    <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>用于程序调用的唯一标识,建议使用英文小写和下划线</FormDescription>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="remark"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel>备注</FormLabel>
-                    <FormControl>
-                      <Textarea 
-                        placeholder="请输入备注信息(可选)"
-                        className="resize-none"
-                        {...field} 
-                      />
-                    </FormControl>
-                    <FormDescription>对广告类型的详细描述</FormDescription>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="status"
-                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={field.onChange}
-                      />
-                    </FormControl>
-                  </FormItem>
-                )}
-              />
-
-              <DialogFooter>
-                <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
-                  取消
-                </Button>
-                <Button type="submit" disabled={createMutation.isPending || updateMutation.isPending}>
-                  {createMutation.isPending || updateMutation.isPending ? '处理中...' : (isCreateForm ? '创建' : '更新')}
-                </Button>
-              </DialogFooter>
-            </form>
-          </Form>
+          {isCreateForm ? (
+            // 创建表单(独立渲染)
+            <Form {...createForm}>
+              <form onSubmit={createForm.handleSubmit(handleCreateSubmit)} className="space-y-4">
+                <FormField
+                  control={createForm.control}
+                  name="name"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel className="flex items-center">
+                        类型名称 <span className="text-red-500 ml-1">*</span>
+                      </FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入类型名称" {...field} />
+                      </FormControl>
+                      <FormDescription>例如:首页轮播、侧边广告等</FormDescription>
+                      <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>用于程序调用的唯一标识,建议使用英文小写和下划线</FormDescription>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={createForm.control}
+                  name="remark"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>备注</FormLabel>
+                      <FormControl>
+                        <Textarea
+                          placeholder="请输入备注信息(可选)"
+                          className="resize-none"
+                          {...field}
+                        />
+                      </FormControl>
+                      <FormDescription>对广告类型的详细描述</FormDescription>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={createForm.control}
+                  name="status"
+                  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={field.onChange}
+                        />
+                      </FormControl>
+                    </FormItem>
+                  )}
+                />
+
+                <DialogFooter>
+                  <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+                    取消
+                  </Button>
+                  <Button type="submit" disabled={createMutation.isPending}>
+                    {createMutation.isPending ? '创建中...' : '创建'}
+                  </Button>
+                </DialogFooter>
+              </form>
+            </Form>
+          ) : (
+            // 编辑表单(独立渲染)
+            <Form {...updateForm}>
+              <form onSubmit={updateForm.handleSubmit(handleUpdateSubmit)} className="space-y-4">
+                <FormField
+                  control={updateForm.control}
+                  name="name"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel className="flex items-center">
+                        类型名称 <span className="text-red-500 ml-1">*</span>
+                      </FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入类型名称" {...field} />
+                      </FormControl>
+                      <FormDescription>例如:首页轮播、侧边广告等</FormDescription>
+                      <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>用于程序调用的唯一标识,建议使用英文小写和下划线</FormDescription>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={updateForm.control}
+                  name="remark"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>备注</FormLabel>
+                      <FormControl>
+                        <Textarea
+                          placeholder="请输入备注信息(可选)"
+                          className="resize-none"
+                          {...field}
+                        />
+                      </FormControl>
+                      <FormDescription>对广告类型的详细描述</FormDescription>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={updateForm.control}
+                  name="status"
+                  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={field.onChange}
+                        />
+                      </FormControl>
+                    </FormItem>
+                  )}
+                />
+
+                <DialogFooter>
+                  <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+                    取消
+                  </Button>
+                  <Button type="submit" disabled={updateMutation.isPending}>
+                    {updateMutation.isPending ? '更新中...' : '更新'}
+                  </Button>
+                </DialogFooter>
+              </form>
+            </Form>
+          )}
         </DialogContent>
       </Dialog>
 

+ 6 - 1
src/client/components/ui/textarea.tsx

@@ -2,7 +2,11 @@ import * as React from "react"
 
 import { cn } from "@/client/lib/utils"
 
-function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
+interface TextareaProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'value'> {
+  value?: string | null
+}
+
+function Textarea({ className, value, ...props }: TextareaProps) {
   return (
     <textarea
       data-slot="textarea"
@@ -10,6 +14,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
         "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
         className
       )}
+      value={value === null ? undefined : value}
       {...props}
     />
   )