渠道: 新增主页字段 + 表格展示 favicon
- Channel 新增 homepage 字段(渠道主页)
- 后端 /admin/channels/:id/favicon 代理抓取 {homepage}/favicon.ico, 内存缓存 1h;
服务端抓取可解析 localhost/内网主页, 避免浏览器跨域
- 前端渠道表单加"渠道主页"输入; 表格新增主页列(favicon + 域名), 加载失败回退灰色地球图标
- mock 上游提供 /favicon.ico 演示
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ export interface Channel {
|
||||
name: string
|
||||
provider: 'openai' | 'anthropic' | 'compatible'
|
||||
formats: string[] // chat | responses | messages
|
||||
homepage?: string
|
||||
base_url: string
|
||||
api_key_masked: string
|
||||
weight: number
|
||||
|
||||
@@ -19,6 +19,7 @@ const busyId = ref<number | null>(null)
|
||||
const form = reactive({
|
||||
name: '',
|
||||
formats: ['chat'] as string[],
|
||||
homepage: '',
|
||||
base_url: '',
|
||||
api_key: '',
|
||||
weight: 1,
|
||||
@@ -28,6 +29,24 @@ const form = reactive({
|
||||
enabled: true,
|
||||
})
|
||||
|
||||
// favicon 兜底:灰色地球
|
||||
const FAV_FALLBACK =
|
||||
'data:image/svg+xml;utf8,' +
|
||||
encodeURIComponent(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><circle cx="8" cy="8" r="6.5" fill="none" stroke="#a1a1aa" stroke-width="1.4"/><ellipse cx="8" cy="8" rx="3" ry="6.5" fill="none" stroke="#a1a1aa" stroke-width="1.4"/><path d="M1.8 8h12.4" stroke="#a1a1aa" stroke-width="1.4"/></svg>',
|
||||
)
|
||||
function onFavError(e: Event) {
|
||||
;(e.target as HTMLImageElement).src = FAV_FALLBACK
|
||||
}
|
||||
function hostOf(u?: string): string {
|
||||
if (!u) return '-'
|
||||
try {
|
||||
return new URL(u).host
|
||||
} catch {
|
||||
return u
|
||||
}
|
||||
}
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
const { data } = await http.get('/admin/channels')
|
||||
@@ -40,7 +59,7 @@ async function load() {
|
||||
function openCreate() {
|
||||
editing.value = null
|
||||
Object.assign(form, {
|
||||
name: '', formats: ['chat'], base_url: '', api_key: '',
|
||||
name: '', formats: ['chat'], homepage: '', base_url: '', api_key: '',
|
||||
weight: 1, priority: 0, timeout_ms: 120000, max_concurrency: 16, enabled: true,
|
||||
})
|
||||
editOpen.value = true
|
||||
@@ -50,6 +69,7 @@ function openEdit(ch: Channel) {
|
||||
editing.value = ch
|
||||
Object.assign(form, {
|
||||
name: ch.name, formats: [...(ch.formats?.length ? ch.formats : ['chat'])],
|
||||
homepage: ch.homepage || '',
|
||||
base_url: ch.base_url, api_key: '',
|
||||
weight: ch.weight, priority: ch.priority, timeout_ms: ch.timeout_ms,
|
||||
max_concurrency: ch.max_concurrency, enabled: ch.enabled,
|
||||
@@ -143,6 +163,7 @@ onMounted(load)
|
||||
<tr class="border-b border-edge text-left text-xs text-muted">
|
||||
<th scope="col" class="px-4 py-2.5 font-medium">名称</th>
|
||||
<th scope="col" class="px-4 py-2.5 font-medium">API 格式</th>
|
||||
<th scope="col" class="px-4 py-2.5 font-medium">主页</th>
|
||||
<th scope="col" class="px-4 py-2.5 font-medium">Base URL</th>
|
||||
<th scope="col" class="px-4 py-2.5 font-medium">Key</th>
|
||||
<th scope="col" class="px-4 py-2.5 font-medium">健康</th>
|
||||
@@ -162,6 +183,18 @@ onMounted(load)
|
||||
>{{ protocolName(f) }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-2.5">
|
||||
<div class="flex items-center gap-2">
|
||||
<img
|
||||
:src="`/api/v1/admin/channels/${ch.id}/favicon`"
|
||||
:alt="ch.name"
|
||||
class="size-5 shrink-0 rounded-sm"
|
||||
loading="lazy"
|
||||
@error="onFavError"
|
||||
/>
|
||||
<span class="text-xs text-muted">{{ hostOf(ch.homepage) }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-2.5 font-mono text-xs text-muted">{{ ch.base_url }}</td>
|
||||
<td class="px-4 py-2.5 font-mono text-xs text-muted">{{ ch.api_key_masked || '****' }}</td>
|
||||
<td class="px-4 py-2.5">
|
||||
@@ -182,7 +215,7 @@ onMounted(load)
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="channels.length === 0">
|
||||
<td colspan="7" class="px-4 py-10 text-center text-sm text-muted">还没有渠道,点击「添加渠道」</td>
|
||||
<td colspan="8" class="px-4 py-10 text-center text-sm text-muted">还没有渠道,点击「添加渠道」</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -213,6 +246,7 @@ onMounted(load)
|
||||
<p class="mt-1.5 text-xs text-muted">客户端协议不在其中时,网关自动转换为其支持的格式</p>
|
||||
</div>
|
||||
<Input v-model="form.base_url" label="Base URL" placeholder="https://api.openai.com" />
|
||||
<Input v-model="form.homepage" label="渠道主页" placeholder="https://openai.com(用于展示 favicon)" />
|
||||
<Input
|
||||
v-model="form.api_key"
|
||||
label="上游 API Key"
|
||||
|
||||
Reference in New Issue
Block a user