2025-03-13 03:41:21 +08:00
|
|
|
|
"use client"
|
2025-11-08 15:43:44 +08:00
|
|
|
|
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "@/components/window/Card"
|
2025-03-13 03:41:21 +08:00
|
|
|
|
import { ToolButton } from "@/components/window/ToolButton"
|
2025-11-08 13:24:00 +08:00
|
|
|
|
import { Chip, Skeleton } from "@heroui/react"
|
2025-03-13 03:44:48 +08:00
|
|
|
|
import { Refresh, SettingConfig } from "@icon-park/react"
|
2025-03-21 18:00:16 +08:00
|
|
|
|
// import { version } from "@tauri-apps/plugin-os"
|
2025-03-13 03:44:48 +08:00
|
|
|
|
import { type AllSystemInfo, allSysInfo } from "tauri-plugin-system-info-api"
|
2025-11-08 13:24:00 +08:00
|
|
|
|
import { invoke } from "@tauri-apps/api/core"
|
2025-11-08 13:28:41 +08:00
|
|
|
|
import useSWR, { useSWRConfig } from "swr"
|
2025-11-05 13:20:50 +08:00
|
|
|
|
|
2024-09-20 23:15:42 +08:00
|
|
|
|
export default function Page() {
|
2025-03-13 03:41:21 +08:00
|
|
|
|
return (
|
2025-11-05 02:24:17 +08:00
|
|
|
|
<section className="flex flex-col gap-4 overflow-hidden rounded-lg">
|
2025-11-05 13:20:50 +08:00
|
|
|
|
<div className="flex flex-col h-full gap-4 overflow-hidden hide-scrollbar">
|
|
|
|
|
|
<Card className="overflow-hidden">
|
2025-11-05 02:24:17 +08:00
|
|
|
|
<CardHeader>
|
|
|
|
|
|
<CardIcon type="menu">
|
|
|
|
|
|
<SettingConfig /> 硬件
|
|
|
|
|
|
</CardIcon>
|
|
|
|
|
|
<CardTool>
|
|
|
|
|
|
{/* <ToolButton>
|
|
|
|
|
|
<UploadOne />
|
|
|
|
|
|
云同步
|
|
|
|
|
|
</ToolButton> */}
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<HardwareInfo />
|
2025-11-05 02:24:17 +08:00
|
|
|
|
</CardTool>
|
|
|
|
|
|
</CardHeader>
|
2025-03-13 03:41:21 +08:00
|
|
|
|
|
2025-11-05 02:24:17 +08:00
|
|
|
|
<CardBody>
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<HardwareInfoContent />
|
2025-11-05 02:24:17 +08:00
|
|
|
|
</CardBody>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
2025-03-13 03:41:21 +08:00
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function HardwareInfo() {
|
2025-11-08 13:28:41 +08:00
|
|
|
|
const { mutate } = useSWRConfig()
|
2025-11-08 15:43:44 +08:00
|
|
|
|
|
2025-11-08 13:24:00 +08:00
|
|
|
|
return (
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<ToolButton
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
// 使用 SWR 的 mutate 来刷新数据
|
|
|
|
|
|
mutate("/api/hardware-info")
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<Refresh /> 刷新
|
|
|
|
|
|
</ToolButton>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
2025-03-21 16:02:47 +08:00
|
|
|
|
|
2025-11-08 13:24:00 +08:00
|
|
|
|
interface ComputerInfo {
|
|
|
|
|
|
OsName?: string
|
|
|
|
|
|
OSDisplayVersion?: string
|
|
|
|
|
|
BiosSMBIOSBIOSVersion?: string
|
|
|
|
|
|
CsManufacturer?: string
|
|
|
|
|
|
CsName?: string
|
2025-11-08 15:43:44 +08:00
|
|
|
|
ReleaseId?: string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface GpuInfo {
|
|
|
|
|
|
vendor: string
|
|
|
|
|
|
model: string
|
|
|
|
|
|
family: string
|
|
|
|
|
|
device_id: string
|
|
|
|
|
|
total_vram: number
|
|
|
|
|
|
used_vram: number
|
|
|
|
|
|
load_pct: number
|
|
|
|
|
|
temperature: number
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface MemoryInfo {
|
|
|
|
|
|
manufacturer?: string
|
|
|
|
|
|
part_number?: string
|
|
|
|
|
|
speed?: number // MHz
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface MonitorInfo {
|
|
|
|
|
|
name?: string
|
|
|
|
|
|
refresh_rate?: number // Hz
|
|
|
|
|
|
resolution_width?: number
|
|
|
|
|
|
resolution_height?: number
|
2025-11-08 13:24:00 +08:00
|
|
|
|
}
|
2025-03-13 03:41:21 +08:00
|
|
|
|
|
2025-11-08 13:28:41 +08:00
|
|
|
|
interface HardwareData {
|
|
|
|
|
|
allSysData: AllSystemInfo
|
|
|
|
|
|
computerInfo: ComputerInfo
|
2025-11-08 15:43:44 +08:00
|
|
|
|
gpuInfo: GpuInfo | null
|
|
|
|
|
|
memoryInfo: MemoryInfo[]
|
|
|
|
|
|
monitorInfo: MonitorInfo[]
|
2025-11-08 13:28:41 +08:00
|
|
|
|
}
|
2025-11-08 13:24:00 +08:00
|
|
|
|
|
2025-11-08 13:28:41 +08:00
|
|
|
|
// 硬件信息 fetcher
|
|
|
|
|
|
const hardwareInfoFetcher = async (): Promise<HardwareData> => {
|
2025-11-08 15:43:44 +08:00
|
|
|
|
// 并行获取系统信息、PowerShell 信息、GPU 信息、内存信息和显示器信息
|
|
|
|
|
|
const [sys, computerInfoData, gpuInfoData, memoryInfoData, monitorInfoData] = await Promise.all([
|
2025-11-08 13:28:41 +08:00
|
|
|
|
allSysInfo(),
|
|
|
|
|
|
invoke<ComputerInfo>("get_computer_info").catch((error) => {
|
|
|
|
|
|
console.error("获取 PowerShell 信息失败:", error)
|
|
|
|
|
|
return {} as ComputerInfo
|
2025-11-08 15:43:44 +08:00
|
|
|
|
}),
|
|
|
|
|
|
invoke<GpuInfo | null>("get_gpu_info").catch((error) => {
|
|
|
|
|
|
console.error("获取 GPU 信息失败:", error)
|
|
|
|
|
|
return null
|
|
|
|
|
|
}),
|
|
|
|
|
|
invoke<MemoryInfo[]>("get_memory_info").catch((error) => {
|
|
|
|
|
|
console.error("获取内存信息失败:", error)
|
|
|
|
|
|
return [] as MemoryInfo[]
|
|
|
|
|
|
}),
|
|
|
|
|
|
invoke<MonitorInfo[]>("get_monitor_info").catch((error) => {
|
|
|
|
|
|
console.error("获取显示器信息失败:", error)
|
|
|
|
|
|
return [] as MonitorInfo[]
|
|
|
|
|
|
}),
|
2025-11-08 13:28:41 +08:00
|
|
|
|
])
|
2025-11-08 15:43:44 +08:00
|
|
|
|
|
2025-11-08 13:28:41 +08:00
|
|
|
|
console.log("系统信息:", sys)
|
|
|
|
|
|
console.log("PowerShell 信息:", computerInfoData)
|
2025-11-08 15:43:44 +08:00
|
|
|
|
console.log("GPU 信息:", gpuInfoData)
|
|
|
|
|
|
console.log("内存信息:", memoryInfoData)
|
|
|
|
|
|
console.log("显示器信息:", monitorInfoData)
|
|
|
|
|
|
|
2025-11-08 13:28:41 +08:00
|
|
|
|
if (sys?.cpus) {
|
|
|
|
|
|
console.log("CPU数据:", sys.cpus)
|
|
|
|
|
|
console.log("第一个CPU:", sys.cpus[0])
|
|
|
|
|
|
if (sys.cpus[0]) {
|
|
|
|
|
|
console.log("CPU字段:", Object.keys(sys.cpus[0]))
|
2025-03-13 03:41:21 +08:00
|
|
|
|
}
|
2025-11-08 13:24:00 +08:00
|
|
|
|
}
|
2025-11-08 15:43:44 +08:00
|
|
|
|
|
2025-11-08 13:28:41 +08:00
|
|
|
|
return {
|
|
|
|
|
|
allSysData: sys,
|
2025-11-08 15:43:44 +08:00
|
|
|
|
computerInfo: computerInfoData,
|
|
|
|
|
|
gpuInfo: gpuInfoData,
|
|
|
|
|
|
memoryInfo: memoryInfoData,
|
|
|
|
|
|
monitorInfo: monitorInfoData,
|
2025-11-08 13:28:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-03-13 03:41:21 +08:00
|
|
|
|
|
2025-11-08 13:28:41 +08:00
|
|
|
|
function HardwareInfoContent() {
|
2025-11-08 15:43:44 +08:00
|
|
|
|
const { data, isLoading } = useSWR<HardwareData>("/api/hardware-info", hardwareInfoFetcher, {
|
|
|
|
|
|
revalidateOnFocus: false,
|
|
|
|
|
|
revalidateOnReconnect: false,
|
|
|
|
|
|
dedupingInterval: 5 * 60 * 1000, // 5分钟内相同请求去重
|
|
|
|
|
|
})
|
2025-11-08 13:28:41 +08:00
|
|
|
|
|
|
|
|
|
|
const allSysData = data?.allSysData
|
|
|
|
|
|
const computerInfo = data?.computerInfo || {}
|
2025-11-08 15:43:44 +08:00
|
|
|
|
const gpuInfo = data?.gpuInfo
|
|
|
|
|
|
const memoryInfo = data?.memoryInfo || []
|
|
|
|
|
|
const monitorInfo = data?.monitorInfo || []
|
2025-03-13 03:41:21 +08:00
|
|
|
|
|
2025-11-08 13:24:00 +08:00
|
|
|
|
const formatBytes = (bytes?: number) => {
|
|
|
|
|
|
if (!bytes) return "未知"
|
|
|
|
|
|
const gb = bytes / 1024 / 1024 / 1024
|
|
|
|
|
|
if (gb >= 1) {
|
|
|
|
|
|
return `${gb.toFixed(2)}GB`
|
|
|
|
|
|
}
|
|
|
|
|
|
const mb = bytes / 1024 / 1024
|
|
|
|
|
|
return `${mb.toFixed(2)}MB`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-08 15:43:44 +08:00
|
|
|
|
// 格式化系统信息:Windows 11 (26200) 25H2
|
|
|
|
|
|
const formatSystemInfo = () => {
|
|
|
|
|
|
const osVersion = allSysData?.os_version || null
|
|
|
|
|
|
// 使用 OSDisplayVersion 作为版本代码(如 "25H2")
|
|
|
|
|
|
const osDisplayVersion = computerInfo.OSDisplayVersion || null
|
|
|
|
|
|
|
|
|
|
|
|
let systemStr = allSysData?.name || "未知"
|
|
|
|
|
|
if (osVersion) {
|
|
|
|
|
|
systemStr += ` ${osVersion}`
|
|
|
|
|
|
}
|
|
|
|
|
|
if (osDisplayVersion) {
|
|
|
|
|
|
systemStr += ` ${osDisplayVersion}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return systemStr
|
|
|
|
|
|
}
|
|
|
|
|
|
const memoryUsagePercent =
|
|
|
|
|
|
allSysData?.total_memory && allSysData?.used_memory !== undefined
|
|
|
|
|
|
? Math.round((allSysData.used_memory / allSysData.total_memory) * 100)
|
|
|
|
|
|
: null
|
|
|
|
|
|
|
2025-11-08 13:24:00 +08:00
|
|
|
|
// 计算所有CPU核心的平均频率(统一转换为GHz)
|
2025-11-08 15:43:44 +08:00
|
|
|
|
const averageCpuFrequency =
|
|
|
|
|
|
allSysData?.cpus && allSysData.cpus.length > 0
|
|
|
|
|
|
? (() => {
|
|
|
|
|
|
// 尝试多个可能的频率字段名
|
|
|
|
|
|
const frequencies = allSysData.cpus
|
|
|
|
|
|
.map((cpu) => {
|
|
|
|
|
|
// 尝试不同的字段名
|
|
|
|
|
|
const freq = (cpu as any).frequency ?? (cpu as any).freq ?? (cpu as any).clock_speed
|
|
|
|
|
|
return freq
|
|
|
|
|
|
})
|
|
|
|
|
|
.filter((freq): freq is number => {
|
|
|
|
|
|
// 确保是有效的数字且大于0
|
|
|
|
|
|
return typeof freq === "number" && !isNaN(freq) && freq > 0
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (frequencies.length === 0) {
|
|
|
|
|
|
console.log("未找到有效的CPU频率数据,CPU对象:", allSysData.cpus[0])
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const sum = frequencies.reduce((acc, freq) => acc + freq, 0)
|
|
|
|
|
|
const avg = sum / frequencies.length
|
|
|
|
|
|
|
|
|
|
|
|
// 判断单位并统一转换为GHz
|
|
|
|
|
|
// 如果值在2-10范围,可能是GHz
|
|
|
|
|
|
// 如果值在2000-10000范围,可能是MHz(需要除以1000)
|
|
|
|
|
|
// 如果值在百万级别(2000000+),可能是Hz(需要除以1,000,000)
|
|
|
|
|
|
let freqInGhz: number
|
|
|
|
|
|
if (avg >= 1_000_000) {
|
|
|
|
|
|
// Hz单位,转换为GHz
|
|
|
|
|
|
freqInGhz = avg / 1_000_000
|
|
|
|
|
|
} else if (avg >= 1000) {
|
|
|
|
|
|
// MHz单位,转换为GHz
|
|
|
|
|
|
freqInGhz = avg / 1000
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 已经是GHz单位
|
|
|
|
|
|
freqInGhz = avg
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log("CPU频率数据:", frequencies, "原始平均值:", avg, "转换为GHz:", freqInGhz)
|
|
|
|
|
|
|
|
|
|
|
|
return freqInGhz
|
|
|
|
|
|
})()
|
|
|
|
|
|
: null
|
2025-11-08 13:24:00 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果正在加载,显示 Skeleton 骨架屏
|
2025-11-08 13:28:41 +08:00
|
|
|
|
if (isLoading) {
|
2025-11-08 13:24:00 +08:00
|
|
|
|
return (
|
|
|
|
|
|
<div className="flex flex-col gap-4">
|
|
|
|
|
|
{/* 系统信息 Skeleton */}
|
|
|
|
|
|
<div className="flex flex-col gap-2">
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<Skeleton className="w-24 h-5 rounded" />
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<div className="flex flex-wrap gap-2">
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<Skeleton className="w-48 h-8 rounded-full" />
|
|
|
|
|
|
<Skeleton className="w-32 h-8 rounded-full" />
|
2025-11-08 13:24:00 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-11-08 15:43:44 +08:00
|
|
|
|
{/* CPU 信息 Skeleton */}
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<div className="flex flex-col gap-2">
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<Skeleton className="w-20 h-5 rounded" />
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<div className="flex flex-wrap gap-2">
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<Skeleton className="w-56 h-8 rounded-full" />
|
|
|
|
|
|
<Skeleton className="w-32 h-8 rounded-full" />
|
|
|
|
|
|
<Skeleton className="h-8 rounded-full w-28" />
|
2025-11-08 13:24:00 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-11-08 15:43:44 +08:00
|
|
|
|
{/* 内存信息 Skeleton */}
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<div className="flex flex-col gap-2">
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<Skeleton className="w-16 h-5 rounded" />
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<div className="flex flex-wrap gap-2">
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<Skeleton className="h-8 rounded-full w-36" />
|
|
|
|
|
|
<Skeleton className="w-40 h-8 rounded-full" />
|
|
|
|
|
|
<Skeleton className="h-8 rounded-full w-36" />
|
2025-11-08 13:24:00 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-11-08 15:43:44 +08:00
|
|
|
|
{/* GPU 信息 Skeleton */}
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<div className="flex flex-col gap-2">
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<Skeleton className="w-16 h-5 rounded" />
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<div className="flex flex-wrap gap-2">
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<Skeleton className="h-8 rounded-full w-52" />
|
2025-11-08 13:24:00 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-11-08 15:43:44 +08:00
|
|
|
|
{/* 主板信息 Skeleton */}
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<div className="flex flex-col gap-2">
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<Skeleton className="w-16 h-5 rounded" />
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<div className="flex flex-wrap gap-2">
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<Skeleton className="w-40 h-8 rounded-full" />
|
|
|
|
|
|
<Skeleton className="h-8 rounded-full w-36" />
|
2025-11-08 13:24:00 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-13 03:41:21 +08:00
|
|
|
|
return (
|
2025-11-08 13:24:00 +08:00
|
|
|
|
<div className="flex flex-col gap-4">
|
|
|
|
|
|
{/* 系统信息 */}
|
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
|
<div className="text-sm font-medium text-foreground-600">系统信息</div>
|
|
|
|
|
|
<div className="flex flex-wrap gap-2">
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">系统:</span> {formatSystemInfo()}
|
|
|
|
|
|
</Chip>
|
2025-11-08 13:24:00 +08:00
|
|
|
|
{computerInfo.CsName && (
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">主机名:</span> {computerInfo.CsName}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{!computerInfo.CsName && allSysData?.hostname && (
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">主机名:</span> {allSysData.hostname}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* CPU 信息 */}
|
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
|
<div className="text-sm font-medium text-foreground-600">处理器</div>
|
|
|
|
|
|
<div className="flex flex-wrap gap-2">
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">型号:</span> {allSysData?.cpus?.[0]?.brand || "未知"}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
{averageCpuFrequency !== null && (
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">频率:</span> {averageCpuFrequency.toFixed(2)} GHz
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
)}
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">核心数:</span> {allSysData?.cpu_count || "未知"}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 内存信息 */}
|
|
|
|
|
|
{allSysData?.total_memory && (
|
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
|
<div className="text-sm font-medium text-foreground-600">内存</div>
|
|
|
|
|
|
<div className="flex flex-wrap gap-2">
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">总容量:</span> {formatBytes(allSysData.total_memory)}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
{allSysData.used_memory !== undefined && (
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">已用:</span> {formatBytes(allSysData.used_memory)}
|
|
|
|
|
|
{memoryUsagePercent !== null && (
|
|
|
|
|
|
<span className="ml-1">({memoryUsagePercent}%)</span>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{allSysData.total_memory !== undefined && allSysData.used_memory !== undefined && (
|
|
|
|
|
|
<Chip>
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<span className="font-medium">可用:</span>{" "}
|
|
|
|
|
|
{formatBytes(allSysData.total_memory - allSysData.used_memory)}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{memoryInfo.length > 0 && memoryInfo[0].part_number && (
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">型号:</span> {memoryInfo[0].part_number}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{memoryInfo.length > 0 && memoryInfo[0].speed && (
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">频率:</span> {memoryInfo[0].speed} MHz
|
2025-11-08 13:24:00 +08:00
|
|
|
|
</Chip>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* GPU 信息 */}
|
2025-11-08 15:43:44 +08:00
|
|
|
|
{gpuInfo ? (
|
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
|
<div className="text-sm font-medium text-foreground-600">显卡</div>
|
|
|
|
|
|
<div className="flex flex-wrap gap-2">
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">型号:</span> {gpuInfo.model}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">总显存:</span> {formatBytes(gpuInfo.total_vram)}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">已用显存:</span> {formatBytes(gpuInfo.used_vram)}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">温度:</span> {gpuInfo.temperature.toFixed(2)}°C
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
allSysData?.components &&
|
|
|
|
|
|
allSysData.components.length > 0 &&
|
2025-11-08 13:24:00 +08:00
|
|
|
|
(() => {
|
2025-11-08 15:43:44 +08:00
|
|
|
|
const gpuComponents = allSysData.components.filter(
|
|
|
|
|
|
(comp) =>
|
|
|
|
|
|
comp.label?.toLowerCase().includes("gpu") ||
|
|
|
|
|
|
comp.label?.toLowerCase().includes("graphics") ||
|
|
|
|
|
|
comp.label?.toLowerCase().includes("显卡")
|
2025-11-08 13:24:00 +08:00
|
|
|
|
)
|
2025-11-08 15:43:44 +08:00
|
|
|
|
|
2025-11-08 13:24:00 +08:00
|
|
|
|
if (gpuComponents.length === 0) return null
|
2025-11-08 15:43:44 +08:00
|
|
|
|
|
2025-11-08 13:24:00 +08:00
|
|
|
|
return (
|
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
|
<div className="text-sm font-medium text-foreground-600">显卡</div>
|
|
|
|
|
|
<div className="flex flex-wrap gap-2">
|
|
|
|
|
|
{gpuComponents.map((gpu, index) => (
|
|
|
|
|
|
<Chip key={index}>
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<span className="font-medium">GPU{index > 0 ? ` ${index + 1}` : ""}:</span>{" "}
|
|
|
|
|
|
{gpu.label || "未知"}
|
2025-11-08 13:24:00 +08:00
|
|
|
|
{gpu.temperature !== undefined && (
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<span className="ml-1">
|
|
|
|
|
|
({gpu.temperature}°C{gpu.max !== undefined ? ` / ${gpu.max}°C` : ""})
|
|
|
|
|
|
</span>
|
2025-11-08 13:24:00 +08:00
|
|
|
|
)}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
})()
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2025-11-08 15:43:44 +08:00
|
|
|
|
{/* 主板信息 */}
|
|
|
|
|
|
{(computerInfo.CsManufacturer || computerInfo.BiosSMBIOSBIOSVersion) && (
|
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
|
<div className="text-sm font-medium text-foreground-600">主板</div>
|
|
|
|
|
|
<div className="flex flex-wrap gap-2">
|
|
|
|
|
|
{computerInfo.CsManufacturer && (
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">制造商:</span> {computerInfo.CsManufacturer}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{computerInfo.BiosSMBIOSBIOSVersion && (
|
|
|
|
|
|
<Chip>
|
|
|
|
|
|
<span className="font-medium">BIOS版本:</span> {computerInfo.BiosSMBIOSBIOSVersion}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* 显示器信息 */}
|
|
|
|
|
|
{monitorInfo.length > 0 && monitorInfo.some((m) => m.refresh_rate) && (
|
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
|
<div className="text-sm font-medium text-foreground-600">显示器</div>
|
|
|
|
|
|
<div className="flex flex-wrap gap-2">
|
|
|
|
|
|
{monitorInfo.map(
|
|
|
|
|
|
(monitor, index) =>
|
|
|
|
|
|
monitor.refresh_rate && (
|
|
|
|
|
|
<Chip key={index}>
|
|
|
|
|
|
<span className="font-medium">
|
|
|
|
|
|
刷新率{monitorInfo.length > 1 ? ` ${index + 1}` : ""}:
|
|
|
|
|
|
</span>{" "}
|
|
|
|
|
|
{monitor.refresh_rate} Hz
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
)
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2025-11-08 13:24:00 +08:00
|
|
|
|
{/* 电池信息 */}
|
|
|
|
|
|
{allSysData?.batteries && allSysData.batteries.length > 0 && (
|
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
|
<div className="text-sm font-medium text-foreground-600">电池</div>
|
|
|
|
|
|
<div className="flex flex-wrap gap-2">
|
|
|
|
|
|
{allSysData.batteries.map((battery, index) => (
|
|
|
|
|
|
<Chip key={index}>
|
|
|
|
|
|
<span className="font-medium">电池{index > 0 ? ` ${index + 1}` : ""}:</span>
|
|
|
|
|
|
{battery.state && `${battery.state} `}
|
|
|
|
|
|
{battery.state_of_charge !== undefined && `${battery.state_of_charge}% `}
|
|
|
|
|
|
{battery.energy_full !== undefined && battery.energy !== undefined && (
|
2025-11-08 15:43:44 +08:00
|
|
|
|
<span>
|
|
|
|
|
|
({formatBytes(battery.energy)} / {formatBytes(battery.energy_full)})
|
|
|
|
|
|
</span>
|
2025-11-08 13:24:00 +08:00
|
|
|
|
)}
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2025-03-13 03:41:21 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
)
|
2024-09-20 23:15:42 +08:00
|
|
|
|
}
|