fix(channel):修复渠道无法修改协议

This commit is contained in:
chengqian committed 2026-07-31 15:19:01 +08:00
1 parent 6ff5c6e949
commit f4debdfab2
5 files changed
+44 -3

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
# CodeGraph data files — local to each machine, not for committing.
# Ignore everything in .codegraph/ except this file itself, so transient
# files (the database, daemon.pid, sockets, logs) never show up in git.
*
!.gitignore
+3 -3
View File
@@ -11,7 +11,7 @@
- **后端**:Go + GoFrame (v2) + goose(数据库迁移)
- **数据库**:PostgreSQL(唯一,不用 MySQL/SQLite)
- **缓存**:Redis + 内存缓存(双层缓存)
- **前端**:Vue 3 + Vite + TailwindCSS + Naive UI(管理后台)
- **前端**:Vue 3 + Vite + TailwindCSS + Arco Design Vue(管理后台)
- **包管理**:bun
- **对象存储**:S3 / OSS / COS / MinIO(禁止纯本地磁盘作为生产存储)
@@ -383,9 +383,9 @@ wallets 表包含 `frozen_balance` 字段,用于支付中/退款中的金额
### 前端
- Vue 3 Composition API + `<script setup>` 语法
- **管理后台**:使用 **Naive UI** 组件库,快速构建数据密集型页面(表格、表单、图表等),可参考 [Soybean Admin](https://github.com/soybeanjs/soybean-admin) 的架构设计(路由、权限、布局)
- **管理后台**:使用 **Arco Design Vue** 组件库(`@arco-design/web-vue`),快速构建数据密集型页面(表格、表单、图表等),可参考 [Soybean Admin](https://github.com/soybeanjs/soybean-admin) 的架构设计(路由、权限、布局)
- **租户控制台**:TailwindCSS 原子类 + 自建组件,面向客户,注重品牌调性和视觉差异化
- TailwindCSS 作为基础样式层,两个控制台共用(Naive UI 的 CSS-in-JS 方案与 TailwindCSS 无冲突)
- TailwindCSS 作为基础样式层,两个控制台共用(注意与 Arco 组件内置样式共存,避免全局 reset 冲突)
- 状态管理用 Pinia
- HTTP 请求封装 Axios,JWT 自动刷新,统一错误处理
- 按钮级权限控制:无权限的按钮不渲染(`v-if="hasPermission('xxx')"`)
+1
View File
@@ -69,6 +69,7 @@ type ChannelUpdateReq struct {
g.Meta `path:"/channels/{id}" method:"put" mime:"json" tags:"管理后台-渠道" summary:"更新渠道"`
ID int64 `json:"id" in:"path" v:"required" dc:"渠道ID"`
Name string `json:"name" dc:"渠道名称"`
Type *int `json:"type" v:"min:1#请选择供应商类型" dc:"供应商类型(留空不修改)"`
BaseURL string `json:"base_url" dc:"API 基础地址"`
ApiKey *string `json:"api_key" dc:"更新 API Key(留空不更新)"`
Priority int `json:"priority" dc:"优先级"`
+3
View File
@@ -294,6 +294,9 @@ func (s *sAdmin) UpdateChannel(ctx context.Context, req *v1.ChannelUpdateReq) (*
if req.Name != "" {
data.Name = req.Name
}
if req.Type != nil {
data.Type = *req.Type
}
if req.BaseURL != "" {
data.BaseUrl = req.BaseURL
}
@@ -18,6 +18,30 @@ const activeTab = ref('info')
const statusTagColor: Record<string, string> = { active: 'green', disabled: 'orangered', testing: 'arcoblue' }
const statusLabel: Record<string, string> = { active: '启用', disabled: '禁用', testing: '测试中' }
// 供应商类型选项(与渠道列表页保持一致,用于编辑时切换渠道协议类型)
const providerTypeOptions = [
{ label: 'OpenAI', value: 1 }, { label: 'Claude (Anthropic)', value: 2 },
{ label: 'Gemini (Google)', value: 3 }, { label: 'Ali (百炼)', value: 4 },
{ label: 'Tencent (混元)', value: 6 },
{ label: 'Zhipu (智谱)', value: 7 }, { label: 'DeepSeek', value: 8 },
{ label: 'Moonshot', value: 9 }, { label: 'Volcengine (火山)', value: 10 },
{ label: 'AWS Bedrock', value: 11 }, { label: 'Azure OpenAI', value: 12 },
{ label: 'Vertex AI', value: 13 },
{ label: 'Mistral', value: 15 }, { label: 'xAI (Grok)', value: 16 },
{ label: '360 智脑', value: 17 }, { label: 'Lingyi (零一万物)', value: 18 },
{ label: 'Baidu V2', value: 19 }, { label: 'Cloudflare Workers AI', value: 20 },
{ label: 'Ollama', value: 22 },
{ label: 'SiliconFlow (硅基流动)', value: 25 }, { label: 'Xunfei (讯飞)', value: 26 },
{ label: 'OpenRouter', value: 27 }, { label: 'XInference', value: 28 },
{ label: 'MiniMax', value: 29 }, { label: 'Submodel', value: 30 },
{ label: 'Coze (扣子)', value: 32 },
{ label: 'Dify', value: 33 }, { label: 'Jimeng (即梦)', value: 34 },
{ label: 'Codex', value: 35 },
]
function filterProviderOption(inputValue: string, option: { label: string; value: number }) {
return option.label.toLowerCase().includes(inputValue.toLowerCase())
}
const healthColor = (score: number | null | undefined) => {
if (score === null || score === undefined) return '#94a3b8'
if (score >= 80) return '#10b981'
@@ -46,6 +70,7 @@ const showEditModal = ref(false)
const editLoading = ref(false)
const editForm = reactive({
name: '',
type: 1,
base_url: '',
priority: 0,
weight: 100,
@@ -63,6 +88,7 @@ function openEditModal() {
if (!detail.value) return
Object.assign(editForm, {
name: detail.value.name || '',
type: detail.value.type || 1,
base_url: detail.value.base_url || '',
priority: detail.value.priority || 0,
weight: detail.value.weight || 100,
@@ -621,6 +647,12 @@ function formatHeaders(headers: Record<string, string>): string {
<!-- Edit Channel Modal -->
<AModal v-model:visible="showEditModal" title="编辑渠道" :width="600" :mask-closable="false" :on-before-ok="handleEditSubmit" :ok-loading="editLoading">
<AForm :model="editForm" :auto-label-width="true" layout="vertical">
<AFormItem label="供应商类型" field="type">
<ASelect v-model="editForm.type" :options="providerTypeOptions" placeholder="搜索或选择" allow-search :filter-option="filterProviderOption" />
<template #extra>
<span class="field-help">切换协议类型会改变该渠道的转发适配器;请确认 Base URL 与已配置的模型能力与新类型匹配。</span>
</template>
</AFormItem>
<ARow :gutter="16">
<ACol :span="8">
<AFormItem label="渠道名称"><AInput v-model="editForm.name" /></AFormItem>