/**
* 图片生成组件
*/
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}
`;
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;