show scan progress

This commit is contained in:
Julian Freeman
2026-03-03 16:27:43 -04:00
parent 8efbb3ebc4
commit 39e2f41aab
3 changed files with 50 additions and 6 deletions

View File

@@ -98,13 +98,32 @@ pub async fn disable_hibernation() -> Result<String, String> {
// --- 原有逻辑保持 (磁盘树等) --- // --- 原有逻辑保持 (磁盘树等) ---
pub async fn run_full_scan(root_path: String, state: &DiskState) { #[derive(Serialize, Clone)]
pub struct ScanProgress {
pub file_count: u64,
pub current_path: String,
}
pub async fn run_full_scan(root_path: String, state: &DiskState, app_handle: tauri::AppHandle) {
use jwalk::WalkDir; use jwalk::WalkDir;
use tauri::Emitter;
let mut dir_sizes = HashMap::new(); let mut dir_sizes = HashMap::new();
let root = Path::new(&root_path); let root = Path::new(&root_path);
let mut file_count = 0;
for entry in WalkDir::new(root).skip_hidden(false).into_iter().filter_map(|e| e.ok()) { for entry in WalkDir::new(root).skip_hidden(false).into_iter().filter_map(|e| e.ok()) {
if entry.file_type.is_file() { if entry.file_type.is_file() {
file_count += 1;
// 节流推送进度:每 2000 个文件推送一次,避免 IPC 过载
if file_count % 2000 == 0 {
let _ = app_handle.emit("scan-progress", ScanProgress {
file_count,
current_path: entry.parent_path().to_string_lossy().to_string(),
});
}
let size = entry.metadata().map(|m| m.len()).unwrap_or(0); let size = entry.metadata().map(|m| m.len()).unwrap_or(0);
let mut current_path = entry.parent_path().to_path_buf(); let mut current_path = entry.parent_path().to_path_buf();
while current_path.starts_with(root) { while current_path.starts_with(root) {

View File

@@ -14,8 +14,8 @@ async fn start_fast_clean(selected_paths: Vec<String>) -> Result<cleaner::CleanR
} }
#[tauri::command] #[tauri::command]
async fn start_full_disk_scan(state: State<'_, cleaner::DiskState>) -> Result<(), String> { async fn start_full_disk_scan(state: State<'_, cleaner::DiskState>, app_handle: tauri::AppHandle) -> Result<(), String> {
cleaner::run_full_scan("C:\\".to_string(), &state).await; cleaner::run_full_scan("C:\\".to_string(), &state, app_handle).await;
Ok(()) Ok(())
} }

View File

@@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref, onUnmounted } from "vue";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import { openUrl } from "@tauri-apps/plugin-opener"; import { openUrl } from "@tauri-apps/plugin-opener";
import pkg from "../package.json"; import pkg from "../package.json";
@@ -25,6 +26,7 @@ const isCleaning = ref(false);
const isCleanDone = ref(false); const isCleanDone = ref(false);
const isFullScanning = ref(false); const isFullScanning = ref(false);
const scanProgress = ref(0); const scanProgress = ref(0);
const fullScanProgress = ref({ fileCount: 0, currentPath: "" });
const fastScanResult = ref<FastScanResult | null>(null); const fastScanResult = ref<FastScanResult | null>(null);
const cleanResult = ref<CleanResult | null>(null); const cleanResult = ref<CleanResult | null>(null);
const treeData = ref<FileNode[]>([]); const treeData = ref<FileNode[]>([]);
@@ -174,6 +176,14 @@ async function runAdvancedTask(task: string) {
async function startFullDiskScan() { async function startFullDiskScan() {
isFullScanning.value = true; isFullScanning.value = true;
treeData.value = []; treeData.value = [];
fullScanProgress.value = { fileCount: 0, currentPath: "" };
// 监听进度
const unlisten = await listen<{file_count: number, current_path: string}>("scan-progress", (event) => {
fullScanProgress.value.fileCount = event.payload.file_count;
fullScanProgress.value.currentPath = event.payload.current_path;
});
try { try {
await invoke("start_full_disk_scan"); await invoke("start_full_disk_scan");
const rootChildren = await invoke<FileNode[]>("get_tree_children", { path: "C:\\" }); const rootChildren = await invoke<FileNode[]>("get_tree_children", { path: "C:\\" });
@@ -182,6 +192,7 @@ async function startFullDiskScan() {
alert("扫描失败,请确保以管理员身份运行。"); alert("扫描失败,请确保以管理员身份运行。");
} finally { } finally {
isFullScanning.value = false; isFullScanning.value = false;
unlisten();
} }
} }
@@ -502,7 +513,7 @@ function splitSize(sizeStr: string | number) {
</div> </div>
<div class="header-actions"> <div class="header-actions">
<button class="btn-primary btn-sm" @click="startFullDiskScan" :disabled="isFullScanning"> <button class="btn-primary btn-sm" @click="startFullDiskScan" :disabled="isFullScanning">
{{ isFullScanning ? '正在扫描...' : '开始深度分析' }} {{ isFullScanning ? '正在扫描...' : '开始扫描' }}
</button> </button>
</div> </div>
</div> </div>
@@ -510,7 +521,15 @@ function splitSize(sizeStr: string | number) {
<div class="tree-table-container shadow-card" v-if="treeData.length > 0 || isFullScanning"> <div class="tree-table-container shadow-card" v-if="treeData.length > 0 || isFullScanning">
<div v-if="isFullScanning" class="scanning-overlay"> <div v-if="isFullScanning" class="scanning-overlay">
<div class="spinner"></div> <div class="spinner"></div>
<p>正在分析数百万个文件请稍候...</p> <div class="scanning-status">
<p class="scanning-main-text">正在扫描全盘文件...</p>
<div class="scanning-stats-row">
<span class="stat-badge">已扫描{{ fullScanProgress.fileCount.toLocaleString() }} 个文件</span>
</div>
<p class="scanning-current-path" v-if="fullScanProgress.currentPath">
当前{{ fullScanProgress.currentPath }}
</p>
</div>
</div> </div>
<div v-else class="tree-content-wrapper"> <div v-else class="tree-content-wrapper">
@@ -1013,6 +1032,12 @@ body {
/* --- 通用状态 --- */ /* --- 通用状态 --- */
.scanning-loader, .scanning-overlay { padding: 100px 40px; text-align: center; color: var(--text-sec); } .scanning-loader, .scanning-overlay { padding: 100px 40px; text-align: center; color: var(--text-sec); }
.scanning-status { margin-top: 16px; }
.scanning-main-text { font-size: 16px; font-weight: 600; color: var(--text-main); margin-bottom: 12px; }
.scanning-stats-row { margin-bottom: 16px; }
.stat-badge { background: #EBF4FF; color: var(--primary-color); padding: 6px 16px; border-radius: 20px; font-size: 13px; font-weight: 700; }
.scanning-current-path { font-size: 12px; color: var(--text-sec); max-width: 500px; margin: 0 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; opacity: 0.8; }
.spinner { .spinner {
width: 44px; height: 44px; width: 44px; height: 44px;
border: 3px solid #F2F2F7; border: 3px solid #F2F2F7;