refactor: 优化端点管理表单和上游模型获取逻辑

- 端点表单布局优化,支持 provider website 作为默认 Base URL
- 模型路由始终显示 provider_model_name
- 无端点时显示添加按钮
- 上游模型获取只针对基础 API 格式,跳过 CLI 格式
This commit is contained in:
fawney19
2026-01-20 01:32:47 +08:00
parent edf8c663f1
commit 24ef24558c
5 changed files with 93 additions and 98 deletions

View File

@@ -852,9 +852,9 @@ function getPriorityModeLabel(mode: string): string {
return labels[mode] || mode
}
// 判断是否存在模型映射转换
// 判断是否存在模型映射(始终显示 provider_model_name
function hasModelMapping(provider: RoutingProviderInfo): boolean {
return provider.provider_model_name !== routingData.value?.global_model_name
return !!provider.provider_model_name
}
// 获取 Key 的 allowed_models 中匹配当前 GlobalModel 的所有模型名

View File

@@ -23,9 +23,9 @@
>
<!-- 编辑模式 -->
<template v-if="editingEndpointId === endpoint.id">
<div class="space-y-2">
<div class="space-y-3">
<div class="flex items-center gap-2">
<span class="text-sm font-medium w-24 shrink-0">{{ API_FORMAT_LABELS[endpoint.api_format] || endpoint.api_format }}</span>
<span class="text-sm font-medium">{{ API_FORMAT_LABELS[endpoint.api_format] || endpoint.api_format }}</span>
<div class="flex items-center gap-1 ml-auto">
<Button
variant="ghost"
@@ -48,21 +48,21 @@
</Button>
</div>
</div>
<div class="grid grid-cols-2 gap-2">
<div class="space-y-1">
<div class="flex items-end gap-3">
<div class="flex-1 min-w-0 space-y-1">
<Label class="text-xs text-muted-foreground">Base URL</Label>
<Input
v-model="editingUrl"
class="h-8 text-sm"
placeholder="https://api.example.com"
class="h-9"
:placeholder="provider?.website || 'https://api.example.com'"
@keyup.escape="cancelEdit"
/>
</div>
<div class="space-y-1">
<Label class="text-xs text-muted-foreground">自定义路径 (可选)</Label>
<div class="w-52 shrink-0 space-y-1">
<Label class="text-xs text-muted-foreground">自定义路径可选</Label>
<Input
v-model="editingPath"
class="h-8 text-sm"
class="h-9"
:placeholder="editingDefaultPath || '留空使用默认路径'"
@keyup.escape="cancelEdit"
/>
@@ -71,7 +71,7 @@
<!-- 请求头规则配置 -->
<Collapsible
v-model:open="rulesExpanded"
class="mt-2"
class="mt-1"
>
<CollapsibleTrigger as-child>
<button
@@ -96,7 +96,7 @@
<div
v-for="(rule, index) in editingRules"
:key="index"
class="flex items-start gap-2 p-2 rounded bg-muted/50"
class="flex items-center gap-2"
>
<!-- 操作类型选择 -->
<Select
@@ -104,10 +104,13 @@
:model-value="rule.action"
@update:model-value="(v) => updateRuleAction(index, v as 'set' | 'drop' | 'rename')"
>
<SelectTrigger class="w-24 h-7 text-xs">
<SelectTrigger class="w-24 h-8 text-xs shrink-0">
<SelectValue />
</SelectTrigger>
<SelectContent :side-offset="4">
<SelectContent
position="popper"
:side-offset="4"
>
<SelectItem value="set">
覆写
</SelectItem>
@@ -120,89 +123,62 @@
</SelectContent>
</Select>
<!-- set: key + value -->
<!-- set: key = value -->
<template v-if="rule.action === 'set'">
<div class="flex-1 space-y-1">
<Input
v-model="rule.key"
placeholder="Header 名称"
class="h-7 text-xs"
/>
<p
v-if="validateRuleKey(rule.key, index)"
class="text-xs text-destructive"
>
{{ validateRuleKey(rule.key, index) }}
</p>
</div>
<Input
v-model="rule.key"
placeholder="Header 名称"
:class="`w-36 h-8 text-xs ${validateRuleKey(rule.key, index) ? 'border-destructive' : ''}`"
:title="validateRuleKey(rule.key, index) || ''"
/>
<span class="text-muted-foreground text-xs">=</span>
<Input
v-model="rule.value"
placeholder="Header "
class="flex-1 h-7 text-xs"
class="flex-1 h-8 text-xs"
/>
</template>
<!-- drop: key only -->
<!-- drop: key -->
<template v-else-if="rule.action === 'drop'">
<div class="flex-1 space-y-1">
<Input
v-model="rule.key"
placeholder="要删除的 Header 名称"
class="h-7 text-xs"
/>
<p
v-if="validateRuleKey(rule.key, index)"
class="text-xs text-destructive"
>
{{ validateRuleKey(rule.key, index) }}
</p>
</div>
<Input
v-model="rule.key"
placeholder="要删除的 Header 名称"
:class="`flex-1 h-8 text-xs ${validateRuleKey(rule.key, index) ? 'border-destructive' : ''}`"
:title="validateRuleKey(rule.key, index) || ''"
/>
</template>
<!-- rename: from + to -->
<!-- rename: from -> to -->
<template v-else-if="rule.action === 'rename'">
<div class="flex-1 space-y-1">
<Input
v-model="rule.from"
placeholder=" Header 名称"
class="h-7 text-xs"
/>
<p
v-if="validateRenameFrom(rule.from, index)"
class="text-xs text-destructive"
>
{{ validateRenameFrom(rule.from, index) }}
</p>
</div>
<ArrowRight class="w-4 h-4 shrink-0 text-muted-foreground mt-1.5" />
<div class="flex-1 space-y-1">
<Input
v-model="rule.to"
placeholder=" Header 名称"
class="h-7 text-xs"
/>
<p
v-if="validateRenameTo(rule.to, index)"
class="text-xs text-destructive"
>
{{ validateRenameTo(rule.to, index) }}
</p>
</div>
<Input
v-model="rule.from"
placeholder="原名称"
:class="`flex-1 h-8 text-xs ${validateRenameFrom(rule.from, index) ? 'border-destructive' : ''}`"
:title="validateRenameFrom(rule.from, index) || ''"
/>
<ArrowRight class="w-4 h-4 shrink-0 text-muted-foreground" />
<Input
v-model="rule.to"
placeholder="新名称"
:class="`flex-1 h-8 text-xs ${validateRenameTo(rule.to, index) ? 'border-destructive' : ''}`"
:title="validateRenameTo(rule.to, index) || ''"
/>
</template>
<Button
variant="ghost"
size="icon"
class="h-7 w-7 shrink-0"
class="h-8 w-8 shrink-0"
@click="removeRule(index)"
>
<X class="w-3 h-3" />
<X class="w-3.5 h-3.5" />
</Button>
</div>
<Button
variant="outline"
size="sm"
class="w-full h-7 text-xs"
class="w-full h-8 text-xs"
@click="addRule"
>
<Plus class="w-3 h-3 mr-1" />
@@ -273,7 +249,7 @@
>
<Label class="text-muted-foreground">添加新端点</Label>
<div class="flex items-end gap-3">
<div class="w-32 shrink-0 space-y-1.5">
<div class="w-36 shrink-0 space-y-1.5">
<Label class="text-xs">API 格式</Label>
<Select
v-model="newEndpoint.api_format"
@@ -293,31 +269,22 @@
</SelectContent>
</Select>
</div>
<div class="flex-1 space-y-1.5">
<div class="flex-1 min-w-0 space-y-1.5">
<Label class="text-xs">Base URL</Label>
<Input
v-model="newEndpoint.base_url"
placeholder="https://api.example.com"
:placeholder="provider?.website || 'https://api.example.com'"
class="h-9"
/>
</div>
<div class="w-40 shrink-0 space-y-1.5">
<Label class="text-xs">自定义路径</Label>
<div class="w-52 shrink-0 space-y-1.5">
<Label class="text-xs">自定义路径(可选)</Label>
<Input
v-model="newEndpoint.custom_path"
:placeholder="newEndpointDefaultPath || '可选'"
:placeholder="newEndpointDefaultPath || '留空使用默认路径'"
class="h-9"
/>
</div>
<Button
variant="outline"
size="sm"
class="h-9 shrink-0"
:disabled="!newEndpoint.api_format || !newEndpoint.base_url || addingEndpoint"
@click="handleAddEndpoint"
>
{{ addingEndpoint ? '添加中...' : '添加' }}
</Button>
</div>
</div>
@@ -337,6 +304,14 @@
>
关闭
</Button>
<Button
v-if="availableFormats.length > 0"
variant="outline"
:disabled="!newEndpoint.api_format || (!newEndpoint.base_url?.trim() && !provider?.website?.trim()) || addingEndpoint"
@click="handleAddEndpoint"
>
{{ addingEndpoint ? '添加中...' : '添加' }}
</Button>
</template>
</Dialog>
@@ -710,21 +685,27 @@ async function saveEndpointUrl(endpoint: ProviderEndpoint) {
// 添加端点
async function handleAddEndpoint() {
if (!props.provider || !newEndpoint.value.api_format || !newEndpoint.value.base_url) return
if (!props.provider || !newEndpoint.value.api_format) return
// 如果没有输入 base_url使用提供商的 website 作为默认值
const baseUrl = newEndpoint.value.base_url || props.provider.website
if (!baseUrl) {
showError('请输入 Base URL')
return
}
addingEndpoint.value = true
try {
await createEndpoint(props.provider.id, {
provider_id: props.provider.id,
api_format: newEndpoint.value.api_format,
base_url: newEndpoint.value.base_url,
base_url: baseUrl,
custom_path: newEndpoint.value.custom_path || undefined,
is_active: true,
})
success(`已添加 ${API_FORMAT_LABELS[newEndpoint.value.api_format] || newEndpoint.value.api_format} 端点`)
// 重置表单,保留 URL
const url = newEndpoint.value.base_url
newEndpoint.value = { api_format: '', base_url: url, custom_path: '' }
newEndpoint.value = { api_format: '', base_url: baseUrl, custom_path: '' }
emit('endpointCreated')
} catch (error: any) {
showError(error.response?.data?.detail || '添加失败', '错误')

View File

@@ -213,9 +213,9 @@
<!-- 自动获取模型 -->
<div class="flex items-center justify-between py-2 px-3 rounded-md border border-border/60 bg-muted/30">
<div class="space-y-0.5">
<Label class="text-sm font-medium">自动获取模型</Label>
<Label class="text-sm font-medium">自动获取上游可用模型</Label>
<p class="text-xs text-muted-foreground">
系统将定时从上游获取可用模型, 但无法默认接受提供商模型
定时更新上游模型, 配合模型映射使用
</p>
<p
v-if="showAutoFetchWarning"

View File

@@ -95,10 +95,21 @@
>{{ API_FORMAT_LABELS[endpoint.api_format] || endpoint.api_format }}</span>
</template>
<span
v-if="endpoints.length > 0"
class="text-xs px-2 py-0.5 rounded-md border border-dashed border-border hover:bg-accent hover:border-accent-foreground/20 cursor-pointer transition-colors text-muted-foreground"
title="编辑端点"
@click="showAddEndpointDialog"
>编辑</span>
<Button
v-else
variant="outline"
size="sm"
class="h-7 text-xs"
@click="showAddEndpointDialog"
>
<Plus class="w-3 h-3 mr-1" />
添加 API 端点
</Button>
</div>
</div>