diff --git a/controller/model_meta.go b/controller/model_meta.go index fd3626442a..c3d9954677 100644 --- a/controller/model_meta.go +++ b/controller/model_meta.go @@ -17,15 +17,15 @@ import ( func GetAllModelsMeta(c *gin.Context) { pageInfo := common.GetPageQuery(c) - modelsMeta, err := model.GetAllModels(pageInfo.GetStartIdx(), pageInfo.GetPageSize()) + status := c.Query("status") + syncOfficial := c.Query("sync_official") + modelsMeta, total, err := model.SearchModels("", "", status, syncOfficial, pageInfo.GetStartIdx(), pageInfo.GetPageSize()) if err != nil { common.ApiError(c, err) return } // 批量填充附加字段,提升列表接口性能 enrichModels(modelsMeta) - var total int64 - model.DB.Model(&model.Model{}).Count(&total) // 统计供应商计数(全部数据,不受分页影响) vendorCounts, _ := model.GetVendorModelCounts() @@ -46,18 +46,27 @@ func SearchModelsMeta(c *gin.Context) { keyword := c.Query("keyword") vendor := c.Query("vendor") + status := c.Query("status") + syncOfficial := c.Query("sync_official") pageInfo := common.GetPageQuery(c) - modelsMeta, total, err := model.SearchModels(keyword, vendor, pageInfo.GetStartIdx(), pageInfo.GetPageSize()) + modelsMeta, total, err := model.SearchModels(keyword, vendor, status, syncOfficial, pageInfo.GetStartIdx(), pageInfo.GetPageSize()) if err != nil { common.ApiError(c, err) return } // 批量填充附加字段,提升列表接口性能 enrichModels(modelsMeta) + vendorCounts, _ := model.GetVendorModelCounts() pageInfo.SetTotal(int(total)) pageInfo.SetItems(modelsMeta) - common.ApiSuccess(c, pageInfo) + common.ApiSuccess(c, gin.H{ + "items": modelsMeta, + "total": total, + "page": pageInfo.GetPage(), + "page_size": pageInfo.GetPageSize(), + "vendor_counts": vendorCounts, + }) } // GetModelMeta 根据 ID 获取单条模型信息 diff --git a/model/model_meta.go b/model/model_meta.go index 8642127716..bd701e2bf7 100644 --- a/model/model_meta.go +++ b/model/model_meta.go @@ -105,8 +105,7 @@ func GetVendorModelCounts() (map[int64]int64, error) { } func GetAllModels(offset int, limit int) ([]*Model, error) { - var models []*Model - err := DB.Order("id DESC").Offset(offset).Limit(limit).Find(&models).Error + models, _, err := SearchModels("", "", "", "", offset, limit) return models, err } @@ -192,7 +191,7 @@ func GetPreferredModelOwnerChannelTypes(modelNames []string, groups []string) (m return result, nil } -func SearchModels(keyword string, vendor string, offset int, limit int) ([]*Model, int64, error) { +func SearchModels(keyword string, vendor string, status string, syncOfficial string, offset int, limit int) ([]*Model, int64, error) { var models []*Model db := DB.Model(&Model{}) if keyword != "" { @@ -206,6 +205,12 @@ func SearchModels(keyword string, vendor string, offset int, limit int) ([]*Mode db = db.Joins("JOIN vendors ON vendors.id = models.vendor_id").Where("vendors.name LIKE ?", "%"+vendor+"%") } } + if statusValue, ok := parseModelStatusFilter(status); ok { + db = db.Where("models.status = ?", statusValue) + } + if syncValue, ok := parseModelSyncFilter(syncOfficial); ok { + db = db.Where("models.sync_official = ?", syncValue) + } var total int64 if err := db.Count(&total).Error; err != nil { return nil, 0, err @@ -215,3 +220,41 @@ func SearchModels(keyword string, vendor string, offset int, limit int) ([]*Mode } return models, total, nil } + +// parseModelStatusFilter maps UI/API status values to the models.status column. +// Returns ok=false when no status filter should be applied. +func parseModelStatusFilter(status string) (value int, ok bool) { + switch strings.ToLower(strings.TrimSpace(status)) { + case "", "all": + return 0, false + case "enabled", "1": + return 1, true + case "disabled", "0": + return 0, true + default: + n, err := strconv.Atoi(status) + if err != nil { + return 0, false + } + return n, true + } +} + +// parseModelSyncFilter maps UI/API sync values to the models.sync_official column. +// Returns ok=false when no sync filter should be applied. +func parseModelSyncFilter(syncOfficial string) (value int, ok bool) { + switch strings.ToLower(strings.TrimSpace(syncOfficial)) { + case "", "all": + return 0, false + case "yes", "1": + return 1, true + case "no", "0": + return 0, true + default: + n, err := strconv.Atoi(syncOfficial) + if err != nil { + return 0, false + } + return n, true + } +} diff --git a/web/default/src/features/models/components/models-table.tsx b/web/default/src/features/models/components/models-table.tsx index dd76d3d5e3..f0c31c3261 100644 --- a/web/default/src/features/models/components/models-table.tsx +++ b/web/default/src/features/models/components/models-table.tsx @@ -93,9 +93,6 @@ export function ModelsTable() { })) }, [vendors]) - // Determine whether to use search or regular list API - const shouldSearch = Boolean(globalFilter?.trim()) - // Apply selected vendor from context or filter const activeVendorFilter = selectedVendor || @@ -103,55 +100,50 @@ export function ModelsTable() { ? vendorFilter[0] : undefined) + const statusFilterValue = + statusFilter.length > 0 && !statusFilter.includes('all') + ? statusFilter[0] + : undefined + const syncFilterValue = + syncFilter.length > 0 && !syncFilter.includes('all') + ? syncFilter[0] + : undefined + + // Use search API whenever any filter is active so status/sync are applied server-side + const shouldSearch = Boolean( + globalFilter?.trim() || + activeVendorFilter || + statusFilterValue || + syncFilterValue + ) + // Fetch models data // eslint-disable-next-line @tanstack/query/exhaustive-deps const { data, isLoading, isFetching } = useQuery({ queryKey: modelsQueryKeys.list({ keyword: globalFilter, vendor: activeVendorFilter, - status: - statusFilter.length > 0 && !statusFilter.includes('all') - ? statusFilter[0] - : undefined, - sync_official: - syncFilter.length > 0 && !syncFilter.includes('all') - ? syncFilter[0] - : undefined, + status: statusFilterValue, + sync_official: syncFilterValue, p: pagination.pageIndex + 1, page_size: pagination.pageSize, }), queryFn: async () => { - if (shouldSearch || activeVendorFilter) { + if (shouldSearch) { return searchModels({ keyword: globalFilter, vendor: activeVendorFilter, - status: - statusFilter.length > 0 && !statusFilter.includes('all') - ? statusFilter[0] - : undefined, - sync_official: - syncFilter.length > 0 && !syncFilter.includes('all') - ? syncFilter[0] - : undefined, - p: pagination.pageIndex + 1, - page_size: pagination.pageSize, - }) - } else { - return getModels({ - status: - statusFilter.length > 0 && !statusFilter.includes('all') - ? statusFilter[0] - : undefined, - sync_official: - syncFilter.length > 0 && !syncFilter.includes('all') - ? syncFilter[0] - : undefined, + status: statusFilterValue, + sync_official: syncFilterValue, p: pagination.pageIndex + 1, page_size: pagination.pageSize, }) } + return getModels({ + p: pagination.pageIndex + 1, + page_size: pagination.pageSize, + }) }, - placeholderData: (previousData) => previousData, }) const models = data?.data?.items || []