From e7e0bbd9536c5280053013b9b12fd6fd4eaf0520 Mon Sep 17 00:00:00 2001 From: purp1e Date: Sat, 8 Nov 2025 13:24:00 +0800 Subject: [PATCH] [feat] better hw info (still no gpu) --- bun.lockb | Bin 425085 -> 425085 bytes next-env.d.ts | 2 +- package.json | 4 +- src-tauri/Cargo.lock | 1 + src-tauri/Cargo.toml | 1 + src-tauri/src/cmds.rs | 44 +++++ src-tauri/src/main.rs | 1 + src/app/(main)/gear/page.tsx | 358 ++++++++++++++++++++++++++++++++--- 8 files changed, 377 insertions(+), 34 deletions(-) diff --git a/bun.lockb b/bun.lockb index 52a6cbe627a856d4a89bc5fd88d3b16b6ce1645a..8fa750c848f2757069a2e79b33ce6ec04c3700be 100755 GIT binary patch delta 2982 zcmZ`*3p`Y5A3kR;hjGb_R&tpkm)e$wnGDKwLB*C4skS7d*8MW(l45-$$|#L!T8B_c zglcoC2K6J_RxY*DJ&B@?Zu(@~iLmbslj+kpzu$kJ^S;mjJn#Sf=gj*)qf)(5sa`tO z_|lS|yBVj6L54Pq^EN_xKUk`F7zeafn>-E4=TUU%)N*$dvS14fhKwL6VJSc|Kx2Ru zfGiRW=>QyegP}P9I{}&itOF?Ha{-zHjGN@Q05k?;2Ee%hwI}((H52ox07+oJc^wRC z0IZ$B$^mi+Ihbi|?z^h*B!j_Z%y#ZeF9&PGJcf)(U zg+s4qREw|Nj$Zc`qmymKm=`S+#Utf5EAT^q+O zLV5)Sb2C@(7p@(x3G%@$uX1|(QTS)O*UPM39eaLjXJBZ2YhAUl)f-Z7evL$C$sb zk&{_7plhz*Gk)x&7I+0;WX2ooQiu47yb%kSulIpy{b;%IvoJbrPv^l90V zC#LNB_dja0=Wo4H8bfcD3X=blUY@JJaX0flQTzT3XD9pXeZHYwdtXbrFbud@%N-`XkxVnE|%*;sxsZf6_!ECp4 z`lk*TP(N5CdU~5RG>Wd56^yrr4ym;q3{$mod0i;IMHBtnM`+1%;fWUKqog8?mmmRDeNuT1*TRE6NNUYlp0Qw`HN(1}QbTz+quN@1 z7U9LNqF2fAk*?(rJDlBDiuF8-)mPc*+H{Jo=;wuph+FMXJf|2K$d|xDM4(%stV{N| zmGi0G;=DK<$L2@Gc~@b#IaVGOBm*^c}yP4L0r zh2{;KJQ~Zhx8zSWMO76DpHdbUk`8}ZcQ(D%&8qD|dEuSA@z@($*|_9EgAr{ELloza zMKAO+yIp(y_xuzm8MNT)ITz`?uM}@eILPR%6kN-1pR8Eci9%&zbNA(x+V1(6ezp&5 z<<~lNL=A-`-yOaan>o6}{t3_QT(16&xFlcZ%?+swozz^ozTaOSv+6Y5tFwuHoN6A} zFf>`PtP@4bx?;&#;kd6)Ii=-*qX5;qvQwproTe3YG|j_fkTKS7TH+$o+AOIPaT-le zt2mJ^ne?q�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 R3J%BPWRxl>ipL)*{tdu?$OHfY diff --git a/next-env.d.ts b/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/package.json b/package.json index aa0a484..8d0b041 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "tauri-plugin-system-info-api": "^2.0.10" }, "devDependencies": { - "@tailwindcss/postcss": "^4.1.16", + "@tailwindcss/postcss": "^4.1.17", "@tauri-apps/cli": "^2.9.3", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", @@ -73,7 +73,7 @@ "postcss-import": "^16.1.1", "postcss-nesting": "^13.0.2", "tailwind-merge": "3.0.2", - "tailwindcss": "^4.1.16", + "tailwindcss": "^4.1.17", "typescript": "^5.9.3" }, "browserslist": { diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index e2e13ae..ed1131c 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -34,6 +34,7 @@ dependencies = [ "tauri-plugin-store", "tauri-plugin-system-info", "tauri-plugin-valtio", + "tokio", "walkdir", "window-vibrancy", "winreg 0.55.0", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index e238379..7461b6d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -54,6 +54,7 @@ tauri-plugin-deep-link = "2.4.5" anyhow = "1.0.100" notify = "8.2.0" dirs = "6.0.0" +tokio = { version = "1.40", features = ["process"] } [target.'cfg(windows)'.dependencies] # Windows Only winreg = "0.55.0" diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index a2fbe5d..0d8bb61 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -336,3 +336,47 @@ pub async fn download_app_update( pub fn install_app_update(installer_path: String) -> Result<(), String> { wrap_err!(install_update(&installer_path)) } + +/// 获取 PowerShell Get-ComputerInfo 信息(异步版本) +#[tauri::command] +#[cfg(target_os = "windows")] +pub async fn get_computer_info() -> Result { + use tokio::process::Command; + + // 异步执行 PowerShell 命令获取计算机信息并转换为 JSON + let output = Command::new("powershell") + .args(&[ + "-NoProfile", + "-Command", + "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-ComputerInfo | Select-Object OsName, OSDisplayVersion, BiosSMBIOSBIOSVersion, CsManufacturer, CsName | ConvertTo-Json -Compress" + ]) + .output() + .await + .map_err(|e| format!("执行 PowerShell 命令失败: {}", e))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(format!("PowerShell 命令执行失败: {}", stderr)); + } + + // 处理 PowerShell 输出,移除 BOM 和空白字符 + let stdout = String::from_utf8_lossy(&output.stdout); + let cleaned = stdout.trim().trim_start_matches('\u{feff}'); // 移除 BOM + + // 如果输出为空,返回空对象 + if cleaned.is_empty() { + return Ok(serde_json::json!({})); + } + + let json: serde_json::Value = serde_json::from_str(cleaned) + .map_err(|e| format!("解析 JSON 失败: {},原始输出: {}", e, cleaned))?; + + Ok(json) +} + +/// 获取 PowerShell Get-ComputerInfo 信息(非 Windows 平台返回空对象) +#[tauri::command] +#[cfg(not(target_os = "windows"))] +pub async fn get_computer_info() -> Result { + Ok(serde_json::json!({})) +} \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index a18afbb..a82ddc9 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -169,6 +169,7 @@ fn main() { cmds::check_app_update, cmds::download_app_update, cmds::install_app_update, + cmds::get_computer_info, on_button_clicked ]) .run(ctx) diff --git a/src/app/(main)/gear/page.tsx b/src/app/(main)/gear/page.tsx index 60d45e4..a194e05 100644 --- a/src/app/(main)/gear/page.tsx +++ b/src/app/(main)/gear/page.tsx @@ -7,11 +7,12 @@ import { CardTool, } from "@/components/window/Card" import { ToolButton } from "@/components/window/ToolButton" -import { Chip } from "@heroui/react" +import { Chip, Skeleton } from "@heroui/react" import { Refresh, SettingConfig } from "@icon-park/react" // import { version } from "@tauri-apps/plugin-os" import { useEffect, useState } from "react" import { type AllSystemInfo, allSysInfo } from "tauri-plugin-system-info-api" +import { invoke } from "@tauri-apps/api/core" export default function Page() { return ( @@ -27,14 +28,12 @@ export default function Page() { 云同步 */} - - 刷新 - + - + @@ -43,39 +42,336 @@ export default function Page() { } function HardwareInfo() { - const [allSysData, setAllSysData] = useState() - // const [memInfo, setMemInfo] = useState("") - // const [staticData, setStaticData] = useState("") - // const [cpuData, setCpuData] = useState("") - // const [batteryData, setBatteryData] = useState("") + return ( + { + // 触发刷新事件 + window.dispatchEvent(new CustomEvent('refresh-hardware-info')) + }}> + 刷新 + + ) +} +interface ComputerInfo { + OsName?: string + OSDisplayVersion?: string + BiosSMBIOSBIOSVersion?: string + CsManufacturer?: string + CsName?: string +} + +function HardwareInfoContent() { + const [allSysData, setAllSysData] = useState() + const [computerInfo, setComputerInfo] = useState({}) + const [loading, setLoading] = useState(false) + + const fetchData = async () => { + setLoading(true) + try { + // 并行获取系统信息和 PowerShell 信息 + const [sys, computerInfoData] = await Promise.all([ + allSysInfo(), + invoke("get_computer_info").catch((error) => { + console.error("获取 PowerShell 信息失败:", error) + return {} as ComputerInfo + }) + ]) + + console.log("系统信息:", sys) + console.log("PowerShell 信息:", computerInfoData) + + 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])) + } + } + setAllSysData(sys) + setComputerInfo(computerInfoData) + } catch (error) { + console.error("获取系统信息失败:", error) + } finally { + setLoading(false) + } + } useEffect(() => { - const fetchData = async () => { - const sys = await allSysInfo() - console.log(sys) - setAllSysData(sys) - // console.log(await memoryInfo()) - // console.log(await staticInfo()) - // console.log(await cpuInfo()) - // console.log(await batteries()) - } - void fetchData() + + // 监听刷新事件 + const handleRefresh = () => { + void fetchData() + } + window.addEventListener('refresh-hardware-info', handleRefresh) + return () => { + window.removeEventListener('refresh-hardware-info', handleRefresh) + } }, []) + 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` + } + + // 如果 PowerShell 提供了 OSDisplayVersion,直接使用;否则尝试从 kernel_version 推断 + const windowsVersionCode = computerInfo.OSDisplayVersion || null + const memoryUsagePercent = allSysData?.total_memory && allSysData?.used_memory !== undefined + ? Math.round((allSysData.used_memory / allSysData.total_memory) * 100) + : null + + // 计算所有CPU核心的平均频率(统一转换为GHz) + 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 + + // 如果正在加载,显示 Skeleton 骨架屏 + if (loading) { + return ( +
+ {/* 系统信息 Skeleton */} +
+ +
+ + +
+
+ + {/* 主板信息 Skeleton */} +
+ +
+ + +
+
+ + {/* CPU 信息 Skeleton */} +
+ +
+ + + +
+
+ + {/* 内存信息 Skeleton */} +
+ +
+ + + +
+
+ + {/* GPU 信息 Skeleton */} +
+ +
+ +
+
+
+ ) + } + return ( -
- CPU型号: {allSysData?.cpus[0]?.brand} - 线程数: {allSysData?.cpu_count} - - 系统: {allSysData?.name} {allSysData?.os_version} - - - 内存: - {allSysData?.total_memory && - `${(allSysData.total_memory / 1024 / 1024 / 1024).toFixed(0)}GB`} - +
+ {/* 系统信息 */} +
+
系统信息
+
+ {computerInfo.OsName && ( + + 系统: {computerInfo.OsName} + {windowsVersionCode && ( + ({windowsVersionCode}) + )} + + )} + {!computerInfo.OsName && ( + + 系统: {allSysData?.name || "未知"} {allSysData?.os_version || ""} + {allSysData?.kernel_version && ( + <> + {" "}{allSysData.kernel_version} + {windowsVersionCode && ( + ({windowsVersionCode}) + )} + + )} + + )} + {computerInfo.CsName && ( + + 主机名: {computerInfo.CsName} + + )} + {!computerInfo.CsName && allSysData?.hostname && ( + + 主机名: {allSysData.hostname} + + )} +
+
+ + {/* 主板信息 */} + {(computerInfo.CsManufacturer || computerInfo.BiosSMBIOSBIOSVersion) && ( +
+
主板
+
+ {computerInfo.CsManufacturer && ( + + 制造商: {computerInfo.CsManufacturer} + + )} + {computerInfo.BiosSMBIOSBIOSVersion && ( + + BIOS版本: {computerInfo.BiosSMBIOSBIOSVersion} + + )} +
+
+ )} + + {/* CPU 信息 */} +
+
处理器
+
+ + 型号: {allSysData?.cpus?.[0]?.brand || "未知"} + + {averageCpuFrequency !== null && ( + + 频率: {averageCpuFrequency.toFixed(2)} GHz + + )} + + 核心数: {allSysData?.cpu_count || "未知"} + +
+
+ + {/* 内存信息 */} + {allSysData?.total_memory && ( +
+
内存
+
+ + 总容量: {formatBytes(allSysData.total_memory)} + + {allSysData.used_memory !== undefined && ( + + 已用: {formatBytes(allSysData.used_memory)} + {memoryUsagePercent !== null && ( + ({memoryUsagePercent}%) + )} + + )} + {allSysData.total_memory !== undefined && allSysData.used_memory !== undefined && ( + + 可用: {formatBytes(allSysData.total_memory - allSysData.used_memory)} + + )} +
+
+ )} + + {/* GPU 信息 */} + {allSysData?.components && allSysData.components.length > 0 && ( + (() => { + const gpuComponents = allSysData.components.filter((comp) => + comp.label?.toLowerCase().includes('gpu') || + comp.label?.toLowerCase().includes('graphics') || + comp.label?.toLowerCase().includes('显卡') + ) + + if (gpuComponents.length === 0) return null + + return ( +
+
显卡
+
+ {gpuComponents.map((gpu, index) => ( + + GPU{index > 0 ? ` ${index + 1}` : ""}: {gpu.label || "未知"} + {gpu.temperature !== undefined && ( + ({gpu.temperature}°C{gpu.max !== undefined ? ` / ${gpu.max}°C` : ""}) + )} + + ))} +
+
+ ) + })() + )} + + {/* 电池信息 */} + {allSysData?.batteries && allSysData.batteries.length > 0 && ( +
+
电池
+
+ {allSysData.batteries.map((battery, index) => ( + + 电池{index > 0 ? ` ${index + 1}` : ""}: + {battery.state && `${battery.state} `} + {battery.state_of_charge !== undefined && `${battery.state_of_charge}% `} + {battery.energy_full !== undefined && battery.energy !== undefined && ( + ({formatBytes(battery.energy)} / {formatBytes(battery.energy_full)}) + )} + + ))} +
+
+ )}
) }