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({