feat(proxy): 持久化质量检测结果并在列表展示

This commit is contained in:
yangjianbo
2026-02-20 12:13:04 +08:00
parent 48dc011b2a
commit d14c24bbf3
9 changed files with 202 additions and 24 deletions

View File

@@ -160,20 +160,32 @@
</template>
<template #cell-latency="{ row }">
<span
v-if="row.latency_status === 'failed'"
class="badge badge-danger"
:title="row.latency_message || undefined"
>
{{ t('admin.proxies.latencyFailed') }}
</span>
<span
v-else-if="typeof row.latency_ms === 'number'"
:class="['badge', row.latency_ms < 200 ? 'badge-success' : 'badge-warning']"
>
{{ row.latency_ms }}ms
</span>
<span v-else class="text-sm text-gray-400">-</span>
<div class="flex flex-col gap-1">
<span
v-if="row.latency_status === 'failed'"
class="badge badge-danger"
:title="row.latency_message || undefined"
>
{{ t('admin.proxies.latencyFailed') }}
</span>
<span
v-else-if="typeof row.latency_ms === 'number'"
:class="['badge', row.latency_ms < 200 ? 'badge-success' : 'badge-warning']"
>
{{ row.latency_ms }}ms
</span>
<span v-else class="text-sm text-gray-400">-</span>
<div
v-if="typeof row.quality_checked === 'number'"
class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400"
:title="row.quality_summary || undefined"
>
<span>{{ t('admin.proxies.qualityInline', { grade: row.quality_grade || '-', score: row.quality_score ?? '-' }) }}</span>
<span class="badge" :class="qualityOverallClass(row.quality_status)">
{{ qualityOverallLabel(row.quality_status) }}
</span>
</div>
</div>
</template>
<template #cell-status="{ value }">
@@ -1250,6 +1262,23 @@ const applyLatencyResult = (
target.latency_message = result.message
}
const summarizeQualityStatus = (result: ProxyQualityCheckResult): Proxy['quality_status'] => {
if (result.challenge_count > 0) return 'challenge'
if (result.failed_count > 0) return 'failed'
if (result.warn_count > 0) return 'warn'
return 'healthy'
}
const applyQualityResult = (proxyId: number, result: ProxyQualityCheckResult) => {
const target = proxies.value.find((proxy) => proxy.id === proxyId)
if (!target) return
target.quality_status = summarizeQualityStatus(result)
target.quality_score = result.score
target.quality_grade = result.grade
target.quality_summary = result.summary
target.quality_checked = result.checked_at
}
const formatLocation = (proxy: Proxy) => {
const parts = [proxy.country, proxy.city].filter(Boolean) as string[]
return parts.join(' · ')
@@ -1330,6 +1359,7 @@ const handleQualityCheck = async (proxy: Proxy) => {
country_code: result.country_code
})
}
applyQualityResult(proxy.id, result)
appStore.showSuccess(
t('admin.proxies.qualityCheckDone', { score: result.score, grade: result.grade })
@@ -1374,6 +1404,7 @@ const runBatchProxyQualityChecks = async (ids: number[]) => {
})
}
}
applyQualityResult(current, result)
if (result.challenge_count > 0) {
challenge++
} else if (result.failed_count > 0) {
@@ -1422,6 +1453,20 @@ const qualityStatusLabel = (status: string) => {
return t('admin.proxies.qualityStatusFail')
}
const qualityOverallClass = (status?: string) => {
if (status === 'healthy') return 'badge-success'
if (status === 'warn') return 'badge-warning'
if (status === 'challenge') return 'badge-danger'
return 'badge-danger'
}
const qualityOverallLabel = (status?: string) => {
if (status === 'healthy') return t('admin.proxies.qualityStatusHealthy')
if (status === 'warn') return t('admin.proxies.qualityStatusWarn')
if (status === 'challenge') return t('admin.proxies.qualityStatusChallenge')
return t('admin.proxies.qualityStatusFail')
}
const qualityTargetLabel = (target: string) => {
switch (target) {
case 'base_connectivity':