chore:优化UI.提升多线程性能
This commit is contained in:
1 parent
aa1a780fac
commit
f674a10bc0
4 files changed
+208
-52
No files matched your search
+90
-7
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/* 按钮动画 */
|
||||
|
||||
@@ -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
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
// 保存当前配置
|
||||
|
||||
Reference in new issue
Block a user