chore:优化UI.提升多线程性能

This commit is contained in:
ASLant committed 2025-11-13 22:27:01 +08:00
1 parent aa1a780fac
commit f674a10bc0
4 files changed
+208 -52

No files matched your search

+90 -7
View File
@@ -216,29 +216,112 @@ async fn process_upx(options: UpxOptions) -> Result<String, String> {
100
};
// 过滤并格式化输出信息
let upx_output = format_upx_output(&stdout, &stderr);
let result = format!(
"操作成功!\n输出: {}\n原始大小: {}\n处理后大小: {}\n压缩率: {}%\n\nUPX 输出:\n{}{}",
"操作成功!\n输出: {}\n原始大小: {}\n处理后大小: {}\n压缩率: {}%{}",
output_file_clone,
format_bytes(original_size_clone),
format_bytes(output_size),
ratio,
stdout,
stderr
upx_output
);
Ok(result)
} else {
Err(format!("UPX 执行失败:\n{}{}", stdout, stderr))
// 解析并优化错误信息
let error_msg = parse_upx_error(&stdout, &stderr);
Err(error_msg)
}
}
Err(e) => {
Err(format!("执行 UPX 命令失败: {}", e))
}
Err(e) => Err(format!("执行 UPX 命令失败: {}", e)),
}
})
.await
.map_err(|e| format!("任务执行错误: {}", e))?
}
// 格式化 UPX 成功输出信息
fn format_upx_output(stdout: &str, stderr: &str) -> String {
let combined = format!("{}{}", stdout, stderr);
// 过滤掉不需要的行
let output_lines: Vec<&str> = combined
.lines()
.filter(|line| {
let l = line.trim();
!l.is_empty()
&& !l.starts_with("---")
&& !l.starts_with("File size")
&& !l.starts_with("Ratio")
&& !l.starts_with("Format")
&& !l.starts_with("Name")
&& !l.starts_with("Ultimate Packer")
&& !l.starts_with("Copyright")
&& !l.starts_with("UPX ")
})
.collect();
if output_lines.is_empty() {
return String::new();
}
format!("\n\nUPX 输出:\n{}", output_lines.join("\n"))
}
// 解析并优化 UPX 错误信息
fn parse_upx_error(stdout: &str, stderr: &str) -> String {
let combined = format!("{}{}", stdout, stderr);
// 常见错误模式匹配
if combined.contains("AlreadyPackedException") || combined.contains("already packed") {
return "[错误] 文件已经被 UPX 加壳过了\n\n解决方案:\n - 如果要重新压缩,请先使用「脱壳解压」功能\n - 或者选择其他未加壳的文件".to_string();
}
if combined.contains("NotPackedException") || combined.contains("not packed") {
return "[错误] 文件未被 UPX 加壳,无法脱壳\n\n解决方案:\n - 请确认文件是否使用 UPX 加壳\n - 或者选择「加壳压缩」功能".to_string();
}
if combined.contains("CantPackException") {
return "[错误] 无法压缩此文件\n\n可能原因:\n - 文件格式不支持\n - 文件已损坏\n - 文件受保护(尝试启用「强制压缩」选项)".to_string();
}
if combined.contains("OverlayException") {
return "[错误] 文件包含附加数据(Overlay)\n\n解决方案:\n - 某些文件在末尾附加了额外数据\n - 尝试启用「强制压缩」选项\n - 或使用其他工具移除附加数据".to_string();
}
if combined.contains("IOException") || combined.contains("can't open") {
return "[错误] 文件访问失败\n\n可能原因:\n - 文件被其他程序占用\n - 文件权限不足\n - 文件路径包含特殊字符".to_string();
}
if combined.contains("NotCompressibleException") {
return "[错误] 文件无法压缩\n\n可能原因:\n - 文件已经高度压缩\n - 压缩后反而会变大\n - UPX 自动跳过了此文件".to_string();
}
// 如果没有匹配到特定错误,返回简化的原始信息
let error_lines: Vec<&str> = combined
.lines()
.filter(|line| {
let l = line.trim();
!l.is_empty()
&& !l.starts_with("---")
&& !l.starts_with("File size")
&& !l.starts_with("Ratio")
&& !l.starts_with("Format")
&& !l.starts_with("Name")
})
.collect();
if error_lines.is_empty() {
return "[错误] UPX 处理失败\n\n请检查文件是否正常,或尝试其他选项".to_string();
}
format!(
"[错误] UPX 处理失败\n\n错误信息:\n{}",
error_lines.join("\n")
)
}
// 格式化字节大小
fn format_bytes(bytes: u64) -> String {
const KB: u64 = 1024;
+15
View File
@@ -111,15 +111,30 @@ body {
.log-error {
color: #ef4444;
background: rgba(239, 68, 68, 0.1);
padding: 8px 12px;
border-radius: 4px;
border-left: 3px solid #ef4444;
margin: 8px 0;
}
.log-warning {
color: #f59e0b;
background: rgba(245, 158, 11, 0.1);
padding: 8px 12px;
border-radius: 4px;
border-left: 3px solid #f59e0b;
margin: 8px 0;
}
.log-success {
color: #10b981;
font-weight: 600;
background: rgba(16, 185, 129, 0.1);
padding: 8px 12px;
border-radius: 4px;
border-left: 3px solid #10b981;
margin: 8px 0;
}
/* 按钮动画 */
+2
View File
@@ -6,6 +6,8 @@
<title>UPX GUI</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="css/style.css">
<!-- 预加载关键资源 -->
<link rel="preload" href="js/main.js" as="script">
</head>
<body class="h-screen overflow-hidden bg-gray-50" oncontextmenu="return false;">
<!-- 标题栏 -->
+101 -45
View File
@@ -59,30 +59,42 @@ document.addEventListener('DOMContentLoaded', async () => {
await appWindow.show();
}, 100);
// 监听窗口大小变化,更新按钮位置缓存
// 监听窗口大小变化,更新按钮位置缓存(使用防抖优化)
let resizeTimer;
window.addEventListener('resize', () => {
cachedButtonRects = null; // 清除缓存,下次使用时重新计算
});
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
cachedButtonRects = null; // 清除缓存,下次使用时重新计算
}, 150);
}, { passive: true });
});
// 屏蔽刷新快捷键
function preventRefresh() {
// 使用 Map 提高查找效率
const blockedKeys = new Set(['F5']);
document.addEventListener('keydown', (e) => {
// F5 或 Ctrl+R 或 Ctrl+Shift+R 刷新
if (e.key === 'F5' ||
(e.ctrlKey && e.key === 'r') ||
(e.ctrlKey && e.shiftKey && e.key === 'R')) {
// F5 刷新
if (blockedKeys.has(e.key)) {
e.preventDefault();
addLog('刷新功能已禁用', 'warning');
return false;
}
// Ctrl+R 或 Ctrl+Shift+R 刷新
if (e.ctrlKey && e.key.toLowerCase() === 'r') {
e.preventDefault();
addLog('刷新功能已禁用', 'warning');
return false;
}
// Ctrl+W 关闭窗口
if (e.ctrlKey && e.key === 'w') {
if (e.ctrlKey && e.key.toLowerCase() === 'w') {
e.preventDefault();
return false;
}
});
}, { passive: false });
}
// 窗口控制
@@ -256,20 +268,22 @@ async function processBatchFiles(files, mode) {
addLog('没有找到可处理的文件', 'warning');
return;
}
addLog(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`, 'info');
addLog(`批量处理模式`, 'info');
addLog(`找到 ${files.length} 个文件`, 'info');
addLog(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`, 'info');
addLog(`批量处理模式 - 找到 ${files.length} 个文件`, 'info');
let successCount = 0;
let failCount = 0;
// 并行处理,每次最多5个文件
const batchSize = 5;
for (let i = 0; i < files.length; i += batchSize) {
const batch = files.slice(i, Math.min(i + batchSize, files.length));
const batchStart = i + 1;
const batchEnd = Math.min(i + batchSize, files.length);
// 显示进度
addLog(`处理进度: ${batchStart}-${batchEnd}/${files.length}`, 'info');
await Promise.all(batch.map(async (file) => {
try {
if (mode === 'compress') {
@@ -283,12 +297,12 @@ async function processBatchFiles(files, mode) {
failCount++;
}
}));
// 让出主线程,避免阻塞 UI
await new Promise(resolve => setTimeout(resolve, 0));
}
addLog(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`, 'info', true);
addLog(`批量处理完成!`, 'success', true);
addLog(`成功: ${successCount} 个,失败: ${failCount} 个`, 'info', true);
addLog(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`, 'info', true);
addLog(`批量处理完成! 成功: ${successCount} 个,失败: ${failCount} 个`, 'success', true);
}
// 判断路径是否为文件夹并扫描
@@ -399,18 +413,20 @@ function getDropTarget(position) {
// 为元素设置视觉反馈
function setupVisualFeedback(element) {
['dragenter', 'dragover'].forEach(eventName => {
element.addEventListener(eventName, (e) => {
e.preventDefault();
element.classList.add('drag-over');
}, false);
});
const handleDragEnter = (e) => {
e.preventDefault();
element.classList.add('drag-over');
};
['dragleave', 'drop'].forEach(eventName => {
element.addEventListener(eventName, () => {
element.classList.remove('drag-over');
}, false);
});
const handleDragLeave = () => {
element.classList.remove('drag-over');
};
// 使用被动事件监听器优化性能
element.addEventListener('dragenter', handleDragEnter, { passive: false });
element.addEventListener('dragover', handleDragEnter, { passive: false });
element.addEventListener('dragleave', handleDragLeave, { passive: true });
element.addEventListener('drop', handleDragLeave, { passive: true });
}
// 处理加壳压缩
@@ -551,20 +567,54 @@ async function processUpx(mode, inputFile, outputFile) {
if (ultraBruteCheckbox.checked) {
addLog('已启用极限压缩模式', 'info');
}
if (forceCompressCheckbox.checked) {
addLog('已启用强制压缩模式', 'warning');
}
addLog(`开始${mode === 'compress' ? '加壳压缩' : '脱壳解压'}...`, 'info');
const result = await invoke('process_upx', { options });
addLog(result, 'success');
addLog('操作完成!', 'success', true);
// 分行显示结果信息
const lines = result.split('\n');
lines.forEach((line) => {
if (line.trim()) {
// 根据内容判断日志类型
if (line.includes('操作成功') || line.includes('操作完成')) {
addLog(line, 'success', true);
} else if (line.includes('输出:') || line.includes('大小:') || line.includes('压缩率:')) {
addLog(line, 'success');
} else if (line.includes('UPX 输出:')) {
addLog(line, 'info');
} else if (line.includes('扫描') || line.includes('检测')) {
addLog(line, 'warning');
} else {
addLog(line, 'info');
}
}
});
} catch (error) {
addLog(`处理失败: ${error}`, 'error');
// 格式化错误信息,保留换行和特殊字符
const errorMsg = String(error);
// 分行显示错误信息,保持格式
const lines = errorMsg.split('\n');
lines.forEach((line, index) => {
if (line.trim()) {
// 错误标题使用 error,解决方案等使用 warning
if (line.includes('[错误]')) {
addLog(line, 'error');
} else if (line.includes('解决方案:') || line.includes('可能原因:')) {
addLog(line, 'warning');
} else if (line.trim().startsWith('-')) {
addLog(line, 'info');
} else {
addLog(line, index === 0 ? 'error' : 'warning');
}
}
});
}
}
@@ -579,6 +629,9 @@ async function handleRefreshIcon() {
}
}
// 日志初始化标志
let logInitialized = false;
// 添加日志
function addLog(message, type = 'info', highlight = false) {
const logLine = document.createElement('div');
@@ -593,15 +646,18 @@ function addLog(message, type = 'info', highlight = false) {
logLine.textContent = `[${timestamp}] ${message}`;
// 清除初始提示
if (logOutput.querySelector('.text-gray-500')) {
// 清除初始提示(只执行一次)
if (!logInitialized) {
logOutput.innerHTML = '';
logInitialized = true;
}
logOutput.appendChild(logLine);
// 自动滚动到底部
logOutput.scrollTop = logOutput.scrollHeight;
// 使用 requestAnimationFrame 优化滚动性能
requestAnimationFrame(() => {
logOutput.scrollTop = logOutput.scrollHeight;
});
}
// 保存当前配置