mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
feat: 提供商详情头部布局优化及行内描述编辑
- 重构详情抽屉头部:网站链接改为图标按钮,布局更紧凑 - 新增行内描述编辑功能,支持点击编辑、回车保存、Esc 取消 - 表单对话框移除描述字段,名称字段改为独占一行 - 修复次级限额重置时间的缩进问题
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user