-
+
+
+
+
+
![查看图像]()
+
+
+
diff --git a/script.js b/script.js
index bb69957..7a79e87 100644
--- a/script.js
+++ b/script.js
@@ -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);
}
};
\ No newline at end of file
diff --git a/styles.css b/styles.css
index 5568b01..1a0587c 100644
--- a/styles.css
+++ b/styles.css
@@ -404,7 +404,8 @@ body {
/* 图像查看模态框样式 */
#viewerModalImage {
- max-height: 80vh;
+ max-height: 85vh;
+ max-width: 95vw;
object-fit: contain;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
@@ -412,7 +413,63 @@ body {
}
#viewerModalImage:hover {
- transform: scale(1.02);
+ //transform: scale(1.02);
+}
+
+/* 全屏模态框样式 */
+.modal-fullscreen .modal-dialog {
+ max-width: 100%;
+ margin: 0;
+ height: 100vh;
+}
+
+.modal-fullscreen .modal-content {
+ height: 100%;
+ border: none;
+ border-radius: 0;
+}
+
+.modal-fullscreen .modal-body {
+ height: calc(100% - 120px);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #000;
+}
+
+/* 翻页按钮样式 */
+#prevImageBtn, #nextImageBtn {
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ opacity: 0.7;
+ transition: opacity 0.3s ease;
+ z-index: 10;
+}
+
+#prevImageBtn:hover, #nextImageBtn:hover {
+ opacity: 1;
+}
+
+#prevImageBtn {
+ left: 20px;
+}
+
+#nextImageBtn {
+ right: 20px;
+}
+
+/* 暗色背景下的按钮样式 */
+.modal-fullscreen .modal-body .btn-light {
+ background-color: rgba(255, 255, 255, 0.2);
+ border-color: rgba(255, 255, 255, 0.3);
+ color: white;
+}
+
+.modal-fullscreen .modal-body .btn-light:hover {
+ background-color: rgba(255, 255, 255, 0.3);
+ border-color: rgba(255, 255, 255, 0.5);
+ color: white;
}
/* 图像查看模态框特定样式 */