fix: 迁移脚本补充 inspect 导入,HardwareTooltip 兼容多种硬件字段格式

- 迁移脚本缺少 inspect 导入导致运行时报错
- HardwareTooltip 使用 pickNumber/pickString 兼容不同节点上报的字段名
- 添加 tooltip 点击交互和 title 属性作为 fallback
This commit is contained in:
fawney19
2026-02-21 18:13:46 +08:00
parent 066fc01d2e
commit b649a69a5e
2 changed files with 61 additions and 12 deletions

View File

@@ -11,7 +11,7 @@ from __future__ import annotations
from collections.abc import Sequence
import sqlalchemy as sa
from sqlalchemy import text
from sqlalchemy import inspect, text
from alembic import op
@@ -69,6 +69,7 @@ def upgrade() -> None:
"""))
# 创建索引
inspector = inspect(conn)
existing_indexes = {idx["name"] for idx in inspector.get_indexes("usage")}
for idx_name, table, columns in NEW_INDEXES:
if idx_name not in existing_indexes:

View File

@@ -2,9 +2,10 @@
import type { ProxyNode } from '@/api/proxy-nodes'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { Cpu } from 'lucide-vue-next'
import { computed } from 'vue'
import { computed, ref } from 'vue'
const props = defineProps<{ node: ProxyNode }>()
const open = ref(false)
const hardwareInfo = computed<Record<string, any> | null>(() => {
const info = props.node.hardware_info
@@ -26,16 +27,24 @@ const hardwareRows = computed(() => {
const info = hardwareInfo.value ?? {}
const rows: Array<{ label: string; value: string }> = []
if (info.cpu_cores != null) {
rows.push({ label: 'CPU', value: `${info.cpu_cores} cores` })
const cpuCores = pickNumber(info.cpu_cores, info.cpu_count, info.cpu?.cores)
if (cpuCores != null) {
rows.push({ label: 'CPU', value: `${cpuCores} cores` })
}
if (info.total_memory_mb != null) {
rows.push({ label: 'RAM', value: formatMemory(info.total_memory_mb) })
const memoryMb = pickNumber(
info.total_memory_mb,
info.memory_total_mb,
info.memory_mb,
info.memory?.total_mb
)
if (memoryMb != null) {
rows.push({ label: 'RAM', value: formatMemory(memoryMb) })
}
if (info.os_info) {
rows.push({ label: 'OS', value: String(info.os_info) })
const osInfo = pickString(info.os_info, info.os, info.platform)
if (osInfo) {
rows.push({ label: 'OS', value: osInfo })
}
if (props.node.estimated_max_concurrency != null) {
@@ -45,13 +54,19 @@ const hardwareRows = computed(() => {
})
}
if (info.fd_limit != null) {
rows.push({ label: 'FD Limit', value: formatNumber(info.fd_limit) })
const fdLimit = pickNumber(info.fd_limit, info.file_descriptor_limit, info.ulimit_nofile)
if (fdLimit != null) {
rows.push({ label: 'FD Limit', value: formatNumber(fdLimit) })
}
return rows
})
const tooltipTitle = computed(() => {
if (hardwareRows.value.length === 0) return '暂无硬件信息上报'
return hardwareRows.value.map(row => `${row.label}: ${row.value}`).join(' | ')
})
const showHardwareInfo = computed(
() =>
!props.node.is_manual
@@ -69,6 +84,32 @@ function formatNumber(n: number) {
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`
return String(n)
}
function pickNumber(...values: unknown[]): number | null {
for (const value of values) {
if (value == null) continue
const parsed = typeof value === 'number' ? value : Number(value)
if (Number.isFinite(parsed)) return parsed
}
return null
}
function pickString(...values: unknown[]): string {
for (const value of values) {
if (value == null) continue
const text = String(value).trim()
if (text) return text
}
return ''
}
function toggleTooltip() {
open.value = !open.value
}
function handleOpenChange(value: boolean) {
open.value = value
}
</script>
<template>
@@ -76,12 +117,19 @@ function formatNumber(n: number) {
v-if="showHardwareInfo"
:delay-duration="0"
>
<Tooltip>
<Tooltip
:open="open"
@update:open="handleOpenChange"
>
<TooltipTrigger as-child>
<button
type="button"
aria-label="硬件信息"
class="inline-flex items-center justify-center rounded-sm p-0.5 hover:bg-muted/60 transition-colors"
:title="tooltipTitle"
class="inline-flex items-center justify-center rounded-sm p-0.5 hover:bg-muted/60 transition-colors cursor-help"
@click.stop="toggleTooltip"
@keydown.enter.prevent.stop="toggleTooltip"
@keydown.space.prevent.stop="toggleTooltip"
>
<Cpu class="h-3.5 w-3.5 text-muted-foreground" />
</button>