diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 6b8eca4..0017b2c 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,12 +7,14 @@ use serde::Serialize; use sysinfo::{System, Disks}; use wmi::{COMLibrary, WMIConnection}; -use std::{fs, ops::Deref}; +use std::fs; use std::path::Path; use tauri::Emitter; use chrono::{Duration, FixedOffset, Local, NaiveDate, TimeZone, DateTime}; // 只引入 minidump 基础库 use minidump::{Minidump, MinidumpException, MinidumpSystemInfo}; +// [新增] 引入 Deref 用于泛型约束 +use std::ops::Deref; // --- 1. 数据结构 (保持不变) --- @@ -188,8 +190,10 @@ fn translate_bugcheck_u32(code: u32) -> (String, String) { } } -// [新增] 抽离公共分析逻辑 -fn analyze_dump_data>(dump: Minidump) -> Result { +// [修复] 抽离公共分析逻辑,使用泛型 T 适配不同数据源 +fn analyze_dump_data(dump: Minidump) -> Result +where T: Deref +{ let exception_stream = dump.get_stream::() .map_err(|_| "无法找到异常信息流 (No Exception Stream),可能是非标准 Dump 文件。".to_string())?; diff --git a/src/App.vue b/src/App.vue index c0d9c37..9c47746 100644 --- a/src/App.vue +++ b/src/App.vue @@ -165,8 +165,8 @@ - - + +
@@ -178,10 +178,10 @@ v-for="file in bsodList" :key="file.path" class="file-item" - :class="{ active: selectedBsod?.path === file.path }" + :class="{ active: selectedBsod?.path === file.path, imported: !!file.fileRef }" @click="analyzeBsod(file)" > -
📄
+
{{ file.fileRef ? '📨' : '📄' }}
{{ file.filename }}
{{ file.created_time }} · {{ file.size_kb }}KB
@@ -320,38 +320,58 @@ async function startScan() { // --- BSOD 功能 --- async function loadMinidumps() { bsodLoading.value = true; try { bsodList.value = await invoke('list_minidumps'); } catch (e) { triggerToast('加载失败', e, 'error'); } finally { bsodLoading.value = false; } } -async function analyzeBsod(file) { if (bsodAnalyzing.value) return; selectedBsod.value = file; bsodResult.value = null; bsodAnalyzing.value = true; try { bsodResult.value = await invoke('analyze_minidump', { filepath: file.path }); } catch (e) { triggerToast('分析失败', e, 'error'); } finally { bsodAnalyzing.value = false; } } - -// [新增] BSOD 导入功能 -function triggerBsodImport() { bsodFileInput.value.click(); } -function handleBsodFileImport(event) { - const file = event.target.files[0]; - if (!file) return; - - // 更新 UI 状态,模拟选中了一个“外部文件” - selectedBsod.value = { path: 'external', filename: file.name, created_time: 'Imported', size_kb: Math.round(file.size / 1024) }; +async function analyzeBsod(file) { + if (bsodAnalyzing.value) return; + selectedBsod.value = file; bsodResult.value = null; bsodAnalyzing.value = true; - const reader = new FileReader(); - reader.onload = async (e) => { - try { - // 将 ArrayBuffer 转换为 Uint8Array (Rust Vec) - const arrayBuffer = e.target.result; + try { + // 判断是导入的文件还是本地文件 + if (file.fileRef) { + // 导入的文件:读取 ArrayBuffer 并传给后端 + const arrayBuffer = await file.fileRef.arrayBuffer(); const bytes = new Uint8Array(arrayBuffer); - const byteArray = Array.from(bytes); // 转换为普通数组以便序列化传输 - - const result = await invoke('analyze_minidump_bytes', { fileContent: byteArray }); - bsodResult.value = result; - triggerToast('分析成功', '已完成外部文件解析', 'success'); - } catch (err) { - triggerToast('分析失败', err, 'error'); - } finally { - bsodAnalyzing.value = false; + const byteArray = Array.from(bytes); + bsodResult.value = await invoke('analyze_minidump_bytes', { fileContent: byteArray }); + } else { + // 本地文件:传路径 + bsodResult.value = await invoke('analyze_minidump', { filepath: file.path }); } - }; - reader.readAsArrayBuffer(file); - event.target.value = ''; + } catch (e) { + triggerToast('分析失败', e, 'error'); + } finally { + bsodAnalyzing.value = false; + } +} + +// [新增] BSOD 导入功能 (支持多选) +function triggerBsodImport() { bsodFileInput.value.click(); } +function handleBsodFileImport(event) { + const files = event.target.files; + if (!files || files.length === 0) return; + + let importedCount = 0; + // 倒序遍历,这样添加到数组头部后顺序是正确的 + for (let i = files.length - 1; i >= 0; i--) { + const file = files[i]; + // 构造一个符合列表格式的虚拟对象 + const newItem = { + filename: file.name, + // 使用一个特殊的 path 标识,防止 key 冲突 + path: `imported-${file.name}-${Date.now()}-${i}`, + size_kb: Math.round(file.size / 1024), + // 使用浏览器读取到的修改时间 + created_time: new Date(file.lastModified).toLocaleString(), + // 关键:保存文件引用,以便点击时读取 + fileRef: file + }; + bsodList.value.unshift(newItem); + importedCount++; + } + + triggerToast('导入成功', `已添加 ${importedCount} 个文件到列表`, 'success'); + event.target.value = ''; // 重置,允许再次导入同名文件 } watch(currentTab, (newVal) => { if (newVal === 'bsod' && bsodList.value.length === 0) loadMinidumps(); }); @@ -443,7 +463,11 @@ body { margin: 0; padding: 0; background-color: #f4f6f9; overflow: hidden; } .bsod-list-panel { background: white; border-radius: 10px; border: 1px solid #eaecf0; overflow-y: auto; } .bsod-detail-panel { background: white; border-radius: 10px; border: 1px solid #eaecf0; padding: 25px; overflow-y: auto; position: relative; } .file-item { padding: 15px; border-bottom: 1px solid #f0f2f5; cursor: pointer; display: flex; gap: 12px; transition: background 0.2s; } -.file-item:hover { background: #f8f9fa; } .file-item.active { background: #eafaf1; border-left: 4px solid #2ecc71; } +.file-item:hover { background: #f8f9fa; } +.file-item.active { background: #eafaf1; border-left: 4px solid #2ecc71; } +/* 给导入的文件加一点特殊样式区分(可选) */ +.file-item.imported .file-name { color: #3498db; } + .file-icon { font-size: 1.5rem; } .file-name { font-weight: 600; font-size: 0.9rem; color: #2c3e50; margin-bottom: 4px; } .file-meta { font-size: 0.8rem; color: #95a5a6; } .empty-state { padding: 40px; text-align: center; color: #95a5a6; font-size: 0.9rem; line-height: 1.6; } .analyzing-state { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #7f8c8d; }