/** * 图片展示组件 */ class ImageGallery { constructor() { this.images = []; this.selectedImages = []; // 下载设置相关 this.lastSavePath = localStorage.getItem('lastSavePath') || '首次下载'; this.pendingDownload = null; // 待下载的图片数据 this.isBatchDownload = false; // 是否批量下载 this.initElements(); this.bindEvents(); } /** * 初始化DOM元素 */ initElements() { this.galleryGrid = document.getElementById('galleryGrid'); this.emptyState = document.getElementById('emptyState'); this.totalImages = document.getElementById('totalImages'); this.successImages = document.getElementById('successImages'); this.failedImages = document.getElementById('failedImages'); this.downloadAllBtn = document.getElementById('downloadAll'); // 图片预览模态框元素 this.imageModal = document.getElementById('imageModal'); this.modalImage = document.getElementById('modalImage'); this.modalClose = document.getElementById('modalClose'); this.modalDownload = document.getElementById('modalDownload'); this.modalCopy = document.getElementById('modalCopy'); // 下载设置模态框元素 this.downloadModal = document.getElementById('downloadModal'); this.downloadModalClose = document.getElementById('downloadModalClose'); this.downloadFilename = document.getElementById('downloadFilename'); this.lastSavePathEl = document.getElementById('lastSavePath'); this.downloadConfirmBtn = document.getElementById('downloadConfirmBtn'); this.downloadCancelBtn = document.getElementById('downloadCancelBtn'); this.currentModalImage = null; } /** * 绑定事件 */ bindEvents() { // 批量下载按钮 this.downloadAllBtn.addEventListener('click', () => { this.downloadAllImages(); }); // 图片预览模态框事件 this.modalClose.addEventListener('click', () => { this.closeModal(); }); this.modalDownload.addEventListener('click', () => { this.downloadModalImage(); }); this.modalCopy.addEventListener('click', () => { this.copyModalImage(); }); // 点击模态框背景关闭 this.imageModal.addEventListener('click', (e) => { if (e.target === this.imageModal) { this.closeModal(); } }); // 下载设置模态框事件 this.downloadModalClose.addEventListener('click', () => { this.closeDownloadModal(); }); this.downloadCancelBtn.addEventListener('click', () => { this.closeDownloadModal(); }); this.downloadConfirmBtn.addEventListener('click', () => { this.confirmDownload(); }); // 点击下载模态框背景关闭 this.downloadModal.addEventListener('click', (e) => { if (e.target === this.downloadModal) { this.closeDownloadModal(); } }); // ESC键关闭模态框 document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { if (this.downloadModal.style.display === 'block') { this.closeDownloadModal(); } else if (this.imageModal.style.display === 'block') { this.closeModal(); } } }); // Enter键确认下载 this.downloadFilename.addEventListener('keydown', (e) => { if (e.key === 'Enter') { this.confirmDownload(); } }); } /** * 添加图片到展示区 */ addImage(imageData) { const imageId = `image_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const imageItem = { id: imageId, index: imageData.index, status: imageData.status, data: imageData.image_data, error: imageData.error, timestamp: imageData.timestamp, batchId: imageData.batchId // 添加批次ID支持 }; this.images.push(imageItem); this.renderImage(imageItem); this.updateStats(); this.updateEmptyState(); } /** * 更新图片状态 */ updateImageStatus(index, status, data = null, error = null, batchId = null) { let image; if (batchId) { // 如果提供了批次ID,优先使用批次ID和索引进行匹配 image = this.images.find(img => img.batchId === batchId && img.index === index); } else { // 兼容旧逻辑:从后往前查找状态为generating的图片 const candidates = this.images.filter(img => img.index === index && img.status === 'generating'); image = candidates[candidates.length - 1]; // 取最后一个 } if (image) { image.status = status; if (data) image.data = data; if (error) image.error = error; this.rerenderImage(image); this.updateStats(); } } /** * 渲染单个图片 */ renderImage(imageItem) { const card = document.createElement('div'); card.className = 'image-card'; card.dataset.imageId = imageItem.id; card.innerHTML = this.getImageCardHTML(imageItem); this.galleryGrid.appendChild(card); this.bindImageEvents(card, imageItem); } /** * 重新渲染图片 */ rerenderImage(imageItem) { const card = this.galleryGrid.querySelector(`[data-image-id="${imageItem.id}"]`); if (card) { card.innerHTML = this.getImageCardHTML(imageItem); this.bindImageEvents(card, imageItem); } } /** * 获取图片卡片HTML */ getImageCardHTML(imageItem) { switch (imageItem.status) { case 'pending': return `

等待生成...

等待中
`; case 'generating': return `

正在生成...

生成中
`; case 'success': return `
生成的图片
成功
`; case 'error': return `

生成失败

${imageItem.error || '未知错误'}
失败
`; default: return `

处理中...

`; } } /** * 绑定图片事件 */ bindImageEvents(card, imageItem) { // 查看原图 const viewBtn = card.querySelector('.btn-view'); if (viewBtn) { viewBtn.addEventListener('click', () => { this.viewImage(imageItem); }); } // 下载图片 const downloadBtn = card.querySelector('.btn-download'); if (downloadBtn) { downloadBtn.addEventListener('click', () => { this.downloadImage(imageItem); }); } // 复制图片 const copyBtn = card.querySelector('.btn-copy'); if (copyBtn) { copyBtn.addEventListener('click', () => { this.copyImage(imageItem); }); } // 删除图片 const deleteBtn = card.querySelector('.btn-delete'); if (deleteBtn) { deleteBtn.addEventListener('click', () => { this.deleteImage(imageItem); }); } // 重试按钮 const retryBtn = card.querySelector('.btn-retry'); if (retryBtn) { retryBtn.addEventListener('click', () => { this.retryImage(imageItem); }); } // 双击查看原图 const img = card.querySelector('img'); if (img) { img.addEventListener('dblclick', () => { this.viewImage(imageItem); }); } } /** * 查看原图 */ viewImage(imageItem) { this.currentModalImage = imageItem; this.modalImage.src = imageItem.data; this.imageModal.style.display = 'block'; } /** * 关闭模态框 */ closeModal() { this.imageModal.style.display = 'none'; this.currentModalImage = null; } /** * 生成默认时间戳文件名 */ generateDefaultFilename() { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); const hours = String(now.getHours()).padStart(2, '0'); const minutes = String(now.getMinutes()).padStart(2, '0'); const seconds = String(now.getSeconds()).padStart(2, '0'); return `img_${year}${month}${day}_${hours}${minutes}${seconds}`; } /** * 打开下载设置对话框 */ openDownloadModal(imageData, isBatch = false) { this.pendingDownload = imageData; this.isBatchDownload = isBatch; // 设置默认文件名 this.downloadFilename.value = this.generateDefaultFilename(); // 显示上次保存路径 this.lastSavePathEl.textContent = this.lastSavePath; // 显示模态框 this.downloadModal.style.display = 'block'; // 聚焦到文件名输入框并选中内容 setTimeout(() => { this.downloadFilename.focus(); this.downloadFilename.select(); }, 100); } /** * 关闭下载设置对话框 */ closeDownloadModal() { this.downloadModal.style.display = 'none'; this.pendingDownload = null; this.isBatchDownload = false; } /** * 确认下载 */ async confirmDownload() { const filename = this.downloadFilename.value.trim(); if (!filename) { this.showToast('请输入文件名', 'warning'); return; } // 保存待下载数据(因为关闭对话框会清空) const downloadData = this.pendingDownload; const isBatch = this.isBatchDownload; // 先关闭对话框,避免阻塞系统文件选择器 this.closeDownloadModal(); if (isBatch) { // 批量下载 - 使用目录选择器 await this.executeBatchDownload(filename, downloadData); } else { // 单张下载 - 使用另存为对话框 await this.executeSingleDownload(filename, downloadData); } } /** * 执行单张图片下载(使用另存为对话框) */ async executeSingleDownload(filename, imageData) { const finalFilename = `${filename}.jpg`; console.log('开始下载单张图片:', finalFilename); console.log('图片数据存在:', !!imageData?.data); const result = await window.ImageUtils.downloadImageWithPicker(imageData.data, finalFilename); console.log('下载结果:', result); if (result.success) { // 更新上次保存路径 if (result.filename) { this.lastSavePath = `上次文件: ${result.filename}`; localStorage.setItem('lastSavePath', this.lastSavePath); } this.showToast('图片下载成功', 'success'); } else if (result.cancelled) { // 用户取消,不提示 } else { this.showToast('图片下载失败', 'error'); } } /** * 执行批量下载(使用目录选择器) */ async executeBatchDownload(filenamePrefix, images) { // 尝试使用目录选择器 const result = await window.ImageUtils.downloadMultipleWithPicker(images, filenamePrefix); if (result.success) { // 更新上次保存路径 this.lastSavePath = `上次目录: ${result.dirName}`; localStorage.setItem('lastSavePath', this.lastSavePath); this.showToast(`成功保存 ${result.count} 张图片到 ${result.dirName}`, 'success'); } else if (result.cancelled) { // 用户取消,不提示 } else if (result.notSupported) { // 浏览器不支持目录选择,回退到传统逐个下载 this.showToast('浏览器不支持选择文件夹,将逐个下载', 'warning'); await this.fallbackBatchDownload(filenamePrefix, images); } else { this.showToast('批量下载失败: ' + (result.error || '未知错误'), 'error'); } } /** * 回退的批量下载方式(逐个下载到默认下载目录) */ async fallbackBatchDownload(filenamePrefix, images) { let successCount = 0; for (let i = 0; i < images.length; i++) { const img = images[i]; const finalFilename = `${filenamePrefix}_${i + 1}.jpg`; if (window.ImageUtils.downloadImage(img.data, finalFilename)) { successCount++; } // 添加小延迟避免浏览器阻止多个下载 if (i < images.length - 1) { await new Promise(resolve => setTimeout(resolve, 500)); } } // 更新上次保存路径 this.lastSavePath = `文件名前缀: ${filenamePrefix}`; localStorage.setItem('lastSavePath', this.lastSavePath); if (successCount > 0) { this.showToast(`成功下载 ${successCount} 张图片`, 'success'); } else { this.showToast('下载失败', 'error'); } } /** * 下载图片(打开设置对话框) */ downloadImage(imageItem) { this.openDownloadModal(imageItem, false); } /** * 下载模态框中的图片 */ downloadModalImage() { if (this.currentModalImage) { this.downloadImage(this.currentModalImage); } } /** * 复制图片 */ async copyImage(imageItem) { try { const success = await window.ImageUtils.copyImageToClipboard(imageItem.data); if (success) { this.showToast('图片已复制到剪贴板', 'success'); } else { this.showToast('复制图片失败', 'error'); } } catch (error) { console.error('复制图片失败:', error); this.showToast('复制图片失败', 'error'); } } /** * 复制模态框中的图片 */ async copyModalImage() { if (this.currentModalImage) { await this.copyImage(this.currentModalImage); } } /** * 删除图片 */ deleteImage(imageItem) { if (!confirm('确定要删除这张图片吗?')) { return; } // 从数组中移除 const index = this.images.findIndex(img => img.id === imageItem.id); if (index > -1) { this.images.splice(index, 1); } // 从DOM中移除 const card = this.galleryGrid.querySelector(`[data-image-id="${imageItem.id}"]`); if (card) { card.remove(); } this.updateStats(); this.updateEmptyState(); this.showToast('图片已删除', 'success'); } /** * 重试生成图片 */ retryImage(imageItem) { // 触发重试事件,由图片生成器处理 const event = new CustomEvent('retryImage', { detail: { imageItem } }); document.dispatchEvent(event); } /** * 批量下载所有图片 */ async downloadAllImages() { const successImages = this.images.filter(img => img.status === 'success'); if (successImages.length === 0) { this.showToast('没有可下载的图片', 'warning'); return; } // 打开下载设置对话框(批量模式) this.openDownloadModal(successImages, true); } /** * 清空所有图片 */ clearAllImages() { this.images = []; this.galleryGrid.innerHTML = ''; this.updateStats(); this.updateEmptyState(); } /** * 更新统计信息 */ updateStats() { const total = this.images.length; const success = this.images.filter(img => img.status === 'success').length; const failed = this.images.filter(img => img.status === 'error').length; this.totalImages.textContent = `总计: ${total}`; this.successImages.textContent = `成功: ${success}`; this.failedImages.textContent = `失败: ${failed}`; // 更新下载按钮状态 this.downloadAllBtn.disabled = success === 0; } /** * 更新空状态显示 */ updateEmptyState() { if (this.images.length === 0) { this.emptyState.style.display = 'flex'; this.galleryGrid.style.display = 'none'; } else { this.emptyState.style.display = 'none'; this.galleryGrid.style.display = 'grid'; } } /** * 显示消息提示 */ showToast(message, type = 'success') { const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.textContent = message; const container = document.getElementById('toastContainer'); container.appendChild(toast); // 3秒后自动移除 setTimeout(() => { if (toast.parentNode) { toast.parentNode.removeChild(toast); } }, 3000); } } // 导出到全局 window.ImageGallery = ImageGallery;