support clean orogress
This commit is contained in:
@@ -2,8 +2,11 @@ use std::fs;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use crate::backend::fast_clean::clean_directory_contents;
|
use crate::backend::fast_clean::clean_directory_contents;
|
||||||
use crate::backend::models::{BrowserProfile, BrowserScanResult, BrowserType, CleanResult};
|
use crate::backend::models::{
|
||||||
|
BrowserProfile, BrowserScanResult, BrowserType, CleanResult, ProjectCleanProgress,
|
||||||
|
};
|
||||||
use crate::backend::utils::{format_size, get_dir_size_simple};
|
use crate::backend::utils::{format_size, get_dir_size_simple};
|
||||||
|
use tauri::Emitter;
|
||||||
|
|
||||||
const BROWSER_CACHE_DIRS: &[&str] = &[
|
const BROWSER_CACHE_DIRS: &[&str] = &[
|
||||||
"Cache",
|
"Cache",
|
||||||
@@ -80,18 +83,23 @@ pub async fn run_browser_scan(browser: BrowserType) -> Result<BrowserScanResult,
|
|||||||
pub async fn run_browser_clean(
|
pub async fn run_browser_clean(
|
||||||
browser: BrowserType,
|
browser: BrowserType,
|
||||||
profile_paths: Vec<String>,
|
profile_paths: Vec<String>,
|
||||||
|
app_handle: tauri::AppHandle,
|
||||||
) -> Result<CleanResult, String> {
|
) -> Result<CleanResult, String> {
|
||||||
let user_data_path = browser.get_user_data_path()?;
|
let user_data_path = browser.get_user_data_path()?;
|
||||||
let mut total_freed = 0;
|
let mut total_freed = 0;
|
||||||
let mut success_count = 0;
|
let mut success_count = 0;
|
||||||
let mut fail_count = 0;
|
let mut fail_count = 0;
|
||||||
|
let mut approx_completed_bytes = 0;
|
||||||
|
let total_items = profile_paths.len() as u32;
|
||||||
|
|
||||||
for profile_dir in profile_paths {
|
for (index, profile_dir) in profile_paths.into_iter().enumerate() {
|
||||||
let profile_path = user_data_path.join(&profile_dir);
|
let profile_path = user_data_path.join(&profile_dir);
|
||||||
|
let mut profile_estimated_size = 0;
|
||||||
if profile_path.exists() {
|
if profile_path.exists() {
|
||||||
for sub_dir in BROWSER_CACHE_DIRS {
|
for sub_dir in BROWSER_CACHE_DIRS {
|
||||||
let target = profile_path.join(sub_dir);
|
let target = profile_path.join(sub_dir);
|
||||||
if target.exists() {
|
if target.exists() {
|
||||||
|
profile_estimated_size += get_dir_size_simple(&target);
|
||||||
let (freed, success, fail) = clean_directory_contents(&target, None);
|
let (freed, success, fail) = clean_directory_contents(&target, None);
|
||||||
total_freed += freed;
|
total_freed += freed;
|
||||||
success_count += success;
|
success_count += success;
|
||||||
@@ -99,6 +107,17 @@ pub async fn run_browser_clean(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
approx_completed_bytes += profile_estimated_size;
|
||||||
|
let _ = app_handle.emit(
|
||||||
|
"browser-clean-progress",
|
||||||
|
ProjectCleanProgress {
|
||||||
|
completed_items: (index + 1) as u32,
|
||||||
|
total_items,
|
||||||
|
current_item: profile_dir,
|
||||||
|
approx_completed_bytes,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CleanResult {
|
Ok(CleanResult {
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ use std::fs;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use crate::backend::models::{CleanResult, CleaningConfig, FastScanResult, ScanItem};
|
use tauri::Emitter;
|
||||||
|
|
||||||
|
use crate::backend::models::{CleanResult, CleaningConfig, FastScanResult, ProjectCleanProgress, ScanItem};
|
||||||
use crate::backend::utils::format_size;
|
use crate::backend::utils::format_size;
|
||||||
|
|
||||||
fn get_fast_cleaning_configs() -> Vec<CleaningConfig> {
|
fn get_fast_cleaning_configs() -> Vec<CleaningConfig> {
|
||||||
@@ -89,22 +91,42 @@ fn get_dir_stats(path: &Path, filter_days: Option<u64>) -> (u64, u32) {
|
|||||||
(size, count)
|
(size, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_fast_clean(selected_paths: Vec<String>) -> Result<CleanResult, String> {
|
pub async fn run_fast_clean(
|
||||||
let configs = get_fast_cleaning_configs();
|
selected_paths: Vec<String>,
|
||||||
|
app_handle: tauri::AppHandle,
|
||||||
|
) -> Result<CleanResult, String> {
|
||||||
|
let selected_configs: Vec<CleaningConfig> = get_fast_cleaning_configs()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|config| selected_paths.contains(&config.path))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut success_count = 0;
|
let mut success_count = 0;
|
||||||
let mut fail_count = 0;
|
let mut fail_count = 0;
|
||||||
let mut total_freed = 0;
|
let mut total_freed = 0;
|
||||||
|
let mut approx_completed_bytes = 0;
|
||||||
|
let total_items = selected_configs.len() as u32;
|
||||||
|
|
||||||
for config in configs {
|
for (index, config) in selected_configs.into_iter().enumerate() {
|
||||||
if selected_paths.contains(&config.path) {
|
|
||||||
let path = Path::new(&config.path);
|
let path = Path::new(&config.path);
|
||||||
|
let item_size = get_dir_stats(path, config.filter_days).0;
|
||||||
|
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
let (freed, success, fail) = clean_directory_contents(path, config.filter_days);
|
let (freed, success, fail) = clean_directory_contents(path, config.filter_days);
|
||||||
total_freed += freed;
|
total_freed += freed;
|
||||||
success_count += success;
|
success_count += success;
|
||||||
fail_count += fail;
|
fail_count += fail;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
approx_completed_bytes += item_size;
|
||||||
|
let _ = app_handle.emit(
|
||||||
|
"fast-clean-progress",
|
||||||
|
ProjectCleanProgress {
|
||||||
|
completed_items: (index + 1) as u32,
|
||||||
|
total_items,
|
||||||
|
current_item: config.name,
|
||||||
|
approx_completed_bytes,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CleanResult {
|
Ok(CleanResult {
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ pub struct ScanProgress {
|
|||||||
pub current_path: String,
|
pub current_path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Clone)]
|
||||||
|
pub struct ProjectCleanProgress {
|
||||||
|
pub completed_items: u32,
|
||||||
|
pub total_items: u32,
|
||||||
|
pub current_item: String,
|
||||||
|
pub approx_completed_bytes: u64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CleaningConfig {
|
pub struct CleaningConfig {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|||||||
@@ -10,8 +10,11 @@ async fn start_fast_scan() -> backend::models::FastScanResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn start_fast_clean(selected_paths: Vec<String>) -> Result<backend::models::CleanResult, String> {
|
async fn start_fast_clean(
|
||||||
backend::fast_clean::run_fast_clean(selected_paths).await
|
selected_paths: Vec<String>,
|
||||||
|
app_handle: tauri::AppHandle,
|
||||||
|
) -> Result<backend::models::CleanResult, String> {
|
||||||
|
backend::fast_clean::run_fast_clean(selected_paths, app_handle).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -66,6 +69,7 @@ async fn start_browser_scan(browser: String) -> Result<backend::models::BrowserS
|
|||||||
async fn start_browser_clean(
|
async fn start_browser_clean(
|
||||||
browser: String,
|
browser: String,
|
||||||
profiles: Vec<String>,
|
profiles: Vec<String>,
|
||||||
|
app_handle: tauri::AppHandle,
|
||||||
) -> Result<backend::models::CleanResult, String> {
|
) -> Result<backend::models::CleanResult, String> {
|
||||||
let browser_type = if browser == "chrome" {
|
let browser_type = if browser == "chrome" {
|
||||||
backend::models::BrowserType::Chrome
|
backend::models::BrowserType::Chrome
|
||||||
@@ -73,7 +77,7 @@ async fn start_browser_clean(
|
|||||||
backend::models::BrowserType::Edge
|
backend::models::BrowserType::Edge
|
||||||
};
|
};
|
||||||
|
|
||||||
backend::browser_clean::run_browser_clean(browser_type, profiles).await
|
backend::browser_clean::run_browser_clean(browser_type, profiles, app_handle).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import { computed, ref } from "vue";
|
|||||||
import {
|
import {
|
||||||
startBrowserClean as runBrowserCleanCommand,
|
startBrowserClean as runBrowserCleanCommand,
|
||||||
startBrowserScan as runBrowserScanCommand,
|
startBrowserScan as runBrowserScanCommand,
|
||||||
|
subscribeBrowserCleanProgress,
|
||||||
} from "../services/tauri/cleaner";
|
} from "../services/tauri/cleaner";
|
||||||
import type {
|
import type {
|
||||||
AlertOptions,
|
AlertOptions,
|
||||||
BrowserScanResult,
|
BrowserScanResult,
|
||||||
CleanResult,
|
CleanResult,
|
||||||
ConfirmOptions,
|
ConfirmOptions,
|
||||||
|
ProjectCleanProgressPayload,
|
||||||
} from "../types/cleaner";
|
} from "../types/cleaner";
|
||||||
import { formatItemSize } from "../utils/format";
|
import { formatItemSize } from "../utils/format";
|
||||||
|
|
||||||
@@ -19,6 +21,13 @@ interface BrowserState {
|
|||||||
cleanResult: CleanResult | null;
|
cleanResult: CleanResult | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface BrowserCleanProgressState {
|
||||||
|
completedItems: number;
|
||||||
|
totalItems: number;
|
||||||
|
currentItem: string;
|
||||||
|
approxCompletedBytes: number;
|
||||||
|
}
|
||||||
|
|
||||||
export function useBrowserClean(
|
export function useBrowserClean(
|
||||||
browser: "chrome" | "edge",
|
browser: "chrome" | "edge",
|
||||||
showAlert: (options: AlertOptions) => void,
|
showAlert: (options: AlertOptions) => void,
|
||||||
@@ -31,20 +40,50 @@ export function useBrowserClean(
|
|||||||
scanResult: null,
|
scanResult: null,
|
||||||
cleanResult: null,
|
cleanResult: null,
|
||||||
});
|
});
|
||||||
|
const cleanProgress = ref<BrowserCleanProgressState>({
|
||||||
|
completedItems: 0,
|
||||||
|
totalItems: 0,
|
||||||
|
currentItem: "",
|
||||||
|
approxCompletedBytes: 0,
|
||||||
|
});
|
||||||
|
|
||||||
const selectedStats = computed(() => {
|
const selectedStats = computed(() => {
|
||||||
const scanResult = state.value.scanResult;
|
const scanResult = state.value.scanResult;
|
||||||
if (!scanResult) return { sizeStr: "0 B", count: 0, hasSelection: false };
|
if (!scanResult) return { totalBytes: 0, sizeStr: "0 B", count: 0, hasSelection: false };
|
||||||
|
|
||||||
const enabledProfiles = scanResult.profiles.filter((profile) => profile.enabled);
|
const enabledProfiles = scanResult.profiles.filter((profile) => profile.enabled);
|
||||||
const totalBytes = enabledProfiles.reduce((acc, profile) => acc + profile.cache_size, 0);
|
const totalBytes = enabledProfiles.reduce((acc, profile) => acc + profile.cache_size, 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
totalBytes,
|
||||||
sizeStr: formatItemSize(totalBytes),
|
sizeStr: formatItemSize(totalBytes),
|
||||||
count: enabledProfiles.length,
|
count: enabledProfiles.length,
|
||||||
hasSelection: enabledProfiles.length > 0,
|
hasSelection: enabledProfiles.length > 0,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
const cleanProgressSizeStr = computed(() => formatSizeValue(cleanProgress.value.approxCompletedBytes));
|
||||||
|
|
||||||
|
function formatSizeValue(bytes: number) {
|
||||||
|
return formatItemSize(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetCleanProgress() {
|
||||||
|
cleanProgress.value = {
|
||||||
|
completedItems: 0,
|
||||||
|
totalItems: 0,
|
||||||
|
currentItem: "",
|
||||||
|
approxCompletedBytes: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCleanProgress(payload: ProjectCleanProgressPayload) {
|
||||||
|
cleanProgress.value = {
|
||||||
|
completedItems: payload.completed_items,
|
||||||
|
totalItems: payload.total_items,
|
||||||
|
currentItem: payload.current_item,
|
||||||
|
approxCompletedBytes: payload.approx_completed_bytes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function startScan() {
|
async function startScan() {
|
||||||
const current = state.value;
|
const current = state.value;
|
||||||
@@ -102,7 +141,9 @@ export function useBrowserClean(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetCleanProgress();
|
||||||
current.isCleaning = true;
|
current.isCleaning = true;
|
||||||
|
const unlisten = await subscribeBrowserCleanProgress(handleCleanProgress);
|
||||||
try {
|
try {
|
||||||
current.cleanResult = await runBrowserCleanCommand(browser, selectedProfiles);
|
current.cleanResult = await runBrowserCleanCommand(browser, selectedProfiles);
|
||||||
current.isDone = true;
|
current.isDone = true;
|
||||||
@@ -114,6 +155,7 @@ export function useBrowserClean(
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
|
unlisten();
|
||||||
current.isCleaning = false;
|
current.isCleaning = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,11 +180,14 @@ export function useBrowserClean(
|
|||||||
scanResult: null,
|
scanResult: null,
|
||||||
cleanResult: null,
|
cleanResult: null,
|
||||||
};
|
};
|
||||||
|
resetCleanProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
state,
|
state,
|
||||||
selectedStats,
|
selectedStats,
|
||||||
|
cleanProgress,
|
||||||
|
cleanProgressSizeStr,
|
||||||
startScan,
|
startScan,
|
||||||
startClean,
|
startClean,
|
||||||
toggleAllProfiles,
|
toggleAllProfiles,
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import { startFastClean as runFastCleanCommand, startFastScan as runFastScanCommand } from "../services/tauri/cleaner";
|
import {
|
||||||
|
startFastClean as runFastCleanCommand,
|
||||||
|
startFastScan as runFastScanCommand,
|
||||||
|
subscribeFastCleanProgress,
|
||||||
|
} from "../services/tauri/cleaner";
|
||||||
import type {
|
import type {
|
||||||
AlertOptions,
|
AlertOptions,
|
||||||
CleanResult,
|
CleanResult,
|
||||||
ConfirmOptions,
|
ConfirmOptions,
|
||||||
FastScanResult,
|
FastScanResult,
|
||||||
|
ProjectCleanProgressPayload,
|
||||||
} from "../types/cleaner";
|
} from "../types/cleaner";
|
||||||
import { formatItemSize } from "../utils/format";
|
import { formatItemSize } from "../utils/format";
|
||||||
|
|
||||||
@@ -17,6 +22,13 @@ interface FastState {
|
|||||||
cleanResult: CleanResult | null;
|
cleanResult: CleanResult | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FastCleanProgressState {
|
||||||
|
completedItems: number;
|
||||||
|
totalItems: number;
|
||||||
|
currentItem: string;
|
||||||
|
approxCompletedBytes: number;
|
||||||
|
}
|
||||||
|
|
||||||
export function useFastClean(
|
export function useFastClean(
|
||||||
showAlert: (options: AlertOptions) => void,
|
showAlert: (options: AlertOptions) => void,
|
||||||
requestConfirm: (options: ConfirmOptions) => Promise<boolean>,
|
requestConfirm: (options: ConfirmOptions) => Promise<boolean>,
|
||||||
@@ -29,21 +41,47 @@ export function useFastClean(
|
|||||||
scanResult: null,
|
scanResult: null,
|
||||||
cleanResult: null,
|
cleanResult: null,
|
||||||
});
|
});
|
||||||
|
const cleanProgress = ref<FastCleanProgressState>({
|
||||||
|
completedItems: 0,
|
||||||
|
totalItems: 0,
|
||||||
|
currentItem: "",
|
||||||
|
approxCompletedBytes: 0,
|
||||||
|
});
|
||||||
|
|
||||||
const selectedStats = computed(() => {
|
const selectedStats = computed(() => {
|
||||||
const scanResult = state.value.scanResult;
|
const scanResult = state.value.scanResult;
|
||||||
if (!scanResult) return { sizeStr: "0 B", count: 0, hasSelection: false };
|
if (!scanResult) return { totalBytes: 0, sizeStr: "0 B", count: 0, hasSelection: false };
|
||||||
|
|
||||||
const enabledItems = scanResult.items.filter((item) => item.enabled);
|
const enabledItems = scanResult.items.filter((item) => item.enabled);
|
||||||
const totalBytes = enabledItems.reduce((acc, item) => acc + item.size, 0);
|
const totalBytes = enabledItems.reduce((acc, item) => acc + item.size, 0);
|
||||||
const totalCount = enabledItems.reduce((acc, item) => acc + item.count, 0);
|
const totalCount = enabledItems.reduce((acc, item) => acc + item.count, 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
totalBytes,
|
||||||
sizeStr: formatItemSize(totalBytes),
|
sizeStr: formatItemSize(totalBytes),
|
||||||
count: totalCount,
|
count: totalCount,
|
||||||
hasSelection: enabledItems.length > 0,
|
hasSelection: enabledItems.length > 0,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
const cleanProgressSizeStr = computed(() => formatItemSize(cleanProgress.value.approxCompletedBytes));
|
||||||
|
|
||||||
|
function resetCleanProgress() {
|
||||||
|
cleanProgress.value = {
|
||||||
|
completedItems: 0,
|
||||||
|
totalItems: 0,
|
||||||
|
currentItem: "",
|
||||||
|
approxCompletedBytes: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCleanProgress(payload: ProjectCleanProgressPayload) {
|
||||||
|
cleanProgress.value = {
|
||||||
|
completedItems: payload.completed_items,
|
||||||
|
totalItems: payload.total_items,
|
||||||
|
currentItem: payload.current_item,
|
||||||
|
approxCompletedBytes: payload.approx_completed_bytes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function startScan() {
|
async function startScan() {
|
||||||
const current = state.value;
|
const current = state.value;
|
||||||
@@ -104,7 +142,9 @@ export function useFastClean(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetCleanProgress();
|
||||||
current.isCleaning = true;
|
current.isCleaning = true;
|
||||||
|
const unlisten = await subscribeFastCleanProgress(handleCleanProgress);
|
||||||
try {
|
try {
|
||||||
current.cleanResult = await runFastCleanCommand(selectedPaths);
|
current.cleanResult = await runFastCleanCommand(selectedPaths);
|
||||||
current.isDone = true;
|
current.isDone = true;
|
||||||
@@ -116,6 +156,7 @@ export function useFastClean(
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
|
unlisten();
|
||||||
current.isCleaning = false;
|
current.isCleaning = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,11 +170,14 @@ export function useFastClean(
|
|||||||
scanResult: null,
|
scanResult: null,
|
||||||
cleanResult: null,
|
cleanResult: null,
|
||||||
};
|
};
|
||||||
|
resetCleanProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
state,
|
state,
|
||||||
selectedStats,
|
selectedStats,
|
||||||
|
cleanProgress,
|
||||||
|
cleanProgressSizeStr,
|
||||||
startScan,
|
startScan,
|
||||||
startClean,
|
startClean,
|
||||||
reset,
|
reset,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const props = defineProps<{
|
|||||||
requestConfirm: (options: ConfirmOptions) => Promise<boolean>;
|
requestConfirm: (options: ConfirmOptions) => Promise<boolean>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { state, selectedStats, startScan, startClean, toggleAllProfiles, invertProfiles, reset } =
|
const { state, selectedStats, cleanProgress, cleanProgressSizeStr, startScan, startClean, toggleAllProfiles, invertProfiles, reset } =
|
||||||
useBrowserClean(props.browser, props.showAlert, props.requestConfirm);
|
useBrowserClean(props.browser, props.showAlert, props.requestConfirm);
|
||||||
|
|
||||||
const browserName = props.browser === "chrome" ? "谷歌浏览器" : "微软浏览器";
|
const browserName = props.browser === "chrome" ? "谷歌浏览器" : "微软浏览器";
|
||||||
@@ -36,23 +36,26 @@ const browserName = props.browser === "chrome" ? "谷歌浏览器" : "微软浏
|
|||||||
|
|
||||||
<div v-else-if="state.scanResult && !state.isDone" class="result-card">
|
<div v-else-if="state.scanResult && !state.isDone" class="result-card">
|
||||||
<div class="result-header">
|
<div class="result-header">
|
||||||
<span class="result-icon">🌍</span>
|
<span class="result-icon">{{ state.isCleaning ? "🧹" : "🌍" }}</span>
|
||||||
<h2>扫描完成</h2>
|
<h2>{{ state.isCleaning ? "正在清理" : "扫描完成" }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="result-stats">
|
<div class="result-stats">
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<span class="stat-value">
|
<span class="stat-value">
|
||||||
{{ splitSize(selectedStats.sizeStr).value }}
|
{{ splitSize(state.isCleaning ? cleanProgressSizeStr : selectedStats.sizeStr).value }}
|
||||||
<span class="unit">{{ splitSize(selectedStats.sizeStr).unit }}</span>
|
<span class="unit">{{ splitSize(state.isCleaning ? cleanProgressSizeStr : selectedStats.sizeStr).unit }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="stat-label">预计释放</span>
|
<span class="stat-label">{{ state.isCleaning ? "已处理约" : "预计释放" }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-divider"></div>
|
<div class="stat-divider"></div>
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<span class="stat-value">{{ selectedStats.count }}</span>
|
<span class="stat-value">{{ state.isCleaning ? `${cleanProgress.completedItems}/${cleanProgress.totalItems}` : selectedStats.count }}</span>
|
||||||
<span class="stat-label">用户资料数量</span>
|
<span class="stat-label">{{ state.isCleaning ? "已完成资料" : "用户资料数量" }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<p v-if="state.isCleaning" class="cleaning-note">
|
||||||
|
正在清理:{{ cleanProgress.currentItem || "准备开始..." }},建议保持浏览器关闭以减少文件占用。
|
||||||
|
</p>
|
||||||
<button class="btn-primary main-btn" :disabled="state.isCleaning || !selectedStats.hasSelection" @click="startClean">
|
<button class="btn-primary main-btn" :disabled="state.isCleaning || !selectedStats.hasSelection" @click="startClean">
|
||||||
{{ state.isCleaning ? "正在清理..." : "立即清理" }}
|
{{ state.isCleaning ? "正在清理..." : "立即清理" }}
|
||||||
</button>
|
</button>
|
||||||
@@ -86,7 +89,7 @@ const browserName = props.browser === "chrome" ? "谷歌浏览器" : "微软浏
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="(state.isScanning || state.scanResult) && !state.isDone" class="detail-list">
|
<div v-if="(state.isScanning || state.scanResult) && !state.isDone && !state.isCleaning" class="detail-list">
|
||||||
<div class="list-header">
|
<div class="list-header">
|
||||||
<h3>用户资料列表</h3>
|
<h3>用户资料列表</h3>
|
||||||
<div class="list-actions">
|
<div class="list-actions">
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const props = defineProps<{
|
|||||||
requestConfirm: (options: ConfirmOptions) => Promise<boolean>;
|
requestConfirm: (options: ConfirmOptions) => Promise<boolean>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { state, selectedStats, startScan, startClean, reset } = useFastClean(
|
const { state, selectedStats, cleanProgress, cleanProgressSizeStr, startScan, startClean, reset } = useFastClean(
|
||||||
props.showAlert,
|
props.showAlert,
|
||||||
props.requestConfirm,
|
props.requestConfirm,
|
||||||
);
|
);
|
||||||
@@ -35,24 +35,28 @@ const { state, selectedStats, startScan, startClean, reset } = useFastClean(
|
|||||||
|
|
||||||
<div v-else-if="state.scanResult && !state.isDone" class="result-card">
|
<div v-else-if="state.scanResult && !state.isDone" class="result-card">
|
||||||
<div class="result-header">
|
<div class="result-header">
|
||||||
<span class="result-icon">📋</span>
|
<span class="result-icon">{{ state.isCleaning ? "🧹" : "📋" }}</span>
|
||||||
<h2>扫描完成</h2>
|
<h2>{{ state.isCleaning ? "正在清理" : "扫描完成" }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="result-stats">
|
<div class="result-stats">
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<span class="stat-value">
|
<span class="stat-value">
|
||||||
{{ splitSize(selectedStats.sizeStr).value }}
|
{{ splitSize(state.isCleaning ? cleanProgressSizeStr : selectedStats.sizeStr).value }}
|
||||||
<span class="unit">{{ splitSize(selectedStats.sizeStr).unit }}</span>
|
<span class="unit">{{ splitSize(state.isCleaning ? cleanProgressSizeStr : selectedStats.sizeStr).unit }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="stat-label">预计释放</span>
|
<span class="stat-label">{{ state.isCleaning ? "已处理约" : "预计释放" }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-divider"></div>
|
<div class="stat-divider"></div>
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<span class="stat-value">{{ selectedStats.count }}</span>
|
<span class="stat-value">{{ state.isCleaning ? `${cleanProgress.completedItems}/${cleanProgress.totalItems}` : selectedStats.count }}</span>
|
||||||
<span class="stat-label">文件数量</span>
|
<span class="stat-label">{{ state.isCleaning ? "已完成项目" : "文件数量" }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p v-if="state.isCleaning" class="cleaning-note">
|
||||||
|
正在清理:{{ cleanProgress.currentItem || "准备开始..." }},请稍候,不要关闭程序。
|
||||||
|
</p>
|
||||||
|
|
||||||
<button class="btn-primary main-btn" :disabled="state.isCleaning || !selectedStats.hasSelection" @click="startClean">
|
<button class="btn-primary main-btn" :disabled="state.isCleaning || !selectedStats.hasSelection" @click="startClean">
|
||||||
{{ state.isCleaning ? "正在清理..." : "立即清理" }}
|
{{ state.isCleaning ? "正在清理..." : "立即清理" }}
|
||||||
</button>
|
</button>
|
||||||
@@ -87,7 +91,7 @@ const { state, selectedStats, startScan, startClean, reset } = useFastClean(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="(state.isScanning || state.scanResult) && !state.isDone" class="detail-list">
|
<div v-if="(state.isScanning || state.scanResult) && !state.isDone && !state.isCleaning" class="detail-list">
|
||||||
<h3>清理项详情</h3>
|
<h3>清理项详情</h3>
|
||||||
<div
|
<div
|
||||||
v-for="item in state.scanResult?.items || []"
|
v-for="item in state.scanResult?.items || []"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import type {
|
|||||||
FastScanResult,
|
FastScanResult,
|
||||||
FileNode,
|
FileNode,
|
||||||
MemoryStats,
|
MemoryStats,
|
||||||
|
ProjectCleanProgressPayload,
|
||||||
ScanProgressPayload,
|
ScanProgressPayload,
|
||||||
} from "../../types/cleaner";
|
} from "../../types/cleaner";
|
||||||
|
|
||||||
@@ -54,6 +55,22 @@ export function subscribeScanProgress(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function subscribeFastCleanProgress(
|
||||||
|
handler: (payload: ProjectCleanProgressPayload) => void,
|
||||||
|
) {
|
||||||
|
return listen<ProjectCleanProgressPayload>("fast-clean-progress", (event) => {
|
||||||
|
handler(event.payload);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function subscribeBrowserCleanProgress(
|
||||||
|
handler: (payload: ProjectCleanProgressPayload) => void,
|
||||||
|
) {
|
||||||
|
return listen<ProjectCleanProgressPayload>("browser-clean-progress", (event) => {
|
||||||
|
handler(event.payload);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function openInExplorer(path: string) {
|
export function openInExplorer(path: string) {
|
||||||
return invoke("open_in_explorer", { path });
|
return invoke("open_in_explorer", { path });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,6 +136,13 @@
|
|||||||
width: 180px;
|
width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cleaning-note {
|
||||||
|
margin: -8px 0 24px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: var(--text-sec);
|
||||||
|
}
|
||||||
|
|
||||||
.scan-circle-container {
|
.scan-circle-container {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ export interface ScanProgressPayload {
|
|||||||
current_path: string;
|
current_path: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProjectCleanProgressPayload {
|
||||||
|
completed_items: number;
|
||||||
|
total_items: number;
|
||||||
|
current_item: string;
|
||||||
|
approx_completed_bytes: number;
|
||||||
|
}
|
||||||
|
|
||||||
export type ModalType = "info" | "success" | "error";
|
export type ModalType = "info" | "success" | "error";
|
||||||
export type ModalMode = "alert" | "confirm";
|
export type ModalMode = "alert" | "confirm";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user