refactor(provider-ui): 调整供应商信息布局,网站和描述展示方式互换

- 详情抽屉:移除描述内联编辑,网站地址改为独占整行显示完整 URL
- 列表/卡片视图:网站改为名称旁的 ExternalLink 图标,新增描述文本展示
- 移除不再使用的 formatWebsiteDisplay 工具函数和描述编辑相关代码
This commit is contained in:
fawney19
2026-02-13 21:27:30 +08:00
parent 941e599d36
commit d01caaa4b1
2 changed files with 55 additions and 99 deletions

View File

@@ -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

View File

@@ -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) => {