improve ui
This commit is contained in:
@@ -110,6 +110,7 @@ struct ZcaResult {
|
||||
#[derive(serde::Deserialize)]
|
||||
struct ExportImageTask {
|
||||
path: String,
|
||||
output_filename: Option<String>,
|
||||
manual_position: Option<ManualPosition>,
|
||||
scale: Option<f64>,
|
||||
opacity: Option<f64>,
|
||||
@@ -251,7 +252,12 @@ async fn export_batch(images: Vec<ExportImageTask>, watermark: WatermarkSettings
|
||||
} // END IF MODE == ADD
|
||||
|
||||
// Save
|
||||
let file_name = input_path.file_name().unwrap_or_default();
|
||||
// Prioritize explicitly provided output filename (from original path), fall back to input filename
|
||||
let file_name = match &task.output_filename {
|
||||
Some(name) => std::ffi::OsStr::new(name),
|
||||
None => input_path.file_name().unwrap_or_default()
|
||||
};
|
||||
let output_path = Path::new(&output_dir).join(file_name);
|
||||
let output_path = Path::new(&output_dir).join(file_name);
|
||||
|
||||
// Handle format specific saving
|
||||
|
||||
21
src/App.vue
21
src/App.vue
@@ -50,13 +50,20 @@ async function exportBatch() {
|
||||
isExporting.value = true;
|
||||
|
||||
// Map images to include manual settings
|
||||
const exportTasks = store.images.map(img => ({
|
||||
path: img.path,
|
||||
manual_position: img.manualPosition || null,
|
||||
scale: img.scale || null,
|
||||
opacity: img.opacity || null,
|
||||
color: img.color || null
|
||||
}));
|
||||
const exportTasks = store.images.map(img => {
|
||||
// Extract filename from originalPath to ensure export uses original name
|
||||
// Handles both Windows (\) and Unix (/) separators
|
||||
const originalName = img.originalPath.split(/[/\\]/).pop() || "image.png";
|
||||
|
||||
return {
|
||||
path: img.path,
|
||||
output_filename: originalName,
|
||||
manual_position: img.manualPosition || null,
|
||||
scale: img.scale || null,
|
||||
opacity: img.opacity || null,
|
||||
color: img.color || null
|
||||
};
|
||||
});
|
||||
|
||||
// Pass dummy globals for rust struct compatibility
|
||||
// The backend struct fields are named _manual_override and _manual_position
|
||||
|
||||
@@ -232,7 +232,12 @@ const applyAll = () => {
|
||||
|
||||
<div class="p-3 bg-red-900/20 border border-red-900/50 rounded text-xs text-red-200 flex flex-col gap-1">
|
||||
<span>AI 修复功能已就绪。</span>
|
||||
<span v-if="store.isProcessing" class="text-yellow-300 animate-pulse">正在处理中,请稍候...</span>
|
||||
<span v-if="store.isProcessing" class="text-yellow-300 animate-pulse">
|
||||
正在处理: {{ store.progress.current }} / {{ store.progress.total }}
|
||||
</span>
|
||||
<span v-if="store.isDetecting" class="text-blue-300 animate-pulse">
|
||||
正在检测: {{ store.progress.current }} / {{ store.progress.total }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Execution Controls -->
|
||||
|
||||
@@ -54,6 +54,7 @@ export const useGalleryStore = defineStore("gallery", () => {
|
||||
|
||||
const isDetecting = ref(false);
|
||||
const isProcessing = ref(false);
|
||||
const progress = ref({ current: 0, total: 0 });
|
||||
|
||||
const selectedImage = computed(() => {
|
||||
if (selectedIndex.value >= 0 && selectedIndex.value < images.value.length) {
|
||||
@@ -136,8 +137,10 @@ export const useGalleryStore = defineStore("gallery", () => {
|
||||
async function detectCurrentWatermark() {
|
||||
if (selectedIndex.value < 0 || !images.value[selectedIndex.value]) return;
|
||||
isDetecting.value = true;
|
||||
progress.value = { current: 0, total: 1 };
|
||||
try {
|
||||
await detectWatermarkForImage(images.value[selectedIndex.value]);
|
||||
progress.value.current = 1;
|
||||
} finally {
|
||||
isDetecting.value = false;
|
||||
}
|
||||
@@ -146,11 +149,15 @@ export const useGalleryStore = defineStore("gallery", () => {
|
||||
async function detectAllWatermarks() {
|
||||
if (images.value.length === 0) return;
|
||||
isDetecting.value = true;
|
||||
progress.value = { current: 0, total: images.value.length };
|
||||
|
||||
try {
|
||||
const batchSize = 5;
|
||||
for (let i = 0; i < images.value.length; i += batchSize) {
|
||||
const batch = images.value.slice(i, i + batchSize).map(img => detectWatermarkForImage(img));
|
||||
const batch = images.value.slice(i, i + batchSize).map(async (img) => {
|
||||
await detectWatermarkForImage(img);
|
||||
progress.value.current++;
|
||||
});
|
||||
await Promise.all(batch);
|
||||
}
|
||||
} finally {
|
||||
@@ -211,8 +218,10 @@ export const useGalleryStore = defineStore("gallery", () => {
|
||||
if (!img) return;
|
||||
|
||||
isProcessing.value = true;
|
||||
progress.value = { current: 0, total: 1 };
|
||||
try {
|
||||
await runInpaintingForImage(img);
|
||||
progress.value.current = 1;
|
||||
} catch (e) {
|
||||
alert("处理失败: " + e);
|
||||
} finally {
|
||||
@@ -225,10 +234,12 @@ export const useGalleryStore = defineStore("gallery", () => {
|
||||
if (candidates.length === 0) return;
|
||||
|
||||
isProcessing.value = true;
|
||||
progress.value = { current: 0, total: candidates.length };
|
||||
try {
|
||||
// Sequential processing to avoid freezing UI or overloading backend
|
||||
for (const img of candidates) {
|
||||
await runInpaintingForImage(img);
|
||||
progress.value.current++;
|
||||
}
|
||||
alert("批量处理完成!");
|
||||
} catch (e) {
|
||||
@@ -285,6 +296,7 @@ export const useGalleryStore = defineStore("gallery", () => {
|
||||
brushSettings,
|
||||
isDetecting,
|
||||
isProcessing,
|
||||
progress,
|
||||
setImages,
|
||||
selectImage,
|
||||
updateWatermarkSettings,
|
||||
|
||||
Reference in New Issue
Block a user