ソースを参照

✨ feat(goods-category): add sort functionality for goods categories

- add sort field to goods category entity and DTOs
- add sort input in create and edit forms
- add sort column in management table
- update API calls to include sort parameters
- set default sort to 'sort' field in ascending order
- update category list to sort by sort field in descending order

♻️ refactor(category-page): update category list sorting

- change category list sort field from 'id' to 'sort'
- change sort order from ascending to descending
yourname 1 ヶ月 前
コミット
7c95f6ce81

+ 2 - 2
mini/src/pages/category/index.tsx

@@ -35,8 +35,8 @@ const CategoryPage: React.FC = () => {
           page: 1,
           page: 1,
           pageSize: 100,
           pageSize: 100,
           filters: JSON.stringify({ level: 1, state: 1 }), // 只显示启用的分类
           filters: JSON.stringify({ level: 1, state: 1 }), // 只显示启用的分类
-          sortBy: 'id',
-          sortOrder: 'ASC'
+          sortBy: 'sort',
+          sortOrder: 'DESC'
         }
         }
       });
       });
       if (response.status !== 200) {
       if (response.status !== 200) {

+ 21 - 0
packages/goods-category-management-ui-mt/src/components/GoodsCategoryForm.tsx

@@ -63,6 +63,7 @@ export const GoodsCategoryForm: React.FC<GoodsCategoryFormProps> = ({
       level: category.level,
       level: category.level,
       state: category.state,
       state: category.state,
       imageFileId: category.imageFileId,
       imageFileId: category.imageFileId,
+      sort: category.sort
     } : {
     } : {
       tenantId: 1, // 测试环境使用默认tenantId
       tenantId: 1, // 测试环境使用默认tenantId
       parentId: smartParentId || 0,
       parentId: smartParentId || 0,
@@ -70,6 +71,7 @@ export const GoodsCategoryForm: React.FC<GoodsCategoryFormProps> = ({
       level: smartLevel ?? 0,
       level: smartLevel ?? 0,
       state: CategoryState.ENABLED,
       state: CategoryState.ENABLED,
       imageFileId: null,
       imageFileId: null,
+      sort: 0
     },
     },
   });
   });
 
 
@@ -148,6 +150,25 @@ export const GoodsCategoryForm: React.FC<GoodsCategoryFormProps> = ({
             )}
             )}
           />
           />
 
 
+          <FormField
+              control={form.control}
+              name="sort"
+              render={({ field }) => (
+              <FormItem>
+                <FormLabel>排序</FormLabel>
+                <FormControl>
+                  <Input
+                    type="number"
+                    placeholder="请输入排序"
+                    value={field.value ?? ''}
+                    onChange={(e) => field.onChange(e.target.value === '' ? undefined : Number(e.target.value))}
+                  />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+              )}
+              />
+
           {/* 状态选择 */}
           {/* 状态选择 */}
           <FormField
           <FormField
             control={form.control}
             control={form.control}

+ 41 - 1
packages/goods-category-management-ui-mt/src/components/GoodsCategoryManagement.tsx

@@ -30,7 +30,13 @@ const updateFormSchema = UpdateGoodsCategoryDto;
 
 
 export const GoodsCategoryManagement = () => {
 export const GoodsCategoryManagement = () => {
   // 状态管理
   // 状态管理
-  const [searchParams, setSearchParams] = useState({ page: 1, limit: 10, search: '' });
+  const [searchParams, setSearchParams] = useState({
+    page: 1,
+    limit: 10,
+    search: '',
+    sortBy: 'sort',
+    sortOrder: 'ASC' as 'ASC' | 'DESC'
+  });
   const [isModalOpen, setIsModalOpen] = useState(false);
   const [isModalOpen, setIsModalOpen] = useState(false);
   const [editingCategory, setEditingCategory] = useState<GoodsCategoryResponse | null>(null);
   const [editingCategory, setEditingCategory] = useState<GoodsCategoryResponse | null>(null);
   const [isCreateForm, setIsCreateForm] = useState(true);
   const [isCreateForm, setIsCreateForm] = useState(true);
@@ -46,6 +52,7 @@ export const GoodsCategoryManagement = () => {
       imageFileId: null,
       imageFileId: null,
       level: 0,
       level: 0,
       state: 1,
       state: 1,
+      sort: 0
     },
     },
   });
   });
 
 
@@ -62,6 +69,8 @@ export const GoodsCategoryManagement = () => {
           page: searchParams.page,
           page: searchParams.page,
           pageSize: searchParams.limit,
           pageSize: searchParams.limit,
           keyword: searchParams.search,
           keyword: searchParams.search,
+          sortBy: searchParams.sortBy,
+          sortOrder: searchParams.sortOrder,
           filters: JSON.stringify({state:[1,2]})
           filters: JSON.stringify({state:[1,2]})
         },
         },
       });
       });
@@ -86,6 +95,7 @@ export const GoodsCategoryManagement = () => {
       imageFileId: null,
       imageFileId: null,
       level: 0,
       level: 0,
       state: 1,
       state: 1,
+      sort: 0,
     });
     });
     setIsModalOpen(true);
     setIsModalOpen(true);
   };
   };
@@ -100,6 +110,7 @@ export const GoodsCategoryManagement = () => {
       imageFileId: category.imageFileId,
       imageFileId: category.imageFileId,
       level: category.level,
       level: category.level,
       state: category.state,
       state: category.state,
+      sort: category.sort,
     });
     });
     setIsModalOpen(true);
     setIsModalOpen(true);
   };
   };
@@ -239,6 +250,7 @@ export const GoodsCategoryManagement = () => {
                   <TableHead>分类名称</TableHead>
                   <TableHead>分类名称</TableHead>
                   <TableHead>上级ID</TableHead>
                   <TableHead>上级ID</TableHead>
                   <TableHead>层级</TableHead>
                   <TableHead>层级</TableHead>
+                  <TableHead>排序</TableHead>
                   <TableHead>状态</TableHead>
                   <TableHead>状态</TableHead>
                   <TableHead>图片</TableHead>
                   <TableHead>图片</TableHead>
                   <TableHead>创建时间</TableHead>
                   <TableHead>创建时间</TableHead>
@@ -374,6 +386,20 @@ export const GoodsCategoryManagement = () => {
                     </FormItem>
                     </FormItem>
                   )}
                   )}
                 />
                 />
+                
+                <FormField
+                    control={createForm.control}
+                    name="sort"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>排序</FormLabel>
+                        <FormControl>
+                          <Input type="number" placeholder="请输入排序" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
 
 
                 <FormField
                 <FormField
                   control={createForm.control}
                   control={createForm.control}
@@ -485,6 +511,20 @@ export const GoodsCategoryManagement = () => {
                   )}
                   )}
                 />
                 />
 
 
+                <FormField
+                    control={updateForm.control}
+                    name="sort"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>排序</FormLabel>
+                        <FormControl>
+                          <Input type="number" placeholder="请输入排序" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
                 <FormField
                 <FormField
                   control={updateForm.control}
                   control={updateForm.control}
                   name="parentId"
                   name="parentId"

+ 2 - 1
packages/goods-category-management-ui-mt/src/components/GoodsCategoryTreeManagement.tsx

@@ -369,7 +369,8 @@ export const GoodsCategoryTreeManagement: React.FC = () => {
                 name: selectedCategory.name,
                 name: selectedCategory.name,
                 level: selectedCategory.level,
                 level: selectedCategory.level,
                 state: selectedCategory.state,
                 state: selectedCategory.state,
-                imageFileId: selectedCategory.imageFileId
+                imageFileId: selectedCategory.imageFileId,
+                sort: selectedCategory.sort
               }}
               }}
               onSubmit={handleUpdateCategory}
               onSubmit={handleUpdateCategory}
               isLoading={updateMutation.isPending}
               isLoading={updateMutation.isPending}

+ 7 - 0
packages/goods-category-management-ui-mt/tests/integration/goods-category-management.integration.test.tsx

@@ -97,6 +97,7 @@ describe('商品分类管理集成测试', () => {
           imageFileId: null,
           imageFileId: null,
           level: 0,
           level: 0,
           state: 1,
           state: 1,
+          sort: 0,
           createdAt: '2024-01-01T00:00:00Z',
           createdAt: '2024-01-01T00:00:00Z',
           updatedAt: '2024-01-01T00:00:00Z',
           updatedAt: '2024-01-01T00:00:00Z',
           createdBy: 1,
           createdBy: 1,
@@ -150,6 +151,7 @@ describe('商品分类管理集成测试', () => {
           imageFileId: null,
           imageFileId: null,
           level: 0,
           level: 0,
           state: 1,
           state: 1,
+          sort: 0,
         },
         },
       });
       });
       expect(toast.success).toHaveBeenCalledWith('创建成功');
       expect(toast.success).toHaveBeenCalledWith('创建成功');
@@ -183,6 +185,7 @@ describe('商品分类管理集成测试', () => {
           imageFileId: null,
           imageFileId: null,
           level: 0,
           level: 0,
           state: 1,
           state: 1,
+          sort: 0,
         },
         },
       });
       });
       expect(toast.success).toHaveBeenCalledWith('更新成功');
       expect(toast.success).toHaveBeenCalledWith('更新成功');
@@ -279,6 +282,8 @@ describe('商品分类管理集成测试', () => {
           page: 1,
           page: 1,
           pageSize: 10,
           pageSize: 10,
           keyword: '搜索关键词',
           keyword: '搜索关键词',
+          sortBy: 'sort',
+          sortOrder: 'ASC',
           filters: expect.stringContaining('state'),
           filters: expect.stringContaining('state'),
         },
         },
       });
       });
@@ -295,6 +300,7 @@ describe('商品分类管理集成测试', () => {
           imageFileId: 1,
           imageFileId: 1,
           level: 0,
           level: 0,
           state: 1,
           state: 1,
+          sort: 0,
           createdAt: '2024-01-01T00:00:00Z',
           createdAt: '2024-01-01T00:00:00Z',
           updatedAt: '2024-01-01T00:00:00Z',
           updatedAt: '2024-01-01T00:00:00Z',
           createdBy: 1,
           createdBy: 1,
@@ -311,6 +317,7 @@ describe('商品分类管理集成测试', () => {
           imageFileId: null,
           imageFileId: null,
           level: 0,
           level: 0,
           state: 2,
           state: 2,
+          sort: 0,
           createdAt: '2024-01-01T00:00:00Z',
           createdAt: '2024-01-01T00:00:00Z',
           updatedAt: '2024-01-01T00:00:00Z',
           updatedAt: '2024-01-01T00:00:00Z',
           createdBy: 1,
           createdBy: 1,

+ 14 - 7
packages/goods-category-management-ui-mt/tests/integration/goods-category-tree-management.integration.test.tsx

@@ -101,7 +101,8 @@ describe('商品分类树形管理集成测试', () => {
           parentId: 0,
           parentId: 0,
           level: 1,
           level: 1,
           state: 1,
           state: 1,
-          imageFileId: null
+          imageFileId: null,
+          sort: 0
         }
         }
       ]
       ]
     }));
     }));
@@ -170,7 +171,8 @@ describe('商品分类树形管理集成测试', () => {
           parentId: 0,
           parentId: 0,
           level: 1,
           level: 1,
           state: 1,
           state: 1,
-          imageFileId: null
+          imageFileId: null,
+          sort: 0
         }
         }
       ]
       ]
     }));
     }));
@@ -227,7 +229,8 @@ describe('商品分类树形管理集成测试', () => {
           parentId: 0,
           parentId: 0,
           level: 1,
           level: 1,
           state: 1,
           state: 1,
-          imageFileId: null
+          imageFileId: null,
+          sort: 0
         }
         }
       ]
       ]
     };
     };
@@ -273,7 +276,8 @@ describe('商品分类树形管理集成测试', () => {
           parentId: 0,
           parentId: 0,
           level: 1,
           level: 1,
           state: 1,
           state: 1,
-          imageFileId: null
+          imageFileId: null,
+          sort: 0
         }
         }
       });
       });
       expect(toast.success).toHaveBeenCalledWith('商品分类创建成功');
       expect(toast.success).toHaveBeenCalledWith('商品分类创建成功');
@@ -317,7 +321,8 @@ describe('商品分类树形管理集成测试', () => {
           parentId: 0,
           parentId: 0,
           level: 1,
           level: 1,
           state: 1,
           state: 1,
-          imageFileId: null
+          imageFileId: null,
+          sort: 0
         }
         }
       ]
       ]
     };
     };
@@ -368,7 +373,8 @@ describe('商品分类树形管理集成测试', () => {
           parentId: 0,
           parentId: 0,
           level: 1,
           level: 1,
           state: 1,
           state: 1,
-          imageFileId: null
+          imageFileId: null,
+          sort: 0
         },
         },
         {
         {
           id: 2,
           id: 2,
@@ -482,7 +488,8 @@ describe('商品分类树形管理集成测试', () => {
           parentId: 3,
           parentId: 3,
           level: 3,
           level: 3,
           state: 1,
           state: 1,
-          imageFileId: null
+          imageFileId: null,
+          sort: 0
         }
         }
       });
       });
       expect(toast.success).toHaveBeenCalledWith('商品分类创建成功');
       expect(toast.success).toHaveBeenCalledWith('商品分类创建成功');

+ 3 - 0
packages/goods-module-mt/src/entities/goods-category.entity.mt.ts

@@ -37,6 +37,9 @@ export class GoodsCategoryMt {
   @Column({ name: 'updated_by', type: 'int', unsigned: true, nullable: true, comment: '更新用户ID' })
   @Column({ name: 'updated_by', type: 'int', unsigned: true, nullable: true, comment: '更新用户ID' })
   updatedBy!: number | null;
   updatedBy!: number | null;
 
 
+  @Column({ name: 'sort', type: 'int', unsigned: true, default: 0, comment: '排序' })
+  sort!: number;
+
   @ManyToOne(() => FileMt, { nullable: true })
   @ManyToOne(() => FileMt, { nullable: true })
   @JoinColumn({ name: 'image_file_id', referencedColumnName: 'id' })
   @JoinColumn({ name: 'image_file_id', referencedColumnName: 'id' })
   imageFile!: FileMt | null;
   imageFile!: FileMt | null;

+ 12 - 0
packages/goods-module-mt/src/schemas/goods-category.schema.mt.ts

@@ -23,6 +23,10 @@ export const GoodsCategorySchema = z.object({
     description: '状态 1可用 2不可用',
     description: '状态 1可用 2不可用',
     example: 1
     example: 1
   }),
   }),
+  sort: z.coerce.number<number>().int().nonnegative('排序值必须为非负数').default(0).openapi({
+    description: '排序',
+    example: 0
+  }),
   imageFile: FileSchema.nullable().optional().openapi({
   imageFile: FileSchema.nullable().optional().openapi({
     description: '分类图片信息'
     description: '分类图片信息'
   }),
   }),
@@ -64,6 +68,10 @@ export const CreateGoodsCategoryDto = z.object({
   state: z.number().int().min(1).max(2).default(1).openapi({
   state: z.number().int().min(1).max(2).default(1).openapi({
     description: '状态 1可用 2不可用',
     description: '状态 1可用 2不可用',
     example: 1
     example: 1
+  }),
+  sort: z.coerce.number<number>().int().nonnegative('排序值必须为非负数').default(0).openapi({
+    description: '排序',
+    example: 0
   })
   })
 });
 });
 
 
@@ -87,5 +95,9 @@ export const UpdateGoodsCategoryDto = z.object({
   state: z.number().int().min(1).max(2).optional().openapi({
   state: z.number().int().min(1).max(2).optional().openapi({
     description: '状态 1可用 2不可用',
     description: '状态 1可用 2不可用',
     example: 1
     example: 1
+  }),
+  sort: z.coerce.number<number>().int().nonnegative('排序值必须为非负数').default(0).openapi({
+    description: '排序',
+    example: 0
   })
   })
 });
 });