[feat] more mborad and memory info
This commit is contained in:
@@ -470,9 +470,338 @@ pub fn get_gpu_info() -> Result<Option<GpuInfo>, String> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 内存信息结构体
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct MemoryInfo {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
capacity: Option<u64>, // 容量(字节)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
manufacturer: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
speed: Option<u32>, // MHz,实际频率 ConfiguredClockSpeed
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
default_speed: Option<u32>, // MHz,默认频率 Speed(如果存在)
|
||||
}
|
||||
|
||||
/// 显示器信息结构体
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct MonitorInfo {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
manufacturer: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
model: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
name: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
refresh_rate: Option<u32>, // Hz
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
resolution_width: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
resolution_height: Option<u32>,
|
||||
}
|
||||
|
||||
/// 主板信息结构体
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct MotherboardInfo {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
manufacturer: Option<String>, // 制造商
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
model: Option<String>, // 型号
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
version: Option<String>,
|
||||
}
|
||||
|
||||
/// 获取内存信息(Windows)
|
||||
#[tauri::command]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub async fn get_memory_info() -> Result<Vec<MemoryInfo>, String> {
|
||||
use tokio::process::Command;
|
||||
|
||||
// 执行 PowerShell 命令获取内存信息
|
||||
let output = Command::new("powershell")
|
||||
.args(&[
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-WmiObject Win32_PhysicalMemory | Select-Object Capacity, Manufacturer, ConfiguredClockSpeed, Speed | 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));
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let cleaned = stdout.trim().trim_start_matches('\u{feff}');
|
||||
|
||||
if cleaned.is_empty() {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
// PowerShell 可能返回数组或单个对象
|
||||
let json: serde_json::Value = serde_json::from_str(cleaned)
|
||||
.map_err(|e| format!("解析 JSON 失败: {},原始输出: {}", e, cleaned))?;
|
||||
|
||||
let mut memory_list = Vec::new();
|
||||
|
||||
if let Some(array) = json.as_array() {
|
||||
// 如果是数组
|
||||
for item in array {
|
||||
memory_list.push(parse_memory_info(item));
|
||||
}
|
||||
} else {
|
||||
// 如果是单个对象
|
||||
memory_list.push(parse_memory_info(&json));
|
||||
}
|
||||
|
||||
Ok(memory_list)
|
||||
}
|
||||
|
||||
/// 解析内存信息
|
||||
fn parse_memory_info(json: &serde_json::Value) -> MemoryInfo {
|
||||
// 容量(字节)
|
||||
let capacity = json.get("Capacity")
|
||||
.and_then(|v| v.as_u64());
|
||||
|
||||
let manufacturer = json.get("Manufacturer")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
// 实际频率:优先使用 ConfiguredClockSpeed
|
||||
let speed = json.get("ConfiguredClockSpeed")
|
||||
.and_then(|v| v.as_u64())
|
||||
.map(|v| v as u32);
|
||||
|
||||
// 默认频率:Speed(如果存在)
|
||||
let default_speed = json.get("Speed")
|
||||
.and_then(|v| v.as_u64())
|
||||
.map(|v| v as u32);
|
||||
|
||||
MemoryInfo {
|
||||
capacity,
|
||||
manufacturer,
|
||||
speed,
|
||||
default_speed,
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取内存信息(非 Windows 平台返回空)
|
||||
#[tauri::command]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub async fn get_memory_info() -> Result<Vec<MemoryInfo>, String> {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
/// 获取显示器信息(Windows)
|
||||
#[tauri::command]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub async fn get_monitor_info() -> Result<Vec<MonitorInfo>, String> {
|
||||
use tokio::process::Command;
|
||||
|
||||
// 执行 PowerShell 命令获取显示器信息
|
||||
let output = Command::new("powershell")
|
||||
.args(&[
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-CimInstance -Namespace root/wmi -ClassName WmiMonitorID | ForEach-Object { [PSCustomObject]@{ Manufacturer = [System.Text.Encoding]::ASCII.GetString($_.ManufacturerName) -replace \"`0\"; Model = [System.Text.Encoding]::ASCII.GetString($_.UserFriendlyName) -replace \"`0\" } } | 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));
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let cleaned = stdout.trim().trim_start_matches('\u{feff}');
|
||||
|
||||
if cleaned.is_empty() {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
let json: serde_json::Value = serde_json::from_str(cleaned)
|
||||
.map_err(|e| format!("解析 JSON 失败: {},原始输出: {}", e, cleaned))?;
|
||||
|
||||
let mut monitor_list = Vec::new();
|
||||
|
||||
if let Some(array) = json.as_array() {
|
||||
for item in array {
|
||||
monitor_list.push(parse_monitor_info(item));
|
||||
}
|
||||
} else {
|
||||
monitor_list.push(parse_monitor_info(&json));
|
||||
}
|
||||
|
||||
// 尝试获取刷新率和分辨率信息
|
||||
let display_output = Command::new("powershell")
|
||||
.args(&[
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-CimInstance -Namespace root/wmi -ClassName WmiMonitorBasicDisplayParams | Select-Object MaxHorizontalImageSize, MaxVerticalImageSize | ConvertTo-Json -Compress"
|
||||
])
|
||||
.output()
|
||||
.await;
|
||||
|
||||
// 获取刷新率信息
|
||||
let refresh_output = Command::new("powershell")
|
||||
.args(&[
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-CimInstance -Namespace root/wmi -ClassName WmiMonitorListedSupportedSourceModes | Select-Object -First 1 -ExpandProperty ModeTimings | Select-Object -First 1 -ExpandProperty RefreshRate | ConvertTo-Json -Compress"
|
||||
])
|
||||
.output()
|
||||
.await;
|
||||
|
||||
// 获取当前显示器的分辨率和刷新率
|
||||
let current_display_output = Command::new("powershell")
|
||||
.args(&[
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Add-Type -AssemblyName System.Windows.Forms; $screens = [System.Windows.Forms.Screen]::AllScreens; $screens | ForEach-Object { [PSCustomObject]@{ Width = $_.Bounds.Width; Height = $_.Bounds.Height; Primary = $_.Primary } } | ConvertTo-Json -Compress"
|
||||
])
|
||||
.output()
|
||||
.await;
|
||||
|
||||
// 合并显示器信息
|
||||
if let Ok(display_result) = current_display_output {
|
||||
if display_result.status.success() {
|
||||
let display_str = String::from_utf8_lossy(&display_result.stdout).to_string();
|
||||
let display_str = display_str.trim().trim_start_matches('\u{feff}').to_string();
|
||||
if let Ok(display_json) = serde_json::from_str::<serde_json::Value>(&display_str) {
|
||||
if let Some(displays) = display_json.as_array() {
|
||||
for (i, display) in displays.iter().enumerate() {
|
||||
if i < monitor_list.len() {
|
||||
if let Some(width) = display.get("Width").and_then(|v| v.as_u64()) {
|
||||
monitor_list[i].resolution_width = Some(width as u32);
|
||||
}
|
||||
if let Some(height) = display.get("Height").and_then(|v| v.as_u64()) {
|
||||
monitor_list[i].resolution_height = Some(height as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(display) = display_json.as_object() {
|
||||
if monitor_list.len() > 0 {
|
||||
if let Some(width) = display.get("Width").and_then(|v| v.as_u64()) {
|
||||
monitor_list[0].resolution_width = Some(width as u32);
|
||||
}
|
||||
if let Some(height) = display.get("Height").and_then(|v| v.as_u64()) {
|
||||
monitor_list[0].resolution_height = Some(height as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(monitor_list)
|
||||
}
|
||||
|
||||
/// 解析显示器信息
|
||||
fn parse_monitor_info(json: &serde_json::Value) -> MonitorInfo {
|
||||
let manufacturer = json.get("Manufacturer")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
let model = json.get("Model")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
// 组合制造商和型号作为名称
|
||||
let name = match (&manufacturer, &model) {
|
||||
(Some(mfg), Some(model_name)) => Some(format!("{} {}", mfg, model_name)),
|
||||
(Some(mfg), None) => Some(mfg.clone()),
|
||||
(None, Some(model_name)) => Some(model_name.clone()),
|
||||
(None, None) => None,
|
||||
};
|
||||
|
||||
MonitorInfo {
|
||||
manufacturer,
|
||||
model,
|
||||
name,
|
||||
refresh_rate: None, // 需要从其他来源获取
|
||||
resolution_width: None,
|
||||
resolution_height: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取显示器信息(非 Windows 平台返回空)
|
||||
#[tauri::command]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub async fn get_monitor_info() -> Result<Vec<MonitorInfo>, String> {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
/// 获取主板信息(Windows)
|
||||
#[tauri::command]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub async fn get_motherboard_info() -> Result<MotherboardInfo, String> {
|
||||
use tokio::process::Command;
|
||||
|
||||
// 执行 PowerShell 命令获取主板信息
|
||||
let output = Command::new("powershell")
|
||||
.args(&[
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-CimInstance -ClassName Win32_BaseBoard | Select-Object Manufacturer, Product, Version | 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));
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let cleaned = stdout.trim().trim_start_matches('\u{feff}');
|
||||
|
||||
if cleaned.is_empty() {
|
||||
return Ok(MotherboardInfo {
|
||||
manufacturer: None,
|
||||
model: None,
|
||||
version: None,
|
||||
});
|
||||
}
|
||||
|
||||
let json: serde_json::Value = serde_json::from_str(cleaned)
|
||||
.map_err(|e| format!("解析 JSON 失败: {},原始输出: {}", e, cleaned))?;
|
||||
|
||||
// 分别获取制造商和型号
|
||||
let manufacturer = json.get("Manufacturer")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
let model = json.get("Product")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
Ok(MotherboardInfo {
|
||||
manufacturer,
|
||||
model,
|
||||
version: json.get("Version")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty()),
|
||||
})
|
||||
}
|
||||
|
||||
/// 获取主板信息(非 Windows 平台返回空)
|
||||
#[tauri::command]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub async fn get_motherboard_info() -> Result<MotherboardInfo, String> {
|
||||
Ok(MotherboardInfo {
|
||||
manufacturer: None,
|
||||
model: None,
|
||||
version: None,
|
||||
})
|
||||
}
|
||||
@@ -171,7 +171,9 @@ fn main() {
|
||||
cmds::install_app_update,
|
||||
cmds::get_computer_info,
|
||||
cmds::get_gpu_info,
|
||||
cmds::get_memory_info,
|
||||
cmds::get_monitor_info,
|
||||
cmds::get_motherboard_info,
|
||||
on_button_clicked
|
||||
])
|
||||
.run(ctx)
|
||||
|
||||
@@ -71,9 +71,10 @@ interface GpuInfo {
|
||||
}
|
||||
|
||||
interface MemoryInfo {
|
||||
capacity?: number // 容量(字节)
|
||||
manufacturer?: string
|
||||
part_number?: string
|
||||
speed?: number // MHz
|
||||
speed?: number // MHz,实际频率 ConfiguredClockSpeed
|
||||
default_speed?: number // MHz,默认频率 Speed(如果存在)
|
||||
}
|
||||
|
||||
interface MonitorInfo {
|
||||
@@ -83,42 +84,55 @@ interface MonitorInfo {
|
||||
resolution_height?: number
|
||||
}
|
||||
|
||||
interface MotherboardInfo {
|
||||
manufacturer?: string // 制造商
|
||||
model?: string // 型号
|
||||
version?: string
|
||||
}
|
||||
|
||||
interface HardwareData {
|
||||
allSysData: AllSystemInfo
|
||||
computerInfo: ComputerInfo
|
||||
gpuInfo: GpuInfo | null
|
||||
memoryInfo: MemoryInfo[]
|
||||
monitorInfo: MonitorInfo[]
|
||||
motherboardInfo: MotherboardInfo | null
|
||||
}
|
||||
|
||||
// 硬件信息 fetcher
|
||||
const hardwareInfoFetcher = async (): Promise<HardwareData> => {
|
||||
// 并行获取系统信息、PowerShell 信息、GPU 信息、内存信息和显示器信息
|
||||
const [sys, computerInfoData, gpuInfoData, memoryInfoData, monitorInfoData] = await Promise.all([
|
||||
allSysInfo(),
|
||||
invoke<ComputerInfo>("get_computer_info").catch((error) => {
|
||||
console.error("获取 PowerShell 信息失败:", error)
|
||||
return {} as ComputerInfo
|
||||
}),
|
||||
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[]
|
||||
}),
|
||||
])
|
||||
// 并行获取系统信息、PowerShell 信息、GPU 信息、内存信息、显示器信息和主板信息
|
||||
const [sys, computerInfoData, gpuInfoData, memoryInfoData, monitorInfoData, motherboardInfoData] =
|
||||
await Promise.all([
|
||||
allSysInfo(),
|
||||
invoke<ComputerInfo>("get_computer_info").catch((error) => {
|
||||
console.error("获取 PowerShell 信息失败:", error)
|
||||
return {} as ComputerInfo
|
||||
}),
|
||||
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[]
|
||||
}),
|
||||
invoke<MotherboardInfo>("get_motherboard_info").catch((error) => {
|
||||
console.error("获取主板信息失败:", error)
|
||||
return { manufacturer: undefined, model: undefined, version: undefined } as MotherboardInfo
|
||||
}),
|
||||
])
|
||||
|
||||
console.log("系统信息:", sys)
|
||||
console.log("PowerShell 信息:", computerInfoData)
|
||||
console.log("GPU 信息:", gpuInfoData)
|
||||
console.log("内存信息:", memoryInfoData)
|
||||
console.log("显示器信息:", monitorInfoData)
|
||||
console.log("主板信息:", motherboardInfoData)
|
||||
|
||||
if (sys?.cpus) {
|
||||
console.log("CPU数据:", sys.cpus)
|
||||
@@ -134,11 +148,12 @@ const hardwareInfoFetcher = async (): Promise<HardwareData> => {
|
||||
gpuInfo: gpuInfoData,
|
||||
memoryInfo: memoryInfoData,
|
||||
monitorInfo: monitorInfoData,
|
||||
motherboardInfo: motherboardInfoData,
|
||||
}
|
||||
}
|
||||
|
||||
function HardwareInfoContent() {
|
||||
const { data, isLoading } = useSWR<HardwareData>("/api/hardware-info", hardwareInfoFetcher, {
|
||||
const { data, isLoading, isValidating } = useSWR<HardwareData>("/api/hardware-info", hardwareInfoFetcher, {
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
dedupingInterval: 5 * 60 * 1000, // 5分钟内相同请求去重
|
||||
@@ -149,6 +164,7 @@ function HardwareInfoContent() {
|
||||
const gpuInfo = data?.gpuInfo
|
||||
const memoryInfo = data?.memoryInfo || []
|
||||
const monitorInfo = data?.monitorInfo || []
|
||||
const motherboardInfo = data?.motherboardInfo
|
||||
|
||||
const formatBytes = (bytes?: number) => {
|
||||
if (!bytes) return "未知"
|
||||
@@ -227,8 +243,8 @@ function HardwareInfoContent() {
|
||||
})()
|
||||
: null
|
||||
|
||||
// 如果正在加载,显示 Skeleton 骨架屏
|
||||
if (isLoading) {
|
||||
// 如果正在加载或正在验证(包括刷新),显示 Skeleton 骨架屏
|
||||
if (isLoading || isValidating) {
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
{/* 系统信息 Skeleton */}
|
||||
@@ -324,6 +340,7 @@ function HardwareInfoContent() {
|
||||
{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)}
|
||||
@@ -336,23 +353,34 @@ function HardwareInfoContent() {
|
||||
)}
|
||||
</Chip>
|
||||
)}
|
||||
{allSysData.total_memory !== undefined && allSysData.used_memory !== undefined && (
|
||||
<Chip>
|
||||
<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
|
||||
</Chip>
|
||||
)}
|
||||
</div>
|
||||
{/* 每个内存条的详细信息 */}
|
||||
{memoryInfo.length > 0 && (
|
||||
<div className="flex flex-col gap-1.5">
|
||||
{memoryInfo.map((mem, index) => (
|
||||
<div key={index} className="flex flex-wrap gap-2">
|
||||
{mem.capacity && (
|
||||
<Chip>
|
||||
<span className="font-medium">容量:</span> {formatBytes(mem.capacity)}
|
||||
</Chip>
|
||||
)}
|
||||
{mem.manufacturer && (
|
||||
<Chip>
|
||||
<span className="font-medium">制造商:</span> {mem.manufacturer}
|
||||
</Chip>
|
||||
)}
|
||||
{mem.speed && (
|
||||
<Chip>
|
||||
<span className="font-medium">频率:</span> {mem.speed} MHz
|
||||
{mem.default_speed && (
|
||||
<span className="ml-1">({mem.default_speed} MHz)</span>
|
||||
)}
|
||||
</Chip>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -410,13 +438,28 @@ function HardwareInfoContent() {
|
||||
)}
|
||||
|
||||
{/* 主板信息 */}
|
||||
{(computerInfo.CsManufacturer || computerInfo.BiosSMBIOSBIOSVersion) && (
|
||||
{(motherboardInfo?.model ||
|
||||
motherboardInfo?.manufacturer ||
|
||||
motherboardInfo?.version ||
|
||||
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 && (
|
||||
{motherboardInfo?.model && (
|
||||
<Chip>
|
||||
<span className="font-medium">制造商:</span> {computerInfo.CsManufacturer}
|
||||
<span className="font-medium">型号:</span> {motherboardInfo.model}
|
||||
</Chip>
|
||||
)}
|
||||
{motherboardInfo?.manufacturer && (
|
||||
<Chip>
|
||||
<span className="font-medium">品牌:</span> {motherboardInfo.manufacturer}
|
||||
</Chip>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{motherboardInfo?.version && (
|
||||
<Chip>
|
||||
<span className="font-medium">版本:</span> {motherboardInfo.version}
|
||||
</Chip>
|
||||
)}
|
||||
{computerInfo.BiosSMBIOSBIOSVersion && (
|
||||
|
||||
@@ -151,6 +151,58 @@ export function FpsTest() {
|
||||
}
|
||||
}, [tool.state.autoCloseGame, batchTestProgress])
|
||||
|
||||
// 生成硬件信息对象的辅助函数
|
||||
const getHardwareInfoObject = useCallback(() => {
|
||||
if (!hardwareInfo?.systemInfo) {
|
||||
return null
|
||||
}
|
||||
|
||||
const osVersion = hardwareInfo.systemInfo.os_version || null
|
||||
const osDisplayVersion = hardwareInfo.computerInfo?.OSDisplayVersion || null
|
||||
let osStr = hardwareInfo.systemInfo.name || null
|
||||
if (osStr) {
|
||||
if (osVersion) {
|
||||
osStr += ` ${osVersion}`
|
||||
}
|
||||
if (osDisplayVersion) {
|
||||
osStr += ` ${osDisplayVersion}`
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
cpu: hardwareInfo.systemInfo.cpus[0]?.brand || null,
|
||||
cpuCount: hardwareInfo.systemInfo.cpu_count || null,
|
||||
os: osStr,
|
||||
memory: hardwareInfo.systemInfo.total_memory
|
||||
? Math.round(hardwareInfo.systemInfo.total_memory / 1024 / 1024 / 1024)
|
||||
: null,
|
||||
memoryManufacturer: hardwareInfo.memoryInfo && hardwareInfo.memoryInfo.length > 0
|
||||
? hardwareInfo.memoryInfo[0].manufacturer || null
|
||||
: null,
|
||||
memorySpeed: hardwareInfo.memoryInfo && hardwareInfo.memoryInfo.length > 0
|
||||
? hardwareInfo.memoryInfo[0].speed || null
|
||||
: null,
|
||||
memoryDefaultSpeed: hardwareInfo.memoryInfo && hardwareInfo.memoryInfo.length > 0
|
||||
? hardwareInfo.memoryInfo[0].default_speed || null
|
||||
: null,
|
||||
gpu: hardwareInfo.gpuInfo
|
||||
? hardwareInfo.gpuInfo.model
|
||||
: null,
|
||||
monitor: hardwareInfo.monitorInfo && hardwareInfo.monitorInfo.length > 0
|
||||
? hardwareInfo.monitorInfo[0].name || null
|
||||
: null,
|
||||
monitorManufacturer: hardwareInfo.monitorInfo && hardwareInfo.monitorInfo.length > 0
|
||||
? hardwareInfo.monitorInfo[0].manufacturer || null
|
||||
: null,
|
||||
monitorModel: hardwareInfo.monitorInfo && hardwareInfo.monitorInfo.length > 0
|
||||
? hardwareInfo.monitorInfo[0].model || null
|
||||
: null,
|
||||
motherboardModel: hardwareInfo.motherboardInfo?.model || null,
|
||||
motherboardVersion: hardwareInfo.motherboardInfo?.version || null,
|
||||
biosVersion: hardwareInfo.computerInfo?.BiosSMBIOSBIOSVersion || null,
|
||||
}
|
||||
}, [hardwareInfo])
|
||||
|
||||
// 读取结果函数
|
||||
const readResult = useCallback(
|
||||
async (silent = false): Promise<boolean> => {
|
||||
@@ -285,35 +337,7 @@ export function FpsTest() {
|
||||
p1,
|
||||
rawResult: parsed.data,
|
||||
videoSetting: currentVideoSetting,
|
||||
hardwareInfo: hardwareInfo?.systemInfo
|
||||
? {
|
||||
cpu: hardwareInfo.systemInfo.cpus[0]?.brand || null,
|
||||
cpuCount: hardwareInfo.systemInfo.cpu_count || null,
|
||||
os: (() => {
|
||||
const osVersion = hardwareInfo.systemInfo.os_version || null
|
||||
// 使用 OSDisplayVersion 作为版本代码(如 "25H2")
|
||||
const osDisplayVersion = hardwareInfo.computerInfo?.OSDisplayVersion || null
|
||||
|
||||
let osStr = hardwareInfo.systemInfo.name || null
|
||||
if (!osStr) return null
|
||||
|
||||
if (osVersion) {
|
||||
osStr += ` ${osVersion}`
|
||||
}
|
||||
if (osDisplayVersion) {
|
||||
osStr += ` ${osDisplayVersion}`
|
||||
}
|
||||
return osStr
|
||||
})(),
|
||||
memory: hardwareInfo.systemInfo.total_memory
|
||||
? Math.round(hardwareInfo.systemInfo.total_memory / 1024 / 1024 / 1024)
|
||||
: null,
|
||||
gpu: hardwareInfo.gpuInfo
|
||||
? hardwareInfo.gpuInfo.model
|
||||
: null,
|
||||
monitor: null,
|
||||
}
|
||||
: null,
|
||||
hardwareInfo: getHardwareInfoObject(),
|
||||
note: batchNote,
|
||||
})
|
||||
|
||||
@@ -339,35 +363,7 @@ export function FpsTest() {
|
||||
p1,
|
||||
rawResult: parsed.data,
|
||||
videoSetting: currentVideoSetting,
|
||||
hardwareInfo: hardwareInfo?.systemInfo
|
||||
? {
|
||||
cpu: hardwareInfo.systemInfo.cpus[0]?.brand || null,
|
||||
cpuCount: hardwareInfo.systemInfo.cpu_count || null,
|
||||
os: (() => {
|
||||
const osVersion = hardwareInfo.systemInfo.os_version || null
|
||||
// 使用 OSDisplayVersion 作为版本代码(如 "25H2")
|
||||
const osDisplayVersion = hardwareInfo.computerInfo?.OSDisplayVersion || null
|
||||
|
||||
let osStr = hardwareInfo.systemInfo.name || null
|
||||
if (!osStr) return null
|
||||
|
||||
if (osVersion) {
|
||||
osStr += ` ${osVersion}`
|
||||
}
|
||||
if (osDisplayVersion) {
|
||||
osStr += ` ${osDisplayVersion}`
|
||||
}
|
||||
return osStr
|
||||
})(),
|
||||
memory: hardwareInfo.systemInfo.total_memory
|
||||
? Math.round(hardwareInfo.systemInfo.total_memory / 1024 / 1024 / 1024)
|
||||
: null,
|
||||
gpu: hardwareInfo.gpuInfo
|
||||
? hardwareInfo.gpuInfo.model
|
||||
: null,
|
||||
monitor: null,
|
||||
}
|
||||
: null,
|
||||
hardwareInfo: getHardwareInfoObject(),
|
||||
note: singleNote, // 保存备注(包含分辨率信息)
|
||||
})
|
||||
}
|
||||
@@ -818,23 +814,7 @@ export function FpsTest() {
|
||||
1
|
||||
)}\nP1低帧: ${avgP1.toFixed(1)}`,
|
||||
videoSetting: tool.store.state.videoSetting,
|
||||
hardwareInfo: hardwareInfo?.systemInfo
|
||||
? {
|
||||
cpu: hardwareInfo.systemInfo.cpus[0]?.brand || null,
|
||||
cpuCount: hardwareInfo.systemInfo.cpu_count || null,
|
||||
os:
|
||||
hardwareInfo.systemInfo.name && hardwareInfo.systemInfo.os_version
|
||||
? `${hardwareInfo.systemInfo.name} ${hardwareInfo.systemInfo.os_version}`
|
||||
: null,
|
||||
memory: hardwareInfo.systemInfo.total_memory
|
||||
? Math.round(hardwareInfo.systemInfo.total_memory / 1024 / 1024 / 1024)
|
||||
: null,
|
||||
gpu: hardwareInfo.gpuInfo
|
||||
? `${hardwareInfo.gpuInfo.vendor} ${hardwareInfo.gpuInfo.model}`
|
||||
: null,
|
||||
monitor: null,
|
||||
}
|
||||
: null,
|
||||
hardwareInfo: getHardwareInfoObject(),
|
||||
note: averageNote,
|
||||
})
|
||||
|
||||
@@ -944,23 +924,7 @@ export function FpsTest() {
|
||||
1
|
||||
)}\nP1低帧: ${avgP1.toFixed(1)}`,
|
||||
videoSetting: tool.store.state.videoSetting,
|
||||
hardwareInfo: hardwareInfo?.systemInfo
|
||||
? {
|
||||
cpu: hardwareInfo.systemInfo.cpus[0]?.brand || null,
|
||||
cpuCount: hardwareInfo.systemInfo.cpu_count || null,
|
||||
os:
|
||||
hardwareInfo.systemInfo.name && hardwareInfo.systemInfo.os_version
|
||||
? `${hardwareInfo.systemInfo.name} ${hardwareInfo.systemInfo.os_version}`
|
||||
: null,
|
||||
memory: hardwareInfo.systemInfo.total_memory
|
||||
? Math.round(hardwareInfo.systemInfo.total_memory / 1024 / 1024 / 1024)
|
||||
: null,
|
||||
gpu: hardwareInfo.gpuInfo
|
||||
? `${hardwareInfo.gpuInfo.vendor} ${hardwareInfo.gpuInfo.model}`
|
||||
: null,
|
||||
monitor: null,
|
||||
}
|
||||
: null,
|
||||
hardwareInfo: getHardwareInfoObject(),
|
||||
note: averageNote,
|
||||
})
|
||||
|
||||
|
||||
@@ -41,16 +41,20 @@ export function TestResultsTable({
|
||||
>
|
||||
<TableHeader>
|
||||
<TableColumn minWidth={140}>测试时间</TableColumn>
|
||||
<TableColumn width={80}>测试地图</TableColumn>
|
||||
<TableColumn width={80}>平均帧</TableColumn>
|
||||
<TableColumn width={80}>P1低帧</TableColumn>
|
||||
<TableColumn width={40}>地图</TableColumn>
|
||||
<TableColumn width={60}>平均帧</TableColumn>
|
||||
<TableColumn width={60}>P1低帧</TableColumn>
|
||||
<TableColumn width={100}>CPU</TableColumn>
|
||||
<TableColumn minWidth={80}>系统版本</TableColumn>
|
||||
<TableColumn minWidth={100}>GPU</TableColumn>
|
||||
<TableColumn width={80}>内存</TableColumn>
|
||||
<TableColumn width={80}>内存频率</TableColumn>
|
||||
<TableColumn width={100}>主板型号</TableColumn>
|
||||
<TableColumn minWidth={80}>主板版本</TableColumn>
|
||||
<TableColumn minWidth={80}>BIOS版本</TableColumn>
|
||||
<TableColumn width={120}>视频设置</TableColumn>
|
||||
<TableColumn minWidth={100}>备注</TableColumn>
|
||||
<TableColumn width={80} align="center">
|
||||
<TableColumn minWidth={40}>备注</TableColumn>
|
||||
<TableColumn width={60} align="center">
|
||||
操作
|
||||
</TableColumn>
|
||||
</TableHeader>
|
||||
@@ -69,10 +73,10 @@ export function TestResultsTable({
|
||||
<TableCell className="text-xs">
|
||||
{result.p1 !== null ? `${result.p1.toFixed(1)}` : "N/A"}
|
||||
</TableCell>
|
||||
<TableCell className="text-xs max-w-[175px]">
|
||||
<TableCell className="text-xs max-w-[170px]">
|
||||
<Tooltip
|
||||
content={result.hardwareInfo?.cpu || "N/A"}
|
||||
delay={500}
|
||||
delay={300}
|
||||
placement="top"
|
||||
>
|
||||
<div className="truncate cursor-help">
|
||||
@@ -91,6 +95,28 @@ export function TestResultsTable({
|
||||
? `${result.hardwareInfo.memory}GB`
|
||||
: "N/A"}
|
||||
</TableCell>
|
||||
<TableCell className="text-xs whitespace-nowrap">
|
||||
{result.hardwareInfo?.memorySpeed
|
||||
? `${result.hardwareInfo.memorySpeed}MHz`
|
||||
: "N/A"}
|
||||
</TableCell>
|
||||
<TableCell className="text-xs">
|
||||
<Tooltip
|
||||
content={result.hardwareInfo?.motherboardModel || "N/A"}
|
||||
delay={500}
|
||||
placement="top"
|
||||
>
|
||||
<div className="truncate cursor-help">
|
||||
{result.hardwareInfo?.motherboardModel || "N/A"}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell className="text-xs">
|
||||
<div className="truncate">{result.hardwareInfo?.motherboardVersion || "N/A"}</div>
|
||||
</TableCell>
|
||||
<TableCell className="text-xs">
|
||||
<div className="truncate">{result.hardwareInfo?.biosVersion || "N/A"}</div>
|
||||
</TableCell>
|
||||
<TableCell className="text-xs">
|
||||
<Tooltip content={formatVideoSettingSummary(result.videoSetting)}>
|
||||
<span className="truncate cursor-help">
|
||||
@@ -100,7 +126,7 @@ export function TestResultsTable({
|
||||
</span>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell className="text-xs">
|
||||
<TableCell className="text-xs min-w-fit">
|
||||
<NoteCell
|
||||
note={result.note || ""}
|
||||
onEdit={() => onEditNote(result.id, result.note || "")}
|
||||
|
||||
@@ -22,10 +22,35 @@ interface ComputerInfo {
|
||||
ReleaseId?: string
|
||||
}
|
||||
|
||||
interface MemoryInfo {
|
||||
capacity?: number // 容量(字节)
|
||||
manufacturer?: string
|
||||
speed?: number // MHz,实际频率 ConfiguredClockSpeed
|
||||
default_speed?: number // MHz,默认频率 Speed(如果存在)
|
||||
}
|
||||
|
||||
interface MonitorInfo {
|
||||
manufacturer?: string
|
||||
model?: string
|
||||
name?: string
|
||||
refresh_rate?: number // Hz
|
||||
resolution_width?: number
|
||||
resolution_height?: number
|
||||
}
|
||||
|
||||
interface MotherboardInfo {
|
||||
manufacturer?: string // 制造商
|
||||
model?: string // 型号
|
||||
version?: string
|
||||
}
|
||||
|
||||
export interface HardwareInfoWithGpu {
|
||||
systemInfo: AllSystemInfo | null
|
||||
gpuInfo: GpuInfo | null
|
||||
computerInfo: ComputerInfo | null
|
||||
memoryInfo: MemoryInfo[]
|
||||
monitorInfo: MonitorInfo[]
|
||||
motherboardInfo: MotherboardInfo | null
|
||||
}
|
||||
|
||||
export function useHardwareInfo() {
|
||||
@@ -34,7 +59,7 @@ export function useHardwareInfo() {
|
||||
useEffect(() => {
|
||||
const fetchHardwareInfo = async () => {
|
||||
try {
|
||||
const [sys, gpuInfo, computerInfo] = await Promise.all([
|
||||
const [sys, gpuInfo, computerInfo, memoryInfo, monitorInfo, motherboardInfo] = await Promise.all([
|
||||
allSysInfo(),
|
||||
invoke<GpuInfo | null>("get_gpu_info").catch((error) => {
|
||||
console.error("获取 GPU 信息失败:", error)
|
||||
@@ -43,12 +68,27 @@ export function useHardwareInfo() {
|
||||
invoke<ComputerInfo>("get_computer_info").catch((error) => {
|
||||
console.error("获取 PowerShell 信息失败:", error)
|
||||
return {} as ComputerInfo
|
||||
}),
|
||||
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[]
|
||||
}),
|
||||
invoke<MotherboardInfo>("get_motherboard_info").catch((error) => {
|
||||
console.error("获取主板信息失败:", error)
|
||||
return { manufacturer: undefined, model: undefined, version: undefined } as MotherboardInfo
|
||||
})
|
||||
])
|
||||
setHardwareInfo({
|
||||
systemInfo: sys,
|
||||
gpuInfo,
|
||||
computerInfo
|
||||
computerInfo,
|
||||
memoryInfo,
|
||||
monitorInfo,
|
||||
motherboardInfo
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("获取硬件信息失败:", error)
|
||||
|
||||
@@ -38,6 +38,10 @@ export async function handleExportCSV(
|
||||
"系统版本",
|
||||
"GPU",
|
||||
"内存(GB)",
|
||||
"内存频率(MHz)",
|
||||
"主板型号",
|
||||
"主板版本",
|
||||
"BIOS版本",
|
||||
"分辨率",
|
||||
"视频设置",
|
||||
"备注",
|
||||
@@ -55,6 +59,10 @@ export async function handleExportCSV(
|
||||
`"${result.hardwareInfo?.os || "N/A"}"`,
|
||||
`"${result.hardwareInfo?.gpu || "N/A"}"`,
|
||||
result.hardwareInfo?.memory ? result.hardwareInfo.memory.toString() : "N/A",
|
||||
result.hardwareInfo?.memorySpeed ? result.hardwareInfo.memorySpeed.toString() : "N/A",
|
||||
`"${result.hardwareInfo?.motherboardModel || "N/A"}"`,
|
||||
`"${result.hardwareInfo?.motherboardVersion || "N/A"}"`,
|
||||
`"${result.hardwareInfo?.biosVersion || "N/A"}"`,
|
||||
result.videoSetting
|
||||
? `${result.videoSetting.defaultres}x${result.videoSetting.defaultresheight}`
|
||||
: "N/A",
|
||||
@@ -118,6 +126,10 @@ export async function handleExportAverageCSV(
|
||||
"系统版本",
|
||||
"GPU",
|
||||
"内存(GB)",
|
||||
"内存频率(MHz)",
|
||||
"主板型号",
|
||||
"主板版本",
|
||||
"BIOS版本",
|
||||
"分辨率",
|
||||
"视频设置",
|
||||
"备注",
|
||||
@@ -135,6 +147,10 @@ export async function handleExportAverageCSV(
|
||||
`"${result.hardwareInfo?.os || "N/A"}"`,
|
||||
`"${result.hardwareInfo?.gpu || "N/A"}"`,
|
||||
result.hardwareInfo?.memory ? result.hardwareInfo.memory.toString() : "N/A",
|
||||
result.hardwareInfo?.memorySpeed ? result.hardwareInfo.memorySpeed.toString() : "N/A",
|
||||
`"${result.hardwareInfo?.motherboardModel || "N/A"}"`,
|
||||
`"${result.hardwareInfo?.motherboardVersion || "N/A"}"`,
|
||||
`"${result.hardwareInfo?.biosVersion || "N/A"}"`,
|
||||
result.videoSetting
|
||||
? `${result.videoSetting.defaultres}x${result.videoSetting.defaultresheight}`
|
||||
: "N/A",
|
||||
|
||||
@@ -19,8 +19,16 @@ export interface FpsTestResult {
|
||||
cpuCount: number | null
|
||||
os: string | null
|
||||
memory: number | null // GB
|
||||
memoryManufacturer: string | null
|
||||
memorySpeed: number | null // MHz,实际频率 ConfiguredClockSpeed
|
||||
memoryDefaultSpeed: number | null // MHz,默认频率 Speed(如果存在)
|
||||
gpu: string | null
|
||||
monitor: string | null
|
||||
monitorManufacturer: string | null
|
||||
monitorModel: string | null
|
||||
motherboardModel: string | null // 合并后的制造商和型号
|
||||
motherboardVersion: string | null
|
||||
biosVersion: string | null
|
||||
} | null // 硬件信息
|
||||
note?: string // 备注(可选,用于向后兼容)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user