mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-13 14:40:20 +08:00
修复前端调试与基础交互问题
This commit is contained in:
@@ -325,6 +325,7 @@
|
||||
<MetricCell
|
||||
label="进程 CPU"
|
||||
:value="formatBasisPointsPercent(gatewayMetrics?.process.processCpuUsageBasisPoints)"
|
||||
:value-class="resourceToneClass(gatewayProcessCpuPercent, 70, 90)"
|
||||
/>
|
||||
<MetricCell
|
||||
label="RSS"
|
||||
|
||||
@@ -2016,12 +2016,14 @@ function refreshOverviewInBackground(): void {
|
||||
}
|
||||
|
||||
function applyQuotaRefreshResultToCurrentPage(result: Awaited<ReturnType<typeof refreshProviderQuota>>): void {
|
||||
const successfulResults = Array.isArray(result.results)
|
||||
? result.results.filter((item) => item.status === 'success' && item.quota_snapshot)
|
||||
: []
|
||||
if (successfulResults.length === 0) return
|
||||
const quotaByKeyId = new Map<string, NonNullable<NonNullable<typeof result.results>[number]['quota_snapshot']>>()
|
||||
for (const item of result.results) {
|
||||
if (item.status === 'success' && item.quota_snapshot) {
|
||||
quotaByKeyId.set(item.key_id, item.quota_snapshot)
|
||||
}
|
||||
}
|
||||
if (quotaByKeyId.size === 0) return
|
||||
|
||||
const quotaByKeyId = new Map(successfulResults.map((item) => [item.key_id, item.quota_snapshot!]))
|
||||
keyPage.value.keys = keyPage.value.keys.map((key) => {
|
||||
const quotaSnapshot = quotaByKeyId.get(key.key_id)
|
||||
if (!quotaSnapshot) return key
|
||||
|
||||
@@ -650,10 +650,6 @@ function updateNodeDetailState(nodeId: string, patch: Partial<ProxyNodeDetailSta
|
||||
}
|
||||
}
|
||||
|
||||
function isNodeExpanded(nodeId: string) {
|
||||
return expandedNodeIds.value.has(nodeId)
|
||||
}
|
||||
|
||||
function toggleNodeDetails(node: ProxyNode) {
|
||||
const next = new Set(expandedNodeIds.value)
|
||||
if (next.has(node.id)) {
|
||||
|
||||
@@ -26,11 +26,21 @@
|
||||
<Select v-model="logLevel">
|
||||
<SelectTrigger><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="trace">trace</SelectItem>
|
||||
<SelectItem value="debug">debug</SelectItem>
|
||||
<SelectItem value="info">info</SelectItem>
|
||||
<SelectItem value="warn">warn</SelectItem>
|
||||
<SelectItem value="error">error</SelectItem>
|
||||
<SelectItem value="trace">
|
||||
trace
|
||||
</SelectItem>
|
||||
<SelectItem value="debug">
|
||||
debug
|
||||
</SelectItem>
|
||||
<SelectItem value="info">
|
||||
info
|
||||
</SelectItem>
|
||||
<SelectItem value="warn">
|
||||
warn
|
||||
</SelectItem>
|
||||
<SelectItem value="error">
|
||||
error
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@@ -49,9 +59,15 @@
|
||||
<Select v-model="schedulingState">
|
||||
<SelectTrigger><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="active">active</SelectItem>
|
||||
<SelectItem value="draining">draining</SelectItem>
|
||||
<SelectItem value="cordoned">cordoned</SelectItem>
|
||||
<SelectItem value="active">
|
||||
active
|
||||
</SelectItem>
|
||||
<SelectItem value="draining">
|
||||
draining
|
||||
</SelectItem>
|
||||
<SelectItem value="cordoned">
|
||||
cordoned
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
|
||||
@@ -1,172 +1,170 @@
|
||||
<template>
|
||||
<template>
|
||||
<TableRow
|
||||
class="border-b border-border/40 hover:bg-muted/30 transition-colors"
|
||||
:class="expanded ? 'bg-muted/20' : ''"
|
||||
>
|
||||
<TableCell class="w-[28px] min-w-[28px] max-w-[28px] p-0 pl-2 text-center">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex h-5 w-5 items-center justify-center rounded-md text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1"
|
||||
:title="expanded ? legacyT('收起数据') : legacyT('展开数据')"
|
||||
@click="$emit('toggle-details')"
|
||||
>
|
||||
<ChevronDown
|
||||
v-if="expanded"
|
||||
class="h-3.5 w-3.5"
|
||||
/>
|
||||
<ChevronRight
|
||||
v-else
|
||||
class="h-3.5 w-3.5"
|
||||
/>
|
||||
</button>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 min-w-0">
|
||||
<div class="flex items-center gap-1.5 min-w-0">
|
||||
<span
|
||||
class="min-w-0 truncate text-sm font-semibold"
|
||||
:title="node.name"
|
||||
>{{ node.name }}</span>
|
||||
<Badge
|
||||
v-if="node.is_manual"
|
||||
variant="outline"
|
||||
class="text-[10px] px-1.5 py-0"
|
||||
>
|
||||
{{ legacyT('手动') }}
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="node.tunnel_mode"
|
||||
variant="outline"
|
||||
class="text-[10px] px-1.5 py-0"
|
||||
>
|
||||
Tunnel
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="schedulingBadge"
|
||||
:variant="schedulingBadge.variant"
|
||||
class="text-[10px] px-1.5 py-0"
|
||||
>
|
||||
{{ legacyT(schedulingBadge.label) }}
|
||||
</Badge>
|
||||
<HardwareTooltip :node="node" />
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 min-w-0">
|
||||
<code
|
||||
class="block min-w-0 truncate text-xs text-muted-foreground"
|
||||
:title="proxyNodeAddress(node)"
|
||||
>{{ proxyNodeAddress(node) }}</code>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 min-w-0">
|
||||
<span
|
||||
class="block min-w-0 truncate text-sm text-muted-foreground"
|
||||
:title="legacyT(formatProxyNodeRegion(node.region))"
|
||||
>{{ legacyT(formatProxyNodeRegion(node.region)) }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<Badge
|
||||
:variant="proxyNodeStatusVariant(node.status)"
|
||||
:title="legacyT(proxyNodeStatusTitle(node))"
|
||||
class="font-medium px-2.5 py-0.5 text-xs"
|
||||
>
|
||||
{{ legacyT(proxyNodeStatusLabel(node)) }}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<span class="text-sm tabular-nums">{{ formatProxyNodeNumber(node.total_requests) }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<span
|
||||
class="text-sm tabular-nums"
|
||||
:class="proxyNodeFailureRate(node) > 5 ? 'text-destructive font-medium' : ''"
|
||||
>{{ formatProxyNodeFailureRate(node) }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<span class="text-sm tabular-nums">{{ node.avg_latency_ms != null ? `${node.avg_latency_ms.toFixed(0)}ms` : '-' }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<span class="text-sm tabular-nums">{{ node.is_manual ? '-' : proxyNodeVersion(node) }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 min-w-0">
|
||||
<span class="block min-w-0 truncate text-xs text-muted-foreground">{{ formatProxyNodeTime(node.last_heartbeat_at, locale) }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<div class="flex items-center justify-center gap-0.5">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
:title="testing ? legacyT('测试中...') : legacyT('测试连通性')"
|
||||
:disabled="testing"
|
||||
@click="$emit('test')"
|
||||
>
|
||||
<Loader2
|
||||
v-if="testing"
|
||||
class="h-4 w-4 animate-spin"
|
||||
/>
|
||||
<Activity
|
||||
v-else
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
v-if="node.is_manual"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
:title="legacyT('编辑')"
|
||||
@click="$emit('edit')"
|
||||
>
|
||||
<SquarePen class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
v-if="!node.is_manual"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
:title="legacyT('远程配置')"
|
||||
@click="$emit('config')"
|
||||
>
|
||||
<Settings class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
v-if="!node.is_manual"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
:title="legacyT('连接事件')"
|
||||
@click="$emit('view-events')"
|
||||
>
|
||||
<History class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
:title="legacyT('删除')"
|
||||
@click="$emit('delete')"
|
||||
>
|
||||
<Trash2 class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow
|
||||
v-if="expanded"
|
||||
class="border-b border-border/40 hover:bg-transparent"
|
||||
>
|
||||
<TableCell
|
||||
colspan="11"
|
||||
class="p-0"
|
||||
<TableRow
|
||||
class="border-b border-border/40 hover:bg-muted/30 transition-colors"
|
||||
:class="expanded ? 'bg-muted/20' : ''"
|
||||
>
|
||||
<TableCell class="w-[28px] min-w-[28px] max-w-[28px] p-0 pl-2 text-center">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex h-5 w-5 items-center justify-center rounded-md text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1"
|
||||
:title="expanded ? legacyT('收起数据') : legacyT('展开数据')"
|
||||
@click="$emit('toggle-details')"
|
||||
>
|
||||
<ProxyNodeDataPanel
|
||||
:node="node"
|
||||
:state="detailState"
|
||||
@refresh="$emit('refresh-details')"
|
||||
<ChevronDown
|
||||
v-if="expanded"
|
||||
class="h-3.5 w-3.5"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</template>
|
||||
<ChevronRight
|
||||
v-else
|
||||
class="h-3.5 w-3.5"
|
||||
/>
|
||||
</button>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 min-w-0">
|
||||
<div class="flex items-center gap-1.5 min-w-0">
|
||||
<span
|
||||
class="min-w-0 truncate text-sm font-semibold"
|
||||
:title="node.name"
|
||||
>{{ node.name }}</span>
|
||||
<Badge
|
||||
v-if="node.is_manual"
|
||||
variant="outline"
|
||||
class="text-[10px] px-1.5 py-0"
|
||||
>
|
||||
{{ legacyT('手动') }}
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="node.tunnel_mode"
|
||||
variant="outline"
|
||||
class="text-[10px] px-1.5 py-0"
|
||||
>
|
||||
Tunnel
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="schedulingBadge"
|
||||
:variant="schedulingBadge.variant"
|
||||
class="text-[10px] px-1.5 py-0"
|
||||
>
|
||||
{{ legacyT(schedulingBadge.label) }}
|
||||
</Badge>
|
||||
<HardwareTooltip :node="node" />
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 min-w-0">
|
||||
<code
|
||||
class="block min-w-0 truncate text-xs text-muted-foreground"
|
||||
:title="proxyNodeAddress(node)"
|
||||
>{{ proxyNodeAddress(node) }}</code>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 min-w-0">
|
||||
<span
|
||||
class="block min-w-0 truncate text-sm text-muted-foreground"
|
||||
:title="legacyT(formatProxyNodeRegion(node.region))"
|
||||
>{{ legacyT(formatProxyNodeRegion(node.region)) }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<Badge
|
||||
:variant="proxyNodeStatusVariant(node.status)"
|
||||
:title="legacyT(proxyNodeStatusTitle(node))"
|
||||
class="font-medium px-2.5 py-0.5 text-xs"
|
||||
>
|
||||
{{ legacyT(proxyNodeStatusLabel(node)) }}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<span class="text-sm tabular-nums">{{ formatProxyNodeNumber(node.total_requests) }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<span
|
||||
class="text-sm tabular-nums"
|
||||
:class="proxyNodeFailureRate(node) > 5 ? 'text-destructive font-medium' : ''"
|
||||
>{{ formatProxyNodeFailureRate(node) }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<span class="text-sm tabular-nums">{{ node.avg_latency_ms != null ? `${node.avg_latency_ms.toFixed(0)}ms` : '-' }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<span class="text-sm tabular-nums">{{ node.is_manual ? '-' : proxyNodeVersion(node) }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 min-w-0">
|
||||
<span class="block min-w-0 truncate text-xs text-muted-foreground">{{ formatProxyNodeTime(node.last_heartbeat_at, locale) }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<div class="flex items-center justify-center gap-0.5">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
:title="testing ? legacyT('测试中...') : legacyT('测试连通性')"
|
||||
:disabled="testing"
|
||||
@click="$emit('test')"
|
||||
>
|
||||
<Loader2
|
||||
v-if="testing"
|
||||
class="h-4 w-4 animate-spin"
|
||||
/>
|
||||
<Activity
|
||||
v-else
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
v-if="node.is_manual"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
:title="legacyT('编辑')"
|
||||
@click="$emit('edit')"
|
||||
>
|
||||
<SquarePen class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
v-if="!node.is_manual"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
:title="legacyT('远程配置')"
|
||||
@click="$emit('config')"
|
||||
>
|
||||
<Settings class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
v-if="!node.is_manual"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
:title="legacyT('连接事件')"
|
||||
@click="$emit('view-events')"
|
||||
>
|
||||
<History class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
:title="legacyT('删除')"
|
||||
@click="$emit('delete')"
|
||||
>
|
||||
<Trash2 class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow
|
||||
v-if="expanded"
|
||||
class="border-b border-border/40 hover:bg-transparent"
|
||||
>
|
||||
<TableCell
|
||||
colspan="11"
|
||||
class="p-0"
|
||||
>
|
||||
<ProxyNodeDataPanel
|
||||
:node="node"
|
||||
:state="detailState"
|
||||
@refresh="$emit('refresh-details')"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
Reference in New Issue
Block a user