mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
fix(frontend): HardwareTooltip 组件健壮性重构
- hardware_info 支持 string/object/null 类型的安全解析 - 用 computed 统一提取展示行,无硬件信息时显示提示文案 - 增加 button 的 title/aria-label 属性
This commit is contained in:
@@ -6,8 +6,61 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@/components/ui'
|
} from '@/components/ui'
|
||||||
import { Cpu } from 'lucide-vue-next'
|
import { Cpu } from 'lucide-vue-next'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
defineProps<{ node: ProxyNode }>()
|
const props = defineProps<{ node: ProxyNode }>()
|
||||||
|
|
||||||
|
const hardwareInfo = computed<Record<string, any> | null>(() => {
|
||||||
|
const info = props.node.hardware_info
|
||||||
|
if (info == null) return null
|
||||||
|
if (typeof info === 'string') {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(info)
|
||||||
|
if (parsed && typeof parsed === 'object') return parsed as Record<string, any>
|
||||||
|
} catch {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
if (typeof info === 'object') return info as Record<string, any>
|
||||||
|
return {}
|
||||||
|
})
|
||||||
|
|
||||||
|
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` })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.total_memory_mb != null) {
|
||||||
|
rows.push({ label: 'RAM', value: formatMemory(info.total_memory_mb) })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.os_info) {
|
||||||
|
rows.push({ label: 'OS', value: String(info.os_info) })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.node.estimated_max_concurrency != null) {
|
||||||
|
rows.push({
|
||||||
|
label: 'Max Concurrency',
|
||||||
|
value: `~${formatNumber(props.node.estimated_max_concurrency)}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.fd_limit != null) {
|
||||||
|
rows.push({ label: 'FD Limit', value: formatNumber(info.fd_limit) })
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows
|
||||||
|
})
|
||||||
|
|
||||||
|
const showHardwareInfo = computed(
|
||||||
|
() =>
|
||||||
|
!props.node.is_manual
|
||||||
|
&& (hardwareInfo.value !== null || props.node.estimated_max_concurrency != null)
|
||||||
|
)
|
||||||
|
|
||||||
function formatMemory(mb: number | null) {
|
function formatMemory(mb: number | null) {
|
||||||
if (mb == null) return '-'
|
if (mb == null) return '-'
|
||||||
@@ -23,10 +76,12 @@ function formatNumber(n: number) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Popover v-if="!node.is_manual && node.hardware_info">
|
<Popover v-if="showHardwareInfo">
|
||||||
<PopoverTrigger as-child>
|
<PopoverTrigger as-child>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
title="Hardware info"
|
||||||
|
aria-label="Hardware info"
|
||||||
class="inline-flex items-center justify-center rounded-sm p-0.5 hover:bg-muted/60 transition-colors"
|
class="inline-flex items-center justify-center rounded-sm p-0.5 hover:bg-muted/60 transition-colors"
|
||||||
>
|
>
|
||||||
<Cpu class="h-3.5 w-3.5 text-muted-foreground" />
|
<Cpu class="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
@@ -37,21 +92,20 @@ function formatNumber(n: number) {
|
|||||||
:side-offset="8"
|
:side-offset="8"
|
||||||
class="w-auto p-3 text-xs space-y-1"
|
class="w-auto p-3 text-xs space-y-1"
|
||||||
>
|
>
|
||||||
<div v-if="node.hardware_info.cpu_cores">
|
<div
|
||||||
CPU: {{ node.hardware_info.cpu_cores }} cores
|
v-if="hardwareRows.length === 0"
|
||||||
</div>
|
class="text-muted-foreground"
|
||||||
<div v-if="node.hardware_info.total_memory_mb">
|
>
|
||||||
RAM: {{ formatMemory(node.hardware_info.total_memory_mb) }}
|
No hardware info reported.
|
||||||
</div>
|
|
||||||
<div v-if="node.hardware_info.os_info">
|
|
||||||
OS: {{ node.hardware_info.os_info }}
|
|
||||||
</div>
|
|
||||||
<div v-if="node.estimated_max_concurrency">
|
|
||||||
Max Concurrency: ~{{ formatNumber(node.estimated_max_concurrency) }}
|
|
||||||
</div>
|
|
||||||
<div v-if="node.hardware_info.fd_limit">
|
|
||||||
FD Limit: {{ formatNumber(node.hardware_info.fd_limit) }}
|
|
||||||
</div>
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
v-for="row in hardwareRows"
|
||||||
|
:key="row.label"
|
||||||
|
>
|
||||||
|
{{ row.label }}: {{ row.value }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user