Files
dsProject/dsLightRag/static/QwenImage/qwen-image-edit.js
2025-08-27 14:05:32 +08:00

268 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Qwen Image Editor API 封装
* 参考 qwen-image.js 的实现风格
*/
class QwenImageEditor {
constructor() {
this.baseUrl = '/api/qwenImage';
this.editUrl = `${this.baseUrl}/edit`;
this.initEventListeners();
// 初始化时渲染示例
this.renderExamples();
}
/**
* 初始化事件监听
*/
initEventListeners() {
// 移除Layui依赖使用原生JS实现
document.getElementById('startEditBtn').addEventListener('click', () => this.handleEditSubmit());
// 删除JS动态创建按钮的代码
// 直接绑定静态按钮事件
document.getElementById('randomExampleBtn').addEventListener('click', () => this.handleRandomExample());
}
/**
* 处理编辑表单提交
*/
handleEditSubmit() {
const imageUrl = document.getElementById('imageUrl').value;
const prompt = document.getElementById('editPrompt').value;
const size = document.getElementById('editSize').value;
const style = document.getElementById('editStyle').value;
if (!imageUrl || !prompt) {
alert('图片URL和编辑提示词不能为空');
return;
}
const loadingIndicator = document.createElement('div');
loadingIndicator.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
loadingIndicator.innerHTML = '<i class="fa fa-spinner fa-spin text-white text-4xl"></i>';
document.body.appendChild(loadingIndicator);
// 显示原始图片
document.getElementById('originalImage').src = imageUrl;
document.getElementById('originalImage').classList.remove('hidden');
document.getElementById('originalImagePlaceholder').classList.add('hidden');
// 调用编辑接口
this.editImage(imageUrl, prompt, size, style)
.then(result => {
document.body.removeChild(loadingIndicator);
document.getElementById('editedImage').src = result.edited_image;
document.getElementById('editedImage').classList.remove('hidden');
document.getElementById('editedImagePlaceholder').classList.add('hidden');
alert('编辑成功');
})
.catch(error => {
document.body.removeChild(loadingIndicator);
alert(`编辑失败: ${error.message || '网络请求错误'}`);
});
}
/**
* 调用图片编辑接口
* @param {string} imageUrl - 原始图片URL
* @param {string} prompt - 编辑提示词
* @param {string} size - 图像尺寸
* @param {string} style - 艺术风格
* @returns {Promise<Object>} - 编辑结果
*/
async editImage(imageUrl, prompt, size, style) {
try {
const response = await axios.post(this.editUrl, {
image_url: imageUrl,
prompt: prompt,
size: size,
style: style
});
if (response.data.code !== 200) {
throw new Error(response.data.message || '编辑接口返回错误');
}
return response.data.data;
} catch (error) {
console.error('图片编辑失败:', error);
throw error.response?.data || { message: '网络请求失败,请重试' };
}
}
/**
* 渲染示例区域
*/
renderExamples() {
// 统一示例数据结构
const exampleGroups = [
{
id: 'bearExamples',
title: '小熊系列',
aspectRatio: '1/1', // 新增:小熊系列方形比例
originalImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p999719.png',
examples: [
{
prompt: '这只熊拿着五彩画板和画笔,站在画板前画画',
resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p999625.png'
},
{
prompt: '这只熊站在白色背景前,手里拿着锅铲,旁边有蔬菜和调料',
resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p999626.png'
},
{
prompt: '这只熊坐在地上,怀里抱着一把吉他,手指拨动琴弦',
resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p999628.png'
},
{
prompt: '这只熊穿着燕尾服,戴着魔术帽,手里拿着魔术棒,做出表演魔术的动作',
resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p999629.png'
},
{
prompt: '这只熊穿着运动服,手里拿着篮球,单腿弯曲',
resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p999630.png'
},
{
prompt: '这只熊戴着草帽,手里拿着花洒和小铲子,正在浇水或种植植物',
resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p999631.png'
},
// 添加缺失的5个示例
{prompt: '这只熊穿着宇航服,伸出手指向远方', resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p999632.png'},
{prompt: '这只熊穿着华丽的舞裙,双臂展开,做出优雅的舞蹈动作', resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p999633.png'},
{prompt: '把背景改成草原', resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p1000045.png'},
{prompt: '给它戴上红色帽子和黑色墨镜', resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p1000051.png'},
{prompt: '改成梵高油画风格', resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p1000061.png'}
]
},
{
id: 'hanfuExamples',
title: '霓裳汉服社系列',
aspectRatio: '5/7', // 新增汉服社系列5:7比例
originalImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p1000025.png',
examples: [
{
prompt: '把“霓裳汉服社”改成“通义实验室”',
resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p1000026.png'
},
{
prompt: '把“活动全程免费”改为“只给学生打折”',
resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p1000027.png'
},
{
prompt: '把女人改成穿汉服的男人',
resultImage: 'https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7219965571/p1000028.png'
}
]
}
];
// 新增:保存示例数据到实例属性
this.exampleGroups = exampleGroups;
this.allExamples = [];
// 渲染每个系列的示例并收集所有示例
exampleGroups.forEach(group => {
const container = document.getElementById(group.id);
if (!container) return;
container.innerHTML = '';
group.examples.forEach(example => {
// 收集所有示例用于随机选择
this.allExamples.push({
originalImage: group.originalImage,
prompt: example.prompt,
resultImage: example.resultImage
});
container.appendChild(this.createExampleCard(
group.originalImage,
example.prompt,
example.resultImage,
group.aspectRatio
));
});
});
// 新增:自动选择小熊系列第一个示例
setTimeout(() => {
const bearContainer = document.getElementById('bearExamples');
if (bearContainer && bearContainer.firstElementChild) {
bearContainer.firstElementChild.click();
}
}, 100);
}
/**
* 创建示例卡片
* @param {string} originalImage - 原始图片URL
* @param {string} prompt - 编辑提示词
* @param {string} resultImage - 结果图片URL
* @param {string} aspectRatio - 宽高比(新增参数)
* @returns {HTMLElement} - 卡片元素
*/
createExampleCard(originalImage, prompt, resultImage, aspectRatio) {
const card = document.createElement('div');
card.className = 'example-card bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden transition-all duration-300 hover:shadow-xl cursor-pointer';
card.innerHTML = `
<div class="overflow-hidden bg-gray-100 dark:bg-gray-900 aspect-[${aspectRatio}]">
<img src="${resultImage}" alt="${prompt.substring(0, 20)}..." class="w-full h-full object-cover transition-transform duration-500 hover:scale-110">
</div>
<div class="p-4">
<p class="text-sm text-gray-700 dark:text-gray-300 line-clamp-2">${prompt}</p>
</div>
`;
// 点击事件 - 加载原图和填充表单
card.addEventListener('click', () => {
// 填充表单
document.getElementById('imageUrl').value = originalImage;
document.getElementById('editPrompt').value = prompt;
// 更新原始图片预览
const originalImageElement = document.getElementById('originalImage');
const originalImagePlaceholder = document.getElementById('originalImagePlaceholder');
originalImageElement.src = originalImage;
originalImageElement.classList.remove('hidden');
originalImagePlaceholder.classList.add('hidden');
// 添加选中效果
document.querySelectorAll('.example-card').forEach(c => c.classList.remove('ring-2', 'ring-primary'));
card.classList.add('ring-2', 'ring-primary');
});
return card;
}
/**
* 随机选择一个示例并填充表单
*/
handleRandomExample() {
if (!this.allExamples || this.allExamples.length === 0) {
alert('没有可用的示例数据');
return;
}
// 随机选择一个示例
const randomIndex = Math.floor(Math.random() * this.allExamples.length);
const randomExample = this.allExamples[randomIndex];
// 填充表单数据
document.getElementById('imageUrl').value = randomExample.originalImage;
document.getElementById('editPrompt').value = randomExample.prompt;
// 更新原始图片预览
const originalImage = document.getElementById('originalImage');
const originalPlaceholder = document.getElementById('originalImagePlaceholder');
originalImage.src = randomExample.originalImage;
originalImage.classList.remove('hidden');
originalPlaceholder.classList.add('hidden');
// 移除所有卡片选中状态
document.querySelectorAll('.example-card').forEach(card => {
card.classList.remove('ring-2', 'ring-primary');
});
}
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => new QwenImageEditor());