mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
refactor(provider-ui): 调整供应商信息布局,网站和描述展示方式互换
- 详情抽屉:移除描述内联编辑,网站地址改为独占整行显示完整 URL - 列表/卡片视图:网站改为名称旁的 ExternalLink 图标,新增描述文本展示 - 移除不再使用的 formatWebsiteDisplay 工具函数和描述编辑相关代码
This commit is contained in:
@@ -31,17 +31,6 @@
|
|||||||
<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>
|
||||||
<!-- 网站图标 -->
|
|
||||||
<a
|
|
||||||
v-if="provider.website"
|
|
||||||
:href="provider.website"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="text-muted-foreground hover:text-primary transition-colors shrink-0"
|
|
||||||
:title="provider.website"
|
|
||||||
>
|
|
||||||
<ExternalLink class="w-4 h-4" />
|
|
||||||
</a>
|
|
||||||
<Badge
|
<Badge
|
||||||
:variant="provider.is_active ? 'default' : 'secondary'"
|
:variant="provider.is_active ? 'default' : 'secondary'"
|
||||||
class="text-xs shrink-0"
|
class="text-xs shrink-0"
|
||||||
@@ -87,24 +76,18 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 描述(独占整行,紧贴名称行下方) -->
|
<!-- 网站地址(独占整行,紧贴名称行下方) -->
|
||||||
<div class="-mt-0.5">
|
<div
|
||||||
<span
|
v-if="provider.website"
|
||||||
v-if="!editingDescription"
|
class="-mt-0.5"
|
||||||
class="text-xs text-muted-foreground truncate block cursor-pointer hover:text-foreground transition-colors"
|
>
|
||||||
:title="provider.description || '点击添加描述'"
|
<a
|
||||||
@click="startEditDescription"
|
:href="provider.website"
|
||||||
>{{ provider.description || '添加描述...' }}</span>
|
target="_blank"
|
||||||
<input
|
rel="noopener noreferrer"
|
||||||
v-else
|
class="text-xs text-muted-foreground hover:text-primary hover:underline transition-colors truncate block"
|
||||||
ref="descriptionInputRef"
|
:title="provider.website"
|
||||||
v-model="editingDescriptionValue"
|
>{{ provider.website }}</a>
|
||||||
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>
|
</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">
|
||||||
@@ -1020,7 +1003,6 @@ import {
|
|||||||
Download,
|
Download,
|
||||||
Shield,
|
Shield,
|
||||||
Shuffle,
|
Shuffle,
|
||||||
ExternalLink,
|
|
||||||
BarChart3,
|
BarChart3,
|
||||||
ShieldX,
|
ShieldX,
|
||||||
Globe,
|
Globe,
|
||||||
@@ -1166,11 +1148,6 @@ const proxyNodesStore = useProxyNodesStore()
|
|||||||
const savingProxyKeyId = ref<string | null>(null)
|
const savingProxyKeyId = ref<string | null>(null)
|
||||||
const proxyPopoverOpenKeyId = ref<string | null>(null)
|
const proxyPopoverOpenKeyId = 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)
|
||||||
@@ -1310,44 +1287,6 @@ 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
|
||||||
|
|||||||
@@ -188,18 +188,25 @@
|
|||||||
>
|
>
|
||||||
<TableCell class="py-3.5">
|
<TableCell class="py-3.5">
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
<span class="text-sm font-medium text-foreground">{{ provider.name }}</span>
|
<div class="flex items-center gap-1.5">
|
||||||
<a
|
<span class="text-sm font-medium text-foreground">{{ provider.name }}</span>
|
||||||
v-if="provider.website"
|
<a
|
||||||
:href="provider.website"
|
v-if="provider.website"
|
||||||
target="_blank"
|
:href="provider.website"
|
||||||
rel="noopener noreferrer"
|
target="_blank"
|
||||||
class="text-xs text-primary/80 hover:text-primary hover:underline truncate block max-w-[160px]"
|
rel="noopener noreferrer"
|
||||||
:title="provider.website"
|
class="text-muted-foreground hover:text-primary transition-colors shrink-0"
|
||||||
@click.stop
|
:title="provider.website"
|
||||||
>
|
@click.stop
|
||||||
{{ formatWebsiteDisplay(provider.website) }}
|
>
|
||||||
</a>
|
<ExternalLink class="w-3.5 h-3.5" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
v-if="provider.description"
|
||||||
|
class="text-xs text-muted-foreground truncate block max-w-[200px]"
|
||||||
|
:title="provider.description"
|
||||||
|
>{{ provider.description }}</span>
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell class="py-3.5">
|
<TableCell class="py-3.5">
|
||||||
@@ -217,7 +224,10 @@
|
|||||||
class="flex items-center gap-2 text-xs"
|
class="flex items-center gap-2 text-xs"
|
||||||
>
|
>
|
||||||
<!-- 余额文字:balance + points 分开显示,或普通余额 -->
|
<!-- 余额文字:balance + points 分开显示,或普通余额 -->
|
||||||
<template v-for="bd in [getProviderBalanceBreakdown(provider.id)]" :key="'bd'">
|
<template
|
||||||
|
v-for="bd in [getProviderBalanceBreakdown(provider.id)]"
|
||||||
|
key="bd"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-if="bd"
|
v-if="bd"
|
||||||
class="min-w-[4.5rem] tabular-nums leading-tight"
|
class="min-w-[4.5rem] tabular-nums leading-tight"
|
||||||
@@ -460,9 +470,20 @@
|
|||||||
>
|
>
|
||||||
<!-- 第一行:名称 + 状态 + 操作 -->
|
<!-- 第一行:名称 + 状态 + 操作 -->
|
||||||
<div class="flex items-start justify-between gap-3">
|
<div class="flex items-start justify-between gap-3">
|
||||||
<div class="flex-1 min-w-0">
|
<div class="flex-1 min-w-0 space-y-0.5">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-1.5">
|
||||||
<span class="font-medium text-foreground truncate">{{ provider.name }}</span>
|
<span class="font-medium text-foreground truncate">{{ provider.name }}</span>
|
||||||
|
<a
|
||||||
|
v-if="provider.website"
|
||||||
|
:href="provider.website"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="text-muted-foreground hover:text-primary transition-colors shrink-0"
|
||||||
|
:title="provider.website"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<ExternalLink class="w-3.5 h-3.5" />
|
||||||
|
</a>
|
||||||
<Badge
|
<Badge
|
||||||
:variant="provider.is_active ? 'success' : 'secondary'"
|
:variant="provider.is_active ? 'success' : 'secondary'"
|
||||||
class="text-xs shrink-0"
|
class="text-xs shrink-0"
|
||||||
@@ -470,6 +491,11 @@
|
|||||||
{{ provider.is_active ? '活跃' : '停用' }}
|
{{ provider.is_active ? '活跃' : '停用' }}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
<span
|
||||||
|
v-if="provider.description"
|
||||||
|
class="text-xs text-muted-foreground truncate block max-w-[120px]"
|
||||||
|
:title="provider.description"
|
||||||
|
>{{ provider.description }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex items-center gap-0.5 shrink-0"
|
class="flex items-center gap-0.5 shrink-0"
|
||||||
@@ -681,7 +707,8 @@ import {
|
|||||||
Power,
|
Power,
|
||||||
KeyRound,
|
KeyRound,
|
||||||
Loader2,
|
Loader2,
|
||||||
FilterX
|
FilterX,
|
||||||
|
ExternalLink
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import Button from '@/components/ui/button.vue'
|
import Button from '@/components/ui/button.vue'
|
||||||
import Badge from '@/components/ui/badge.vue'
|
import Badge from '@/components/ui/badge.vue'
|
||||||
@@ -1174,16 +1201,6 @@ function getProviderBalanceExtra(providerId: string, architectureId?: string): B
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 格式化官网显示
|
|
||||||
function formatWebsiteDisplay(url: string): string {
|
|
||||||
try {
|
|
||||||
const urlObj = new URL(url)
|
|
||||||
return urlObj.hostname.replace(/^www\./, '')
|
|
||||||
} catch {
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 端点排序
|
// 端点排序
|
||||||
function sortEndpoints(endpoints: any[]) {
|
function sortEndpoints(endpoints: any[]) {
|
||||||
return [...endpoints].sort((a, b) => {
|
return [...endpoints].sort((a, b) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user