support fast clean options
This commit is contained in:
@@ -174,11 +174,12 @@ pub struct CleaningConfig {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
pub filter_days: Option<u64>,
|
pub filter_days: Option<u64>,
|
||||||
|
pub default_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CleaningConfig {
|
impl CleaningConfig {
|
||||||
fn new(name: &str, path: &str, filter_days: Option<u64>) -> Self {
|
fn new(name: &str, path: &str, filter_days: Option<u64>, default_enabled: bool) -> Self {
|
||||||
Self { name: name.into(), path: path.into(), filter_days }
|
Self { name: name.into(), path: path.into(), filter_days, default_enabled }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,33 +189,32 @@ fn get_fast_cleaning_configs() -> Vec<CleaningConfig> {
|
|||||||
|
|
||||||
// 1. 用户临时文件
|
// 1. 用户临时文件
|
||||||
if let Ok(t) = std::env::var("TEMP") {
|
if let Ok(t) = std::env::var("TEMP") {
|
||||||
configs.push(CleaningConfig::new("用户临时文件", &t, None));
|
configs.push(CleaningConfig::new("用户临时文件", &t, None, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 系统临时文件
|
// 2. 系统临时文件
|
||||||
configs.push(CleaningConfig::new("系统临时文件", "C:\\Windows\\Temp", None));
|
configs.push(CleaningConfig::new("系统临时文件", "C:\\Windows\\Temp", None, true));
|
||||||
|
|
||||||
// 3. Windows 更新残留 (通常建议清理 10 天前的)
|
// 3. Windows 更新残留 (通常建议清理 10 天前的)
|
||||||
configs.push(CleaningConfig::new("Windows 更新残留", "C:\\Windows\\SoftwareDistribution\\Download", Some(10)));
|
configs.push(CleaningConfig::new("Windows 更新残留", "C:\\Windows\\SoftwareDistribution\\Download", Some(10), true));
|
||||||
|
|
||||||
// 4. 传递优化缓存
|
// 4. 内核转储文件
|
||||||
// configs.push(CleaningConfig::new(
|
configs.push(CleaningConfig::new("内核转储文件", "C:\\Windows\\LiveKernelReports", None, false));
|
||||||
// "传递优化缓存",
|
|
||||||
// "C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\Microsoft\\Windows\\DeliveryOptimization",
|
|
||||||
// None
|
|
||||||
// ));
|
|
||||||
|
|
||||||
// 以后要添加新目录,只需在此处追加一行:
|
|
||||||
// configs.push(CleaningConfig::new("新目录名称", "C:\\路径", None));
|
|
||||||
|
|
||||||
configs
|
configs
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Clone)]
|
#[derive(Serialize, Clone)]
|
||||||
pub struct ScanItem { pub name: String, pub path: String, pub size: u64, pub count: u32 }
|
pub struct ScanItem {
|
||||||
|
pub name: String,
|
||||||
|
pub path: String,
|
||||||
|
pub size: u64,
|
||||||
|
pub count: u32,
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct FastScanResult { pub items: Vec<ScanItem>, pub total_size: String, pub total_count: u32 }
|
pub struct FastScanResult { pub items: Vec<ScanItem>, total_size: String, total_count: u32 }
|
||||||
|
|
||||||
pub async fn run_fast_scan() -> FastScanResult {
|
pub async fn run_fast_scan() -> FastScanResult {
|
||||||
let configs = get_fast_cleaning_configs();
|
let configs = get_fast_cleaning_configs();
|
||||||
@@ -228,7 +228,8 @@ pub async fn run_fast_scan() -> FastScanResult {
|
|||||||
name: config.name,
|
name: config.name,
|
||||||
path: config.path,
|
path: config.path,
|
||||||
size,
|
size,
|
||||||
count
|
count,
|
||||||
|
enabled: config.default_enabled,
|
||||||
});
|
});
|
||||||
total_bytes += size;
|
total_bytes += size;
|
||||||
total_count += count;
|
total_count += count;
|
||||||
@@ -274,21 +275,14 @@ pub struct CleanResult {
|
|||||||
pub fail_count: u32,
|
pub fail_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_fast_clean(is_simulation: bool) -> Result<CleanResult, String> {
|
pub async fn run_fast_clean(selected_paths: Vec<String>) -> Result<CleanResult, String> {
|
||||||
if is_simulation {
|
|
||||||
return Ok(CleanResult {
|
|
||||||
total_freed: "0 B".into(),
|
|
||||||
success_count: 0,
|
|
||||||
fail_count: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let configs = get_fast_cleaning_configs();
|
let configs = get_fast_cleaning_configs();
|
||||||
let mut success_count = 0;
|
let mut success_count = 0;
|
||||||
let mut fail_count = 0;
|
let mut fail_count = 0;
|
||||||
let mut total_freed: u64 = 0;
|
let mut total_freed: u64 = 0;
|
||||||
|
|
||||||
for config in configs {
|
for config in configs {
|
||||||
|
if selected_paths.contains(&config.path) {
|
||||||
let path = Path::new(&config.path);
|
let path = Path::new(&config.path);
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
let (freed, s, f) = clean_directory_contents(path, config.filter_days);
|
let (freed, s, f) = clean_directory_contents(path, config.filter_days);
|
||||||
@@ -297,6 +291,7 @@ pub async fn run_fast_clean(is_simulation: bool) -> Result<CleanResult, String>
|
|||||||
fail_count += f;
|
fail_count += f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(CleanResult {
|
Ok(CleanResult {
|
||||||
total_freed: format_size(total_freed),
|
total_freed: format_size(total_freed),
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ async fn start_fast_scan() -> cleaner::FastScanResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn start_fast_clean(is_simulation: bool) -> Result<cleaner::CleanResult, String> {
|
async fn start_fast_clean(selected_paths: Vec<String>) -> Result<cleaner::CleanResult, String> {
|
||||||
cleaner::run_fast_clean(is_simulation).await
|
cleaner::run_fast_clean(selected_paths).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
82
src/App.vue
82
src/App.vue
@@ -10,7 +10,7 @@ const activeTab = ref<Tab>('clean-c-fast');
|
|||||||
const isCMenuOpen = ref(true);
|
const isCMenuOpen = ref(true);
|
||||||
|
|
||||||
// --- 数据结构 ---
|
// --- 数据结构 ---
|
||||||
interface ScanItem { name: string; path: string; size: number; count: number; }
|
interface ScanItem { name: string; path: string; size: number; count: number; enabled: boolean; }
|
||||||
interface FastScanResult { items: ScanItem[]; total_size: string; total_count: number; }
|
interface FastScanResult { items: ScanItem[]; total_size: string; total_count: number; }
|
||||||
interface CleanResult { total_freed: string; success_count: number; fail_count: number; }
|
interface CleanResult { total_freed: string; success_count: number; fail_count: number; }
|
||||||
interface FileNode {
|
interface FileNode {
|
||||||
@@ -115,10 +115,19 @@ async function startFastScan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function startFastClean() {
|
async function startFastClean() {
|
||||||
if (isCleaning.value) return;
|
if (isCleaning.value || !fastScanResult.value) return;
|
||||||
|
const selectedPaths = fastScanResult.value.items
|
||||||
|
.filter(item => item.enabled)
|
||||||
|
.map(item => item.path);
|
||||||
|
|
||||||
|
if (selectedPaths.length === 0) {
|
||||||
|
showAlert("未选择任何项", "请至少勾选一个需要清理的项目。", 'info');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
isCleaning.value = true;
|
isCleaning.value = true;
|
||||||
try {
|
try {
|
||||||
const res = await invoke<CleanResult>("start_fast_clean", { isSimulation: false });
|
const res = await invoke<CleanResult>("start_fast_clean", { selectedPaths });
|
||||||
cleanResult.value = res;
|
cleanResult.value = res;
|
||||||
isCleanDone.value = true;
|
isCleanDone.value = true;
|
||||||
fastScanResult.value = null;
|
fastScanResult.value = null;
|
||||||
@@ -353,8 +362,20 @@ function splitSize(sizeStr: string | number) {
|
|||||||
|
|
||||||
<div class="detail-list" v-if="(isScanning || fastScanResult) && !isCleanDone">
|
<div class="detail-list" v-if="(isScanning || fastScanResult) && !isCleanDone">
|
||||||
<h3>清理项详情</h3>
|
<h3>清理项详情</h3>
|
||||||
<div class="detail-item" v-for="item in fastScanResult?.items || []" :key="item.path">
|
<div
|
||||||
|
class="detail-item"
|
||||||
|
v-for="item in fastScanResult?.items || []"
|
||||||
|
:key="item.path"
|
||||||
|
@click="item.enabled = !item.enabled"
|
||||||
|
:class="{ disabled: !item.enabled }"
|
||||||
|
>
|
||||||
|
<div class="item-info">
|
||||||
|
<label class="checkbox-container" @click.stop>
|
||||||
|
<input type="checkbox" v-model="item.enabled">
|
||||||
|
<span class="checkmark"></span>
|
||||||
|
</label>
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
|
</div>
|
||||||
<span class="item-size">{{ formatItemSize(item.size) }}</span>
|
<span class="item-size">{{ formatItemSize(item.size) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isScanning" class="scanning-placeholder">正在深度扫描文件系统...</div>
|
<div v-if="isScanning" class="scanning-placeholder">正在深度扫描文件系统...</div>
|
||||||
@@ -972,14 +993,63 @@ body {
|
|||||||
.detail-item {
|
.detail-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
border-bottom: 1px solid #F5F5F7;
|
border-bottom: 1px solid #F5F5F7;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #424245;
|
color: #424245;
|
||||||
transition: transform 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.detail-item:hover { transform: translateX(4px); }
|
.detail-item:hover { background-color: #FAFAFB; padding-left: 8px; padding-right: 8px; border-radius: 8px; }
|
||||||
|
.detail-item.disabled { opacity: 0.5; }
|
||||||
.detail-item:last-child { border-bottom: none; }
|
.detail-item:last-child { border-bottom: none; }
|
||||||
|
|
||||||
|
.item-info { display: flex; align-items: center; gap: 12px; }
|
||||||
|
|
||||||
|
/* --- 自定义复选框 --- */
|
||||||
|
.checkbox-container {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-container input {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 0; width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkmark {
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0;
|
||||||
|
height: 20px; width: 20px;
|
||||||
|
background-color: #F2F2F7;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
border: 1px solid #E5E5E7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-container:hover input ~ .checkmark { background-color: #E5E5E7; }
|
||||||
|
.checkbox-container input:checked ~ .checkmark { background-color: var(--primary-color); border-color: var(--primary-color); }
|
||||||
|
|
||||||
|
.checkmark:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
left: 6px; top: 1px;
|
||||||
|
width: 5px; height: 10px;
|
||||||
|
border: solid white;
|
||||||
|
border-width: 0 2px 2px 0;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-container input:checked ~ .checkmark:after { display: block; }
|
||||||
|
|
||||||
.item-size { font-weight: 600; color: var(--primary-color); }
|
.item-size { font-weight: 600; color: var(--primary-color); }
|
||||||
|
|
||||||
.placeholder-page { padding-top: 120px; text-align: center; color: var(--text-sec); }
|
.placeholder-page { padding-top: 120px; text-align: center; color: var(--text-sec); }
|
||||||
|
|||||||
Reference in New Issue
Block a user