/** * 图片生成组件 */ class ImageGenerator { constructor() { this.isGenerating = false; this.referenceImages = []; // 改为数组,支持多张图片 this.maxImages = 3; // 最多上传3张图片 this.initElements(); this.bindEvents(); } /** * 初始化DOM元素 */ initElements() { this.modelSelect = document.getElementById('modelSelect'); this.promptInput = document.getElementById('promptInput'); this.imageCount = document.getElementById('imageCount'); this.temperature = document.getElementById('temperature'); this.temperatureValue = document.getElementById('temperatureValue'); this.generateBtn = document.getElementById('generateBtn'); this.clearParamsBtn = document.getElementById('clearParams'); this.loadingOverlay = document.getElementById('loadingOverlay'); // 高级随机复选框 this.advancedRandomCheckbox = document.getElementById('advancedRandom'); // 文件上传相关 this.uploadArea = document.getElementById('uploadArea'); this.fileInput = document.getElementById('fileInput'); this.uploadPreviewContainer = document.getElementById('uploadPreviewContainer'); } /** * 绑定事件 */ bindEvents() { // 生成图片按钮 this.generateBtn.addEventListener('click', () => { this.generateImages(); }); // 清空参数按钮 this.clearParamsBtn.addEventListener('click', () => { this.clearParams(); }); // 温度滑块 this.temperature.addEventListener('input', (e) => { this.temperatureValue.textContent = e.target.value; }); // 文件上传区域点击 this.uploadArea.addEventListener('click', () => { this.fileInput.click(); }); // 文件拖拽 this.uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); this.uploadArea.style.borderColor = '#667eea'; this.uploadArea.style.background = '#edf2f7'; }); this.uploadArea.addEventListener('dragleave', (e) => { e.preventDefault(); this.uploadArea.style.borderColor = '#cbd5e0'; this.uploadArea.style.background = '#f7fafc'; }); this.uploadArea.addEventListener('drop', (e) => { e.preventDefault(); this.uploadArea.style.borderColor = '#cbd5e0'; this.uploadArea.style.background = '#f7fafc'; const files = e.dataTransfer.files; if (files.length > 0) { this.handleMultipleFileSelect(files); } }); // 文件选择 this.fileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { this.handleMultipleFileSelect(e.target.files); } }); // 重试图片生成事件 document.addEventListener('retryImage', (e) => { this.retryImageGeneration(e.detail.imageItem); }); // 键盘快捷键 document.addEventListener('keydown', (e) => { // Ctrl/Cmd + Enter 生成图片 if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); if (!this.isGenerating) { this.generateImages(); } } }); } /** * 处理多文件选择 */ async handleMultipleFileSelect(files) { const fileArray = Array.from(files); // 检查是否超过最大数量 const remainingSlots = this.maxImages - this.referenceImages.length; if (remainingSlots <= 0) { this.showToast(`最多只能上传${this.maxImages}张图片`, 'warning'); return; } // 只处理剩余可用数量的文件 const filesToProcess = fileArray.slice(0, remainingSlots); if (fileArray.length > remainingSlots) { this.showToast(`已达到上限,只添加了${remainingSlots}张图片`, 'warning'); } for (const file of filesToProcess) { await this.handleFileSelect(file); } } /** * 处理单个文件选择 */ async handleFileSelect(file) { // 验证文件 const validation = window.ImageUtils.validateImageFile(file); if (!validation.valid) { this.showToast(validation.error, 'error'); return; } try { // 读取文件为base64 const base64Data = await window.ImageUtils.readFileAsBase64(file); // 添加到引用图片数组 this.referenceImages.push({ data: base64Data, name: file.name }); // 更新预览 this.updatePreviewDisplay(); this.showToast(`图片上传成功 (${this.referenceImages.length}/${this.maxImages})`, 'success'); } catch (error) { console.error('文件处理失败:', error); this.showToast('文件处理失败: ' + error.message, 'error'); } } /** * 更新图片预览显示 */ updatePreviewDisplay() { // 清空预览容器 this.uploadPreviewContainer.innerHTML = ''; // 生成每张图片的预览 this.referenceImages.forEach((img, index) => { const previewItem = document.createElement('div'); previewItem.className = 'upload-preview-item'; previewItem.innerHTML = ` 预览图${index + 1} ${index + 1} `; this.uploadPreviewContainer.appendChild(previewItem); // 绑定删除事件 previewItem.querySelector('.btn-remove').addEventListener('click', (e) => { e.stopPropagation(); this.removeReferenceImage(index); }); }); // 如果有图片,显示数量提示 if (this.referenceImages.length > 0) { const countHint = document.createElement('div'); countHint.className = 'upload-count'; countHint.textContent = `已上传 ${this.referenceImages.length}/${this.maxImages} 张图片`; this.uploadPreviewContainer.appendChild(countHint); } // 根据图片数量显示/隐藏上传区域 if (this.referenceImages.length >= this.maxImages) { this.uploadArea.style.display = 'none'; } else { this.uploadArea.style.display = 'block'; } } /** * 移除参考图片 */ removeReferenceImage(index) { if (index !== undefined && index >= 0 && index < this.referenceImages.length) { this.referenceImages.splice(index, 1); } // 更新预览 this.updatePreviewDisplay(); // 清空文件输入 this.fileInput.value = ''; if (this.referenceImages.length === 0) { this.showToast('已移除所有参考图片', 'success'); } } /** * 清空所有参考图片 */ clearAllReferenceImages() { this.referenceImages = []; this.uploadPreviewContainer.innerHTML = ''; this.uploadArea.style.display = 'block'; this.fileInput.value = ''; } /** * 生成图片 */ async generateImages() { if (this.isGenerating) { return; } // 验证参数 const validation = this.validateParams(); if (!validation.valid) { this.showToast(validation.error, 'warning'); return; } try { this.setGeneratingState(true); // 准备请求参数 const params = { prompt: this.promptInput.value.trim(), count: parseInt(this.imageCount.value), model: this.modelSelect.value, temperature: parseFloat(this.temperature.value), advanced_random: this.advancedRandomCheckbox.checked }; // 如果有参考图片,添加到参数中(支持多张) if (this.referenceImages.length > 0) { params.reference_images = this.referenceImages.map(img => img.data); } // 为每张图片创建占位符,直接设置为生成中状态 const batchId = `batch_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; this.currentBatchId = batchId; // 保存当前批次ID for (let i = 0; i < params.count; i++) { window.imageGallery.addImage({ index: i, status: 'generating', timestamp: new Date().toISOString(), batchId: batchId }); } // 发送生成请求 await window.imageApi.generateImages(params, (data) => { this.handleGenerationProgress(data); }); } catch (error) { console.error('生成图片失败:', error); this.showToast('生成图片失败: ' + error.message, 'error'); } finally { this.setGeneratingState(false); } } /** * 处理生成进度 */ handleGenerationProgress(data) { if (data.status === 'completed') { // 生成完成 this.showToast(`生成完成!成功: ${data.success},失败: ${data.failed}`, 'success'); return; } if (data.index !== undefined) { // 更新特定图片的状态 if (data.status === 'success') { window.imageGallery.updateImageStatus(data.index, 'success', data.image_data, null, this.currentBatchId); } else if (data.status === 'error') { window.imageGallery.updateImageStatus(data.index, 'error', null, data.error, this.currentBatchId); } } } /** * 重试图片生成 */ async retryImageGeneration(imageItem) { if (this.isGenerating) { this.showToast('正在生成中,请稍后重试', 'warning'); return; } try { // 更新状态为生成中 window.imageGallery.updateImageStatus(imageItem.index, 'generating', null, null, imageItem.batchId); // 准备重试参数 const params = { prompt: this.promptInput.value.trim() || '生成一张图片', count: 1, model: this.modelSelect.value, temperature: parseFloat(this.temperature.value) }; if (this.referenceImages.length > 0) { params.reference_images = this.referenceImages.map(img => img.data); } // 发送单张图片生成请求 await window.imageApi.generateImages(params, (data) => { if (data.index === 0) { // 重试时只有一张图片,索引为0 if (data.status === 'success') { window.imageGallery.updateImageStatus(imageItem.index, 'success', data.image_data, null, imageItem.batchId); this.showToast('重试成功', 'success'); } else if (data.status === 'error') { window.imageGallery.updateImageStatus(imageItem.index, 'error', null, data.error, imageItem.batchId); this.showToast('重试失败: ' + data.error, 'error'); } } }); } catch (error) { console.error('重试失败:', error); window.imageGallery.updateImageStatus(imageItem.index, 'error', null, error.message, imageItem.batchId); this.showToast('重试失败: ' + error.message, 'error'); } } /** * 验证生成参数 */ validateParams() { const prompt = this.promptInput.value.trim(); const count = parseInt(this.imageCount.value); if (!prompt) { return { valid: false, error: '请输入提示词' }; } if (isNaN(count) || count < 1 || count > 10) { return { valid: false, error: '图片数量必须在1-10之间' }; } return { valid: true }; } /** * 设置生成状态 */ setGeneratingState(isGenerating) { this.isGenerating = isGenerating; if (isGenerating) { this.generateBtn.disabled = true; this.generateBtn.innerHTML = ' 生成中...'; // 移除全屏加载遮罩,只在按钮上显示状态 } else { this.generateBtn.disabled = false; this.generateBtn.innerHTML = ' 生成图片'; // 确保加载遮罩隐藏 this.loadingOverlay.style.display = 'none'; } } /** * 清空参数 */ clearParams() { if (this.isGenerating) { this.showToast('正在生成中,无法清空参数', 'warning'); return; } // 清空表单 this.promptInput.value = ''; this.imageCount.value = '1'; this.temperature.value = '0.7'; this.temperatureValue.textContent = '0.7'; this.advancedRandomCheckbox.checked = false; // 清空参考图片 this.clearAllReferenceImages(); // 清空提示词管理器 if (window.promptManager) { window.promptManager.clearPrompt(); } // 清空图片展示区 window.imageGallery.clearAllImages(); this.showToast('参数已清空', 'success'); } /** * 获取当前参数 */ getCurrentParams() { return { prompt: this.promptInput.value.trim(), count: parseInt(this.imageCount.value), model: this.modelSelect.value, temperature: parseFloat(this.temperature.value), referenceImages: this.referenceImages.map(img => img.data), advancedRandom: this.advancedRandomCheckbox.checked }; } /** * 设置参数 */ setParams(params) { if (params.prompt !== undefined) { this.promptInput.value = params.prompt; } if (params.count !== undefined) { this.imageCount.value = params.count; } if (params.model !== undefined) { this.modelSelect.value = params.model; } if (params.temperature !== undefined) { this.temperature.value = params.temperature; this.temperatureValue.textContent = params.temperature; } if (params.advancedRandom !== undefined) { this.advancedRandomCheckbox.checked = params.advancedRandom; } } /** * 显示消息提示 */ 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.ImageGenerator = ImageGenerator;