From ca9eb1e12e63fad5fb631198c4f55cbf319ac398 Mon Sep 17 00:00:00 2001 From: imeepos Date: Thu, 31 Jul 2025 18:03:43 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E4=BF=AE=E5=A4=8D=E6=96=87=E6=A1=A3=E5=92=8C?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加批量任务修复总结文档 - 添加批量任务显示测试脚本 - 更新项目依赖锁文件 - 更新Tauri功能文档 --- Cargo.lock | 167 +++++++++++++++++++++++++++++++++++++ TAURI_AWESOME.md | 20 ++++- batch_task_fix_summary.md | 140 +++++++++++++++++++++++++++++++ test_batch_task_display.js | 86 +++++++++++++++++++ 4 files changed, 409 insertions(+), 4 deletions(-) create mode 100644 batch_task_fix_summary.md create mode 100644 test_batch_task_display.js diff --git a/Cargo.lock b/Cargo.lock index 3d3cfe7..cb1fa74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -321,6 +321,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -551,6 +557,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "combine" version = "4.6.7" @@ -663,12 +675,37 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.6" @@ -945,6 +982,12 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "embed-resource" version = "3.0.4" @@ -1048,6 +1091,21 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "exr" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -1408,6 +1466,16 @@ dependencies = [ "wasi 0.14.2+wasi-0.2.4", ] +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.31.1" @@ -1581,6 +1649,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1957,6 +2035,24 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-traits", + "png", + "qoi", + "tiff", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -2085,6 +2181,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jpeg-decoder" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" +dependencies = [ + "rayon", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -2146,6 +2251,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libappindicator" version = "0.9.0" @@ -2360,6 +2471,7 @@ dependencies = [ "futures-util", "hex", "hmac", + "image", "lazy_static", "md5", "num_cpus", @@ -3260,6 +3372,15 @@ dependencies = [ "unicase", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quick-xml" version = "0.37.5" @@ -3380,6 +3501,26 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.17" @@ -4641,6 +4782,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.3.41" @@ -5503,6 +5655,12 @@ dependencies = [ "windows-core 0.61.2", ] +[[package]] +name = "weezl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" + [[package]] name = "winapi" version = "0.3.9" @@ -6309,6 +6467,15 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + [[package]] name = "zvariant" version = "4.0.0" diff --git a/TAURI_AWESOME.md b/TAURI_AWESOME.md index 8d000f9..8bd0e61 100644 --- a/TAURI_AWESOME.md +++ b/TAURI_AWESOME.md @@ -13,27 +13,28 @@ A curated collection of the best stuff from the Tauri ecosystem and community. ## Table of Contents +- [Table of Contents](#table-of-contents) - [Getting Started](#getting-started) - - [Guides & Tutorials](#guides--tutorials) + - [Guides \& Tutorials](#guides--tutorials) - [Templates](#templates) - [Development](#development) - [Plugins](#plugins) - [Integrations](#integrations) - [Articles](#articles) - [Applications](#applications) - - [Audio & Video](#audio--video) + - [Audio \& Video](#audio--video) - [ChatGPT clients](#chatgpt-clients) - [Data](#data) - [Developer tools](#developer-tools) - [Ebook readers](#ebook-readers) - - [Email & Feeds](#email--feeds) + - [Email \& Feeds](#email--feeds) - [File management](#file-management) - [Finance](#finance) - [Gaming](#gaming) - [Information](#information) - [Learning](#learning) - [Networking](#networking) - - [Office & Writing](#office--writing) + - [Office \& Writing](#office--writing) - [Productivity](#productivity) - [Search](#search) - [Security](#security) @@ -460,3 +461,14 @@ A curated collection of the best stuff from the Tauri ecosystem and community. [youtube]: https://img.shields.io/badge/YouTube-FF0000 [v1]: https://img.shields.io/badge/v1-white [v2]: https://img.shields.io/badge/v2-white + + + +BOWONG-INPUT-字符串 {image, image_url, text} + +BOWONG-INPUT +{ + type: "string|image", + value: "", + category: "提示词/模特/服装/" +} diff --git a/batch_task_fix_summary.md b/batch_task_fix_summary.md new file mode 100644 index 0000000..09adb6d --- /dev/null +++ b/batch_task_fix_summary.md @@ -0,0 +1,140 @@ +# 图像编辑工具批量处理任务列表显示问题修复总结 + +## 问题描述 +当用户点击批量处理按钮时,右侧边栏的"最近任务"列表显示为空。分析发现是因为后端的批量任务只在处理完成后才存储到任务列表中,而前端在任务开始时调用 loadTasks() 获取不到正在处理的任务。 + +## 修复方案 + +### 1. 修改后端批量编辑命令 (✅ 已完成) +**文件**: `apps/desktop/src-tauri/src/presentation/commands/image_editing_commands.rs` + +**主要改动**: +- 在 `edit_batch_images` 函数开始时立即创建 `BatchImageEditingTask` +- 先将任务存储到 `state.batch_tasks` 中,状态设为 `Processing` +- 使用 `tokio::spawn` 异步执行批量处理,避免阻塞命令返回 +- 通过回调机制实时更新任务状态 +- 修复了生命周期问题,正确克隆 `Arc>` 而不是借用 `State` + +**关键代码变化**: +```rust +// 立即创建并存储任务 +let mut batch_task = BatchImageEditingTask::new(/* ... */); +batch_task.status = ImageEditingTaskStatus::Processing; + +// 立即存储,前端可以获取到 +{ + let mut batch_tasks = state.batch_tasks.lock().map_err(/*...*/)?; + batch_tasks.insert(task_id.clone(), batch_task.clone()); +} + +// 异步执行处理 +tokio::spawn(async move { + // 处理逻辑... +}); +``` + +### 2. 修改图像编辑服务批量处理方法 (✅ 已完成) +**文件**: `apps/desktop/src-tauri/src/infrastructure/image_editing_service.rs` + +**主要改动**: +- 添加新方法 `edit_batch_images_with_callback` +- 接受任务状态更新回调 `Box` +- 在处理每个图片后调用回调更新任务状态 +- 确保任务进度和状态能实时反映到存储中 + +**关键代码变化**: +```rust +pub async fn edit_batch_images_with_callback( + &self, + input_folder: &Path, + output_folder: &Path, + prompt: &str, + params: &ImageEditingParams, + task_update_callback: Option>, +) -> Result { + // 初始回调 + if let Some(ref callback) = task_update_callback { + callback(batch_task.clone()); + } + + // 处理每个图像后回调 + for (index, input_file) in image_files.iter().enumerate() { + // ... 处理逻辑 ... + batch_task.update_progress(); + + // 每处理完一个图像就回调更新状态 + if let Some(ref callback) = task_update_callback { + callback(batch_task.clone()); + } + } +} +``` + +### 3. 优化前端任务刷新机制 (✅ 已完成) +**文件**: `apps/desktop/src/pages/tools/ImageEditingTool.tsx` + +**主要改动**: +- 添加定时刷新状态管理 `refreshInterval` +- 实现 `hasProcessingTasks()` 检查是否有处理中的任务 +- 实现 `startAutoRefresh()` 和 `stopAutoRefresh()` 控制定时刷新 +- 使用 `useEffect` 监听任务状态变化,自动启动/停止刷新 +- 简化批量处理函数,移除不必要的本地任务创建 + +**关键代码变化**: +```typescript +// 检查是否有处理中的任务 +const hasProcessingTasks = useCallback(() => { + return [...tasks, ...batchTasks].some(task => + task.status === ImageEditingTaskStatus.Processing || + task.status === ImageEditingTaskStatus.Pending + ); +}, [tasks, batchTasks]); + +// 监听任务状态变化,自动启动/停止刷新 +useEffect(() => { + if (hasProcessingTasks()) { + startAutoRefresh(); + } else { + stopAutoRefresh(); + } +}, [hasProcessingTasks, startAutoRefresh, stopAutoRefresh]); +``` + +## 修复效果 + +### ✅ 任务立即显示 +- 用户点击批量处理后,右侧边栏立即显示新创建的批量任务 +- 任务状态初始为 `Processing` + +### ✅ 实时状态更新 +- 任务状态、进度、已处理图片数量等信息实时更新 +- 每2秒自动刷新任务列表(仅在有处理中任务时) + +### ✅ 正确的任务生命周期 +- 任务从 `Processing` 状态开始 +- 处理完成后变为 `Completed` 或 `Failed` +- 自动停止不必要的刷新 + +### ✅ 技术要求满足 +- **API兼容性**: 保持现有的前端调用方式不变 +- **线程安全**: 使用 `Arc>` 确保多线程环境下任务状态更新的安全性 +- **错误处理**: 妥善处理任务创建、更新过程中的错误情况 +- **性能考虑**: 只在有处理中任务时才启动定时刷新,避免不必要的性能开销 + +## 测试验证 + +创建了测试脚本 `test_batch_task_display.js`,可以在浏览器控制台中运行来验证: +1. 任务立即显示功能 +2. 实时状态更新 +3. 任务生命周期管理 + +## 编译状态 +✅ Rust代码编译通过,无错误 +✅ 应用可以正常启动和运行 + +## 下一步 +建议进行完整的功能测试,包括: +1. 创建包含图片的测试文件夹 +2. 测试批量处理功能 +3. 验证任务列表实时更新 +4. 确认任务完成后状态正确 diff --git a/test_batch_task_display.js b/test_batch_task_display.js new file mode 100644 index 0000000..ef48348 --- /dev/null +++ b/test_batch_task_display.js @@ -0,0 +1,86 @@ +// 测试批量任务立即显示功能 +// 这个脚本可以在浏览器控制台中运行来测试功能 + +async function testBatchTaskDisplay() { + console.log('开始测试批量任务立即显示功能...'); + + try { + // 1. 获取初始任务列表 + console.log('1. 获取初始任务列表...'); + const initialTasks = await window.__TAURI__.core.invoke('get_all_batch_editing_tasks'); + console.log('初始批量任务数量:', initialTasks.length); + + // 2. 创建一个批量任务 + console.log('2. 创建批量任务...'); + const taskId = await window.__TAURI__.core.invoke('edit_batch_images', { + inputFolder: 'C:\\Users\\imeep\\Desktop\\test_images', + outputFolder: 'C:\\Users\\imeep\\Desktop\\test_output', + prompt: '测试提示词', + params: { + guidance_scale: 5.5, + seed: -1, + watermark: false, + response_format: 'url', + size: 'adaptive' + } + }); + console.log('创建的任务ID:', taskId); + + // 3. 立即检查任务是否出现在列表中 + console.log('3. 立即检查任务列表...'); + const tasksAfterCreate = await window.__TAURI__.core.invoke('get_all_batch_editing_tasks'); + console.log('创建后批量任务数量:', tasksAfterCreate.length); + + const newTask = tasksAfterCreate.find(task => task.id === taskId); + if (newTask) { + console.log('✅ 成功!任务立即出现在列表中'); + console.log('任务状态:', newTask.status); + console.log('任务进度:', newTask.progress); + console.log('总图片数:', newTask.total_images); + console.log('已处理图片数:', newTask.processed_images); + } else { + console.log('❌ 失败!任务没有立即出现在列表中'); + } + + // 4. 持续监控任务状态变化 + console.log('4. 开始监控任务状态变化...'); + let monitorCount = 0; + const maxMonitorCount = 10; + + const monitor = setInterval(async () => { + try { + monitorCount++; + const currentTasks = await window.__TAURI__.core.invoke('get_all_batch_editing_tasks'); + const currentTask = currentTasks.find(task => task.id === taskId); + + if (currentTask) { + console.log(`监控 ${monitorCount}: 状态=${currentTask.status}, 进度=${currentTask.progress}, 处理=${currentTask.processed_images}/${currentTask.total_images}`); + + // 如果任务完成或失败,停止监控 + if (currentTask.status === 'Completed' || currentTask.status === 'Failed') { + console.log('任务已完成,停止监控'); + clearInterval(monitor); + } + } else { + console.log(`监控 ${monitorCount}: 任务不存在`); + } + + // 最多监控10次 + if (monitorCount >= maxMonitorCount) { + console.log('达到最大监控次数,停止监控'); + clearInterval(monitor); + } + } catch (error) { + console.error('监控过程中出错:', error); + clearInterval(monitor); + } + }, 2000); // 每2秒检查一次 + + } catch (error) { + console.error('测试过程中出错:', error); + } +} + +// 运行测试 +console.log('批量任务显示测试脚本已加载'); +console.log('运行 testBatchTaskDisplay() 开始测试');