diff --git a/api/admin/v1/channel.go b/api/admin/v1/channel.go
index a51f580..6ad4c42 100644
--- a/api/admin/v1/channel.go
+++ b/api/admin/v1/channel.go
@@ -9,7 +9,9 @@ type ChannelListReq struct {
PageSize int `json:"page_size" d:"20" v:"min:1|max:100" dc:"每页数量"`
Type int `json:"type" dc:"供应商类型筛选"`
Status string `json:"status" dc:"状态筛选:active/disabled/testing"`
- Search string `json:"search" dc:"搜索关键词"`
+ Search string `json:"search" dc:"搜索关键词(渠道名称/备注)"`
+ ID int64 `json:"id" dc:"按渠道 ID 精确筛选"`
+ Model string `json:"model" dc:"按支持的模型名筛选(模糊匹配平台模型名/上游模型名)"`
}
// ChannelListRes 渠道列表响应
@@ -250,7 +252,9 @@ type ChannelExportReq struct {
Format string `json:"format" in:"query" d:"csv" v:"in:csv,xlsx" dc:"导出格式:csv / xlsx"`
Type int `json:"type" in:"query" dc:"供应商类型筛选"`
Status string `json:"status" in:"query" dc:"状态筛选:active/disabled/testing"`
- Search string `json:"search" in:"query" dc:"搜索关键词"`
+ Search string `json:"search" in:"query" dc:"搜索关键词(渠道名称/备注)"`
+ ID int64 `json:"id" in:"query" dc:"按渠道 ID 精确筛选"`
+ Model string `json:"model" in:"query" dc:"按支持的模型名筛选(模糊匹配平台模型名/上游模型名)"`
}
type ChannelExportRes struct{}
diff --git a/internal/logic/admin/channel.go b/internal/logic/admin/channel.go
index f30f304..30b63e3 100644
--- a/internal/logic/admin/channel.go
+++ b/internal/logic/admin/channel.go
@@ -58,6 +58,14 @@ func (s *sAdmin) ListChannels(ctx context.Context, req *v1.ChannelListReq) (*v1.
if req.Search != "" {
query = query.Where("chn_channels.name LIKE ? OR chn_channels.remark LIKE ?", "%"+req.Search+"%", "%"+req.Search+"%")
}
+ if req.ID > 0 {
+ query = query.Where("chn_channels.id", req.ID)
+ }
+ if req.Model != "" {
+ // 按支持的模型名筛选:渠道需具备匹配的模型能力(chn_abilities)。
+ // 用 EXISTS 相关子查询而非 JOIN,避免一个渠道命中多个模型时产生重复行、破坏分页与总数。
+ query = query.Where("EXISTS (SELECT 1 FROM chn_abilities WHERE chn_abilities.channel_id = chn_channels.id AND (chn_abilities.model_name LIKE ? OR chn_abilities.upstream_model LIKE ?))", "%"+req.Model+"%", "%"+req.Model+"%")
+ }
var total int
var channels []struct {
@@ -731,6 +739,13 @@ func (s *sAdmin) ExportChannels(ctx context.Context, req *v1.ChannelExportReq) (
if req.Search != "" {
query = query.Where("chn_channels.name LIKE ? OR chn_channels.remark LIKE ?", "%"+req.Search+"%", "%"+req.Search+"%")
}
+ if req.ID > 0 {
+ query = query.Where("chn_channels.id", req.ID)
+ }
+ if req.Model != "" {
+ // 与 ListChannels 保持一致:EXISTS 相关子查询避免 JOIN 重复行
+ query = query.Where("EXISTS (SELECT 1 FROM chn_abilities WHERE chn_abilities.channel_id = chn_channels.id AND (chn_abilities.model_name LIKE ? OR chn_abilities.upstream_model LIKE ?))", "%"+req.Model+"%", "%"+req.Model+"%")
+ }
var batch []struct {
ID int64 `json:"id"`
Name string `json:"name"`
diff --git a/web/admin/src/styles/main.css b/web/admin/src/styles/main.css
index 2533df7..56a536f 100644
--- a/web/admin/src/styles/main.css
+++ b/web/admin/src/styles/main.css
@@ -449,6 +449,16 @@ body {
border-top: 1px solid var(--ta-border-light);
}
+/* 统计总数从表格左上角移到底部后:footer 内存在统计栏时两端对齐(统计居左、分页居右),
+ 无统计栏的 footer 保持右对齐不受影响(:has 特异性高于 scoped .table-footer,可统一覆盖各页脚注) */
+.page-table .table-footer:has(.table-stats) {
+ justify-content: space-between;
+}
+/* 统计栏移入底部后,去掉全局样式的下边距,与分页栏垂直居中 */
+.page-table .table-footer .table-stats {
+ margin-bottom: 0;
+}
+
/* Tab 内嵌表格:让 Tab 容器与面板也参与纵向 flex,保证内部表格能撑满滚动 */
.page-table .arco-tabs {
flex: 1;
diff --git a/web/admin/src/views/ai/ChannelsPage.vue b/web/admin/src/views/ai/ChannelsPage.vue
index 4fa6ed1..61cbd79 100644
--- a/web/admin/src/views/ai/ChannelsPage.vue
+++ b/web/admin/src/views/ai/ChannelsPage.vue
@@ -1,7 +1,7 @@
@@ -356,15 +449,17 @@ const { exporting, exportFile } = useExport({
+
+
搜索
-
@@ -522,11 +617,17 @@ const { exporting, exportFile } = useExport({