feat: 提供商详情头部布局优化及行内描述编辑

- 重构详情抽屉头部:网站链接改为图标按钮,布局更紧凑
- 新增行内描述编辑功能,支持点击编辑、回车保存、Esc 取消
- 表单对话框移除描述字段,名称字段改为独占一行
- 修复次级限额重置时间的缩进问题
This commit is contained in:
fawney19
2026-02-05 02:23:41 +08:00
parent 34272af711
commit d9d3a1afcf
2 changed files with 100 additions and 52 deletions

View File

@@ -26,33 +26,28 @@
<template v-else-if="provider"> <template v-else-if="provider">
<!-- 头部:名称 + 快捷操作 --> <!-- 头部:名称 + 快捷操作 -->
<div class="sticky top-0 z-10 bg-background border-b px-4 sm:px-6 pt-4 sm:pt-6 pb-3 sm:pb-3"> <div class="sticky top-0 z-10 bg-background border-b px-4 sm:px-6 pt-4 sm:pt-6 pb-3 sm:pb-3">
<div class="flex items-start justify-between gap-3 sm:gap-4"> <div class="flex items-center justify-between gap-x-3 sm:gap-x-4 flex-wrap">
<div class="space-y-1 flex-1 min-w-0"> <div class="flex items-center gap-2 min-w-0">
<div class="flex items-center gap-2"> <h2 class="text-lg sm:text-xl font-bold truncate">
<h2 class="text-lg sm:text-xl font-bold truncate"> {{ provider.name }}
{{ provider.name }} </h2>
</h2> <!-- 网站图标 -->
<Badge <a
:variant="provider.is_active ? 'default' : 'secondary'"
class="text-xs shrink-0"
>
{{ provider.is_active ? '活跃' : '已停用' }}
</Badge>
</div>
<!-- 网站链接 -->
<div
v-if="provider.website" v-if="provider.website"
class="flex items-center gap-2" :href="provider.website"
target="_blank"
rel="noopener noreferrer"
class="text-muted-foreground hover:text-primary transition-colors shrink-0"
:title="provider.website"
> >
<span class="text-muted-foreground">·</span> <ExternalLink class="w-4 h-4" />
<a </a>
:href="provider.website" <Badge
target="_blank" :variant="provider.is_active ? 'default' : 'secondary'"
rel="noopener noreferrer" class="text-xs shrink-0"
class="text-xs text-primary hover:underline truncate" >
title="访问官网" {{ provider.is_active ? '活跃' : '已停用' }}
>{{ provider.website }}</a> </Badge>
</div>
</div> </div>
<div class="flex items-center gap-1 shrink-0"> <div class="flex items-center gap-1 shrink-0">
<span :title="systemFormatConversionEnabled ? '请先关闭系统级开关' : (provider.enable_format_conversion ? '已启用格式转换(点击关闭)' : '启用格式转换')"> <span :title="systemFormatConversionEnabled ? '请先关闭系统级开关' : (provider.enable_format_conversion ? '已启用格式转换(点击关闭)' : '启用格式转换')">
@@ -92,6 +87,25 @@
</Button> </Button>
</div> </div>
</div> </div>
<!-- 描述独占整行紧贴名称行下方 -->
<div class="-mt-0.5">
<span
v-if="!editingDescription"
class="text-xs text-muted-foreground truncate block cursor-pointer hover:text-foreground transition-colors"
:title="provider.description || '点击添加描述'"
@click="startEditDescription"
>{{ provider.description || '添加描述...' }}</span>
<input
v-else
ref="descriptionInputRef"
v-model="editingDescriptionValue"
type="text"
class="text-xs px-1.5 py-0.5 border rounded bg-background focus:outline-none focus:ring-1 focus:ring-primary w-full"
placeholder="输入描述..."
@keydown="handleDescriptionKeydown"
@blur="saveDescription"
>
</div>
<!-- 端点 API 格式 --> <!-- 端点 API 格式 -->
<div class="flex items-center gap-1.5 flex-wrap mt-3"> <div class="flex items-center gap-1.5 flex-wrap mt-3">
<template <template
@@ -396,13 +410,13 @@
/> />
</div> </div>
<div class="text-[9px] text-muted-foreground/70 mt-0.5"> <div class="text-[9px] text-muted-foreground/70 mt-0.5">
<template v-if="key.upstream_metadata.secondary_reset_seconds"> <template v-if="key.upstream_metadata.secondary_reset_seconds">
{{ formatResetTime(key.upstream_metadata.secondary_reset_seconds) }}后重置 {{ formatResetTime(key.upstream_metadata.secondary_reset_seconds) }}后重置
</template> </template>
<template v-else> <template v-else>
已重置 已重置
</template> </template>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@@ -622,7 +636,8 @@ import {
GripVertical, GripVertical,
Copy, Copy,
Shield, Shield,
Shuffle Shuffle,
ExternalLink
} from 'lucide-vue-next' } from 'lucide-vue-next'
import { useEscapeKey } from '@/composables/useEscapeKey' import { useEscapeKey } from '@/composables/useEscapeKey'
import Button from '@/components/ui/button.vue' import Button from '@/components/ui/button.vue'
@@ -730,6 +745,11 @@ const prioritySaving = ref(false)
// OAuth 刷新状态 // OAuth 刷新状态
const refreshingOAuthKeyId = ref<string | null>(null) const refreshingOAuthKeyId = ref<string | null>(null)
// 描述编辑状态
const editingDescription = ref(false)
const editingDescriptionValue = ref('')
const descriptionInputRef = ref<HTMLInputElement | null>(null)
// 点击编辑倍率相关状态 // 点击编辑倍率相关状态
const editingMultiplierKey = ref<string | null>(null) const editingMultiplierKey = ref<string | null>(null)
const editingMultiplierFormat = ref<string | null>(null) const editingMultiplierFormat = ref<string | null>(null)
@@ -848,6 +868,44 @@ async function toggleFormatConversion() {
} }
} }
// ===== 描述编辑 =====
function startEditDescription() {
editingDescription.value = true
editingDescriptionValue.value = provider.value?.description || ''
nextTick(() => {
descriptionInputRef.value?.focus()
descriptionInputRef.value?.select()
})
}
function handleDescriptionKeydown(e: KeyboardEvent) {
if (e.key === 'Enter') {
e.preventDefault()
saveDescription()
} else if (e.key === 'Escape') {
e.preventDefault()
editingDescription.value = false
}
}
async function saveDescription() {
if (!editingDescription.value || !provider.value) return
editingDescription.value = false
const newDescription = editingDescriptionValue.value.trim()
// 如果没有变化,直接返回
if (newDescription === (provider.value.description || '')) return
try {
await updateProvider(provider.value.id, { description: newDescription || null })
provider.value.description = newDescription || null
showSuccess('描述已更新')
emit('refresh')
} catch {
showError('更新描述失败')
}
}
// 显示端点管理对话框 // 显示端点管理对话框
function showAddEndpointDialog() { function showAddEndpointDialog() {
endpointDialogOpen.value = true endpointDialogOpen.value = true

View File

@@ -17,6 +17,15 @@
基本信息 基本信息
</h3> </h3>
<div class="space-y-1.5">
<Label for="name">名称 *</Label>
<Input
id="name"
v-model="form.name"
placeholder="例如: OpenAI 主账号"
/>
</div>
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
<div class="space-y-1.5"> <div class="space-y-1.5">
<Label>提供商类型</Label> <Label>提供商类型</Label>
@@ -53,25 +62,6 @@
反代使用固定端点且不可修改 反代使用固定端点且不可修改
</p> </p>
</div> </div>
<div class="space-y-1.5">
<Label for="name">名称 *</Label>
<Input
id="name"
v-model="form.name"
placeholder="例如: OpenAI 主账号"
/>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-1.5">
<Label for="description">描述</Label>
<Input
id="description"
v-model="form.description"
placeholder="提供商描述(可选)"
/>
</div>
<div class="space-y-1.5"> <div class="space-y-1.5">
<Label for="website">主站链接</Label> <Label for="website">主站链接</Label>
<Input <Input