feat: 增强图像查看器功能并添加“全部下载”按钮在imageViewerModal中添加了全屏查看、左右翻页和删除图像功能。在index.html中新增了downloadAllImagesBtn按钮,用于一键下载所有生成的图像。script.js中相应实现了这些新功能的逻辑。

This commit is contained in:
2025-08-29 13:51:49 +08:00
parent 8cfa2a8ac9
commit db6d9efd95
3 changed files with 384 additions and 49 deletions

345
script.js
View File

@@ -11,6 +11,8 @@ let currentSettings = {
timeout: 600,
proxy: ''
};
let currentImageIndex = 0; // 当前查看的图像索引
let currentModalInstance = null; // 当前模态框实例
// 配置常量
const CONFIG = {
@@ -112,7 +114,7 @@ const utils = {
await indexedDBStorage.cleanupOldMessages();
await indexedDBStorage.cleanupOldImages();
console.log('IndexedDB 存储空间检查和清理完成');
//console.log('IndexedDB 存储空间检查和清理完成');
} catch (error) {
console.error('存储空间检查失败:', error);
}
@@ -120,9 +122,9 @@ const utils = {
// 检查存储状态 - 调试用
checkStorageStatus: async function() {
console.log('=== 存储状态检查 ===');
console.log('IndexedDB 支持:', indexedDBStorage.isSupported);
console.log('IndexedDB 数据库:', indexedDBStorage.db);
//console.log('=== 存储状态检查 ===');
//console.log('IndexedDB 支持:', indexedDBStorage.isSupported);
//console.log('IndexedDB 数据库:', indexedDBStorage.db);
if (indexedDBStorage.isSupported) {
try {
@@ -130,17 +132,17 @@ const utils = {
const chatHistory = await indexedDBStorage.getChatHistory();
const images = await indexedDBStorage.getGeneratedImages();
console.log('IndexedDB 数据统计:');
console.log(`- 设置: ${settings ? '已保存' : '未保存'}`);
console.log(`- 聊天记录: ${chatHistory.length}`);
console.log(`- 生成图像: ${images.length}`);
//console.log('IndexedDB 数据统计:');
//console.log(`- 设置: ${settings ? '已保存' : '未保存'}`);
//console.log(`- 聊天记录: ${chatHistory.length} 条`);
//console.log(`- 生成图像: ${images.length} 个`);
chatHistory.forEach((msg, idx) => {
console.log(` 聊天 ${idx}: ${msg.role} - ${msg.content.substring(0, 50)}...`);
//console.log(` 聊天 ${idx}: ${msg.role} - ${msg.content.substring(0, 50)}...`);
});
images.forEach((img, idx) => {
console.log(` 图像 ${idx}: URL长度 ${img.url ? img.url.length : 0}`);
//console.log(` 图像 ${idx}: URL长度 ${img.url ? img.url.length : 0}`);
});
} catch (error) {
console.error('读取 IndexedDB 数据失败:', error);
@@ -150,24 +152,24 @@ const utils = {
// 检查事件绑定 - 调试用
checkEventBindings: function() {
console.log('=== 事件绑定检查 ===');
//console.log('=== 事件绑定检查 ===');
const containers = document.querySelectorAll('.image-clickable-container');
console.log(`找到 ${containers.length} 个可点击图像容器`);
//console.log(`找到 ${containers.length} 个可点击图像容器`);
containers.forEach((container, idx) => {
const img = container.querySelector('img');
const tooltip = container.querySelector('.image-preview-tooltip');
console.log(`容器 ${idx}:`, {
src: img ? img.src.substring(0, 50) + '...' : '无图片',
hasTooltip: !!tooltip,
tooltipText: tooltip ? tooltip.textContent : '无',
classList: container.classList.toString()
});
// console.log(`容器 ${idx}:`, {
// src: img ? img.src.substring(0, 50) + '...' : '无图片',
// hasTooltip: !!tooltip,
// tooltipText: tooltip ? tooltip.textContent : '无',
// classList: container.classList.toString()
// });
});
// 测试点击第一个容器
if (containers.length > 0) {
console.log('尝试点击第一个图像容器...');
//console.log('尝试点击第一个图像容器...');
containers[0].click();
}
}
@@ -206,13 +208,13 @@ const indexedDBStorage = {
request.onsuccess = (event) => {
this.db = event.target.result;
this.isSupported = true;
console.log('IndexedDB 初始化成功');
//console.log('IndexedDB 初始化成功');
resolve(true);
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
console.log('正在创建/升级数据库结构...');
//console.log('正在创建/升级数据库结构...');
// 创建设置存储
if (!db.objectStoreNames.contains(CONFIG.INDEXED_DB.STORES.SETTINGS)) {
@@ -513,14 +515,14 @@ const indexedDBStorage = {
}
try {
console.log('开始从 localStorage 迁移数据到 IndexedDB...');
//console.log('开始从 localStorage 迁移数据到 IndexedDB...');
// 迁移设置
const savedSettings = localStorage.getItem(CONFIG.STORAGE_KEYS.SETTINGS);
if (savedSettings) {
const settings = JSON.parse(savedSettings);
await this.saveSettings(settings);
console.log('设置已迁移到 IndexedDB');
//console.log('设置已迁移到 IndexedDB');
}
// 迁移聊天历史
@@ -530,7 +532,7 @@ const indexedDBStorage = {
for (const msg of messages) {
await this.addChatMessage(msg.role, msg.content);
}
console.log(`已迁移 ${messages.length} 条聊天记录到 IndexedDB`);
//console.log(`已迁移 ${messages.length} 条聊天记录到 IndexedDB`);
}
// 迁移图像记录
@@ -540,15 +542,56 @@ const indexedDBStorage = {
for (const img of images) {
await this.addGeneratedImage(img.id, img.url);
}
console.log(`已迁移 ${images.length} 条图像记录到 IndexedDB`);
//console.log(`已迁移 ${images.length} 条图像记录到 IndexedDB`);
}
console.log('数据迁移完成');
//console.log('数据迁移完成');
return true;
} catch (error) {
console.error('数据迁移失败:', error);
return false;
}
},
// 删除生成的图像记录
deleteGeneratedImage: async function(imageId) {
if (!this.isSupported) {
return false;
}
try {
const transaction = this.db.transaction([CONFIG.INDEXED_DB.STORES.GENERATED_IMAGES], 'readwrite');
const store = transaction.objectStore(CONFIG.INDEXED_DB.STORES.GENERATED_IMAGES);
return new Promise((resolve, reject) => {
// 遍历所有记录查找匹配的imageId
const request = store.openCursor();
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
const record = cursor.value;
if (record.imageId === imageId) {
// 找到匹配的记录,删除它
const deleteRequest = cursor.delete();
deleteRequest.onsuccess = () => resolve(true);
deleteRequest.onerror = () => reject(deleteRequest.error);
} else {
// 继续查找下一条记录
cursor.continue();
}
} else {
// 遍历完成但没有找到记录
resolve(false);
}
};
request.onerror = () => reject(request.error);
});
} catch (error) {
console.error('IndexedDB 删除图像记录失败:', error);
return false;
}
}
};
@@ -769,19 +812,30 @@ const uiController = {
}
if (removeBtn) {
removeBtn.addEventListener('click', (e) => {
removeBtn.addEventListener('click', async (e) => {
e.stopPropagation();
app.removeGeneratedImage(imageId);
await app.removeGeneratedImage(imageId);
});
}
// 添加到内存中的图像记录
generatedImages.push({ id: imageId, url: imageUrl });
// 显示"全部下载"按钮
this.updateDownloadAllButtonVisibility();
// 保存到存储 - 优先使用 IndexedDB
await this.saveGeneratedImages(imageId, imageUrl);
},
// 更新"全部下载"按钮的可见性
updateDownloadAllButtonVisibility: function() {
const downloadAllBtn = document.getElementById('downloadAllImagesBtn');
if (downloadAllBtn) {
downloadAllBtn.style.display = generatedImages.length > 0 ? 'block' : 'none';
}
},
// 添加占位图像
addImagePlaceholder: function(index, total) {
const gallery = document.getElementById('imageGallery');
@@ -864,15 +918,18 @@ const uiController = {
}
if (removeBtn) {
removeBtn.addEventListener('click', (e) => {
removeBtn.addEventListener('click', async (e) => {
e.stopPropagation();
app.removeGeneratedImage(imageId);
await app.removeGeneratedImage(imageId);
});
}
// 添加到内存中的图像记录
generatedImages.push({ id: imageId, url: imageUrl });
// 显示"全部下载"按钮
this.updateDownloadAllButtonVisibility();
// 保存到存储 - 优先使用 IndexedDB
this.saveGeneratedImages(imageId, imageUrl);
},
@@ -897,7 +954,7 @@ const uiController = {
// 新的查看大图功能
viewLargeImage: function(imageUrl) {
viewLargeImage: async function(imageUrl) {
try {
const modalElement = document.getElementById('imageViewerModal');
if (!modalElement) {
@@ -913,12 +970,18 @@ const uiController = {
const modalImage = document.getElementById('viewerModalImage');
const downloadButton = document.getElementById('viewerDownloadImage');
const deleteButton = document.getElementById('viewerDeleteImage');
const prevButton = document.getElementById('prevImageBtn');
const nextButton = document.getElementById('nextImageBtn');
if (!modalImage) {
utils.showNotification('无法打开图像查看:找不到图像元素', 'danger');
return;
}
// 找到当前图像在generatedImages中的索引
currentImageIndex = generatedImages.findIndex(img => img.url === imageUrl);
// 设置图像源
modalImage.src = imageUrl;
modalImage.onerror = function() {
@@ -932,9 +995,140 @@ const uiController = {
};
}
// 设置删除按钮功能
if (deleteButton) {
deleteButton.onclick = function() {
// 保存当前图像ID
const currentImageId = generatedImages[currentImageIndex].id;
// 删除当前图像
app.removeGeneratedImage(currentImageId).then(() => {
// 检查删除后是否还有图像
if (generatedImages.length === 0) {
// 没有图像了,关闭模态框
currentModalInstance.hide();
} else {
// 更新当前图像索引
if (currentImageIndex >= generatedImages.length) {
// 如果删除的是最后一张,显示上一张
currentImageIndex = generatedImages.length - 1;
}
// 如果索引仍然有效,显示图像
if (currentImageIndex >= 0 && currentImageIndex < generatedImages.length) {
modalImage.src = generatedImages[currentImageIndex].url;
// 更新删除按钮对应的图像ID
deleteButton.onclick = function() {
const newCurrentImageId = generatedImages[currentImageIndex].id;
app.removeGeneratedImage(newCurrentImageId).then(() => {
// 再次检查删除后是否还有图像
if (generatedImages.length === 0) {
currentModalInstance.hide();
} else {
// 更新当前图像索引
if (currentImageIndex >= generatedImages.length) {
currentImageIndex = generatedImages.length - 1;
}
// 如果索引仍然有效,显示图像
if (currentImageIndex >= 0 && currentImageIndex < generatedImages.length) {
modalImage.src = generatedImages[currentImageIndex].url;
// 更新删除按钮对应的图像ID
deleteButton.onclick = arguments.callee;
}
}
});
};
}
// 更新翻页按钮状态
uiController.updateNavigationButtons();
}
});
};
}
// 设置翻页按钮功能
if (prevButton) {
prevButton.onclick = function() {
if (currentImageIndex > 0) {
currentImageIndex--;
modalImage.src = generatedImages[currentImageIndex].url;
// 更新删除按钮对应的图像ID
deleteButton.onclick =async function() {
// 保存当前图像ID
const currentImageId = generatedImages[currentImageIndex].id;
// 删除当前图像
await app.removeGeneratedImage(currentImageId);
// 检查删除后是否还有图像
if (generatedImages.length === 0) {
// 没有图像了,关闭模态框
currentModalInstance.hide();
} else {
// 更新当前图像索引
if (currentImageIndex >= generatedImages.length) {
// 如果删除的是最后一张,显示上一张
currentImageIndex = generatedImages.length - 1;
}
// 如果索引仍然有效,显示图像
if (currentImageIndex >= 0 && currentImageIndex < generatedImages.length) {
modalImage.src = generatedImages[currentImageIndex].url;
// 更新删除按钮对应的图像ID
deleteButton.onclick = arguments.callee;
}
// 更新翻页按钮状态
uiController.updateNavigationButtons();
}
};
}
};
}
if (nextButton) {
nextButton.onclick = function() {
if (currentImageIndex < generatedImages.length - 1) {
currentImageIndex++;
modalImage.src = generatedImages[currentImageIndex].url;
// 更新删除按钮对应的图像ID
deleteButton.onclick = function() {
// 保存当前图像ID
const currentImageId = generatedImages[currentImageIndex].id;
// 删除当前图像
app.removeGeneratedImage(currentImageId).then(() => {
// 检查删除后是否还有图像
if (generatedImages.length === 0) {
// 没有图像了,关闭模态框
currentModalInstance.hide();
} else {
// 更新当前图像索引
if (currentImageIndex >= generatedImages.length) {
// 如果删除的是最后一张,显示上一张
currentImageIndex = generatedImages.length - 1;
}
// 如果索引仍然有效,显示图像
if (currentImageIndex >= 0 && currentImageIndex < generatedImages.length) {
modalImage.src = generatedImages[currentImageIndex].url;
// 更新删除按钮对应的图像ID
deleteButton.onclick = arguments.callee;
}
// 更新翻页按钮状态
uiController.updateNavigationButtons();
}
});
};
}
};
}
// 更新翻页按钮状态
this.updateNavigationButtons();
// 创建并显示模态框
const modal = new bootstrap.Modal(modalElement);
modal.show();
currentModalInstance = new bootstrap.Modal(modalElement);
currentModalInstance.show();
} catch (error) {
utils.showNotification(`无法打开图像查看: ${error.message}`, 'danger');
@@ -948,6 +1142,20 @@ const uiController = {
}
},
// 更新翻页按钮状态
updateNavigationButtons: function() {
const prevButton = document.getElementById('prevImageBtn');
const nextButton = document.getElementById('nextImageBtn');
if (prevButton) {
prevButton.style.display = currentImageIndex > 0 ? 'block' : 'none';
}
if (nextButton) {
nextButton.style.display = currentImageIndex < generatedImages.length - 1 ? 'block' : 'none';
}
},
// 显示图像预览
displayImagePreview: function(imageData) {
const preview = document.getElementById('imagePreview');
@@ -1113,9 +1321,9 @@ const uiController = {
}
if (removeBtn) {
removeBtn.addEventListener('click', (e) => {
removeBtn.addEventListener('click', async (e) => {
e.stopPropagation();
app.removeGeneratedImage(img.id);
await app.removeGeneratedImage(img.id);
});
}
});
@@ -1125,7 +1333,7 @@ const uiController = {
},
// 移除生成的图像
removeGeneratedImage: function(imageId) {
removeGeneratedImage: async function(imageId) {
// 从内存中移除
generatedImages = generatedImages.filter(img => img.id !== imageId);
@@ -1139,7 +1347,17 @@ const uiController = {
}
});
// 不需要再更新存储,因为 IndexedDB 会自动管理
// IndexedDB中移除
if (indexedDBStorage.isSupported) {
try {
await indexedDBStorage.deleteGeneratedImage(imageId);
} catch (error) {
console.error('从IndexedDB删除图像记录失败:', error);
}
}
// 更新"全部下载"按钮的可见性
this.updateDownloadAllButtonVisibility();
},
// 保存设置
@@ -1233,10 +1451,10 @@ const app = {
// 初始化 IndexedDB
try {
console.log('正在初始化 IndexedDB...');
//console.log('正在初始化 IndexedDB...');
const indexedDBSupported = await indexedDBStorage.init();
if (indexedDBSupported) {
console.log('IndexedDB 初始化成功,将优先使用 IndexedDB 存储');
//console.log('IndexedDB 初始化成功,将优先使用 IndexedDB 存储');
// 尝试迁移数据
await indexedDBStorage.migrateFromLocalStorage();
} else {
@@ -1250,6 +1468,10 @@ const app = {
await utils.checkAndCleanStorage();
await utils.checkStorageStatus();
await uiController.loadSettings();
// 初始化"全部下载"按钮的可见性
uiController.updateDownloadAllButtonVisibility();
console.log('OpenRouter Image Generator initialized');
},
@@ -1274,6 +1496,14 @@ const app = {
}
});
// 全部下载按钮事件
const downloadAllBtn = document.getElementById('downloadAllImagesBtn');
if (downloadAllBtn) {
downloadAllBtn.addEventListener('click', function() {
app.downloadAllImages();
});
}
// 设置变化时自动保存
const inputs = ['apiKey', 'baseUrl', 'model', 'timeout', 'proxy'];
inputs.forEach(id => {
@@ -1524,8 +1754,8 @@ const app = {
},
// 移除生成的图像
removeGeneratedImage: function(imageId) {
uiController.removeGeneratedImage(imageId);
removeGeneratedImage: async function(imageId) {
await uiController.removeGeneratedImage(imageId);
},
// 下载图像
@@ -1536,6 +1766,34 @@ const app = {
link.click();
},
// 全部下载图像
downloadAllImages: function() {
if (generatedImages.length === 0) {
utils.showNotification('没有可下载的图像', 'warning');
return;
}
// 创建一个临时通知
utils.showNotification(`正在下载 ${generatedImages.length} 张图像...`, 'info');
// 逐个下载图像
generatedImages.forEach((img, index) => {
setTimeout(() => {
const link = document.createElement('a');
link.href = img.url;
link.download = `generated-image-${Date.now()}-${index + 1}.png`;
link.click();
// 最后一个图像下载完成后显示通知
if (index === generatedImages.length - 1) {
setTimeout(() => {
utils.showNotification(`已成功下载 ${generatedImages.length} 张图像`, 'success');
}, 500);
}
}, index * 200); // 每个图像下载间隔200ms避免浏览器阻止多个下载
});
},
// 复制图像URL
copyImageUrl: function(imageUrl) {
navigator.clipboard.writeText(imageUrl).then(() => {
@@ -1559,7 +1817,7 @@ const app = {
// 清除 IndexedDB 数据
const cleared = await indexedDBStorage.clearAllData();
if (cleared) {
console.log('IndexedDB 数据已清除');
//console.log('IndexedDB 数据已清除');
} else {
console.error('IndexedDB 清除失败');
}
@@ -1594,6 +1852,7 @@ window.testConnection = () => app.testConnection();
window.sendMessage = () => app.sendMessage();
window.sendBatchMessage = () => app.sendBatchMessage();
window.downloadImage = (url) => app.downloadImage(url);
window.downloadAllImages = () => app.downloadAllImages();
window.copyImageUrl = (url) => app.copyImageUrl(url);
window.removeImage = (id) => app.removeImage(id);
window.removeGeneratedImage = (id) => app.removeGeneratedImage(id);
@@ -1604,6 +1863,6 @@ window.checkEventBindings = () => utils.checkEventBindings();
window.clearIndexedDB = async () => {
if (indexedDBStorage.isSupported) {
const cleared = await indexedDBStorage.clearAllData();
console.log('IndexedDB 已清空:', cleared);
//console.log('IndexedDB 已清空:', cleared);
}
};