Spaces:
Running
Running
document.addEventListener('DOMContentLoaded', function() { | |
// 添加基础URL配置 | |
const BASE_URL = 'https://jasonnoy-boss-translator.hf.space/'; // 空字符串表示使用相对路径 | |
// 动态文案循环显示 - 打字机效果 | |
const dynamicText = document.getElementById('dynamic-text'); | |
if (dynamicText) { | |
const texts = ['"丑"', '"不高级"', '"再试试"']; | |
let currentIndex = 0; | |
let isDeleting = false; | |
let charIndex = 0; | |
let typingSpeed = 150; // 打字速度 | |
let pauseTime = 2000; // 完成后暂停时间,调整为2秒 | |
function typeEffect() { | |
const currentText = texts[currentIndex]; | |
if (!isDeleting) { | |
// 打字阶段 | |
if (charIndex < currentText.length) { | |
dynamicText.textContent = currentText.substring(0, charIndex + 1); | |
charIndex++; | |
setTimeout(typeEffect, typingSpeed); | |
} else { | |
// 完成打字,等待删除 | |
setTimeout(() => { | |
isDeleting = true; | |
typeEffect(); | |
}, pauseTime); | |
} | |
} else { | |
// 删除阶段 | |
if (charIndex > 0) { | |
dynamicText.textContent = currentText.substring(0, charIndex); | |
charIndex--; | |
setTimeout(typeEffect, typingSpeed / 2); | |
} else { | |
// 完成删除,切换到下一个文案 | |
isDeleting = false; | |
currentIndex = (currentIndex + 1) % texts.length; | |
setTimeout(typeEffect, 500); | |
} | |
} | |
} | |
// 开始打字机效果 | |
typeEffect(); | |
} | |
// 添加花朵的微小浮动动画 | |
const flowerImg = document.querySelector('.flower-img'); | |
if (flowerImg) { | |
flowerImg.style.animation = 'floatFlower 4s ease-in-out infinite alternate'; | |
} | |
// 添加浮动动画的CSS | |
const style = document.createElement('style'); | |
style.textContent = ` | |
@keyframes floatFlower { | |
0% { | |
transform: translateY(0) rotate(0); | |
} | |
100% { | |
transform: translateY(-15px) rotate(2deg); | |
} | |
} | |
`; | |
document.head.appendChild(style); | |
// 图片上传处理 | |
const imageUploadArea = document.getElementById('image-upload'); | |
const imageInput = document.getElementById('image-input'); | |
if (imageUploadArea && imageInput) { | |
imageUploadArea.addEventListener('click', function() { | |
imageInput.click(); | |
}); | |
imageInput.addEventListener('change', function() { | |
if (this.files && this.files[0]) { | |
const reader = new FileReader(); | |
reader.onload = function(e) { | |
// 移除上传按钮 | |
while (imageUploadArea.firstChild) { | |
imageUploadArea.removeChild(imageUploadArea.firstChild); | |
} | |
// 添加预览图片 | |
const img = document.createElement('img'); | |
img.src = e.target.result; | |
imageUploadArea.appendChild(img); | |
imageUploadArea.classList.add('with-image'); | |
// 存储图片数据到 sessionStorage 以便在反馈页面使用 | |
sessionStorage.setItem('uploadedImage', e.target.result); | |
}; | |
reader.readAsDataURL(this.files[0]); | |
} | |
}); | |
// 拖放上传功能 | |
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { | |
imageUploadArea.addEventListener(eventName, preventDefaults, false); | |
}); | |
function preventDefaults(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
} | |
['dragenter', 'dragover'].forEach(eventName => { | |
imageUploadArea.addEventListener(eventName, function() { | |
imageUploadArea.classList.add('highlight'); | |
}, false); | |
}); | |
['dragleave', 'drop'].forEach(eventName => { | |
imageUploadArea.addEventListener(eventName, function() { | |
imageUploadArea.classList.remove('highlight'); | |
}, false); | |
}); | |
imageUploadArea.addEventListener('drop', function(e) { | |
const dt = e.dataTransfer; | |
const files = dt.files; | |
if (files && files[0]) { | |
imageInput.files = files; | |
const reader = new FileReader(); | |
reader.onload = function(e) { | |
// 移除上传按钮 | |
while (imageUploadArea.firstChild) { | |
imageUploadArea.removeChild(imageUploadArea.firstChild); | |
} | |
// 添加预览图片 | |
const img = document.createElement('img'); | |
img.src = e.target.result; | |
imageUploadArea.appendChild(img); | |
imageUploadArea.classList.add('with-image'); | |
sessionStorage.setItem('uploadedImage', e.target.result); | |
console.log("image uploaded") | |
}; | |
reader.readAsDataURL(files[0]); | |
} | |
}, false); | |
} | |
// 解析按钮处理 | |
const resolveButton = document.getElementById('resolve-button'); | |
const textInput = document.getElementById('text-input'); | |
if (resolveButton && textInput) { | |
resolveButton.addEventListener('click', function() { | |
const text = textInput.value.trim(); | |
// 检查是否有文本输入 | |
if (text === '') { | |
alert('请输入老板反馈内容'); | |
return; | |
} | |
// 显示加载状态 | |
resolveButton.disabled = true; | |
// 有趣的加载文案 | |
const loadingTexts = [ | |
"检测到甲方第8版需求残留怨气...正在召唤阴阳师修改图层结界", | |
"监控到大象对话框散发红光!自动生成24K纯杠精防护罩..." | |
]; | |
// 随机选择一个文案作为起点 | |
let currentTextIndex = Math.floor(Math.random() * loadingTexts.length); | |
let currentText = loadingTexts[currentTextIndex]; | |
let charIndex = 0; | |
let typingSpeed = 100; // 打字速度,从50ms增加到100ms | |
// 添加弹跳动画的CSS | |
if (!document.getElementById('bounce-animation-style')) { | |
const bounceStyle = document.createElement('style'); | |
bounceStyle.id = 'bounce-animation-style'; | |
bounceStyle.textContent = ` | |
.wave-container { | |
position: relative; | |
white-space: nowrap; | |
} | |
.bounce-text { | |
display: inline-block; | |
position: relative; | |
transition: transform 0.3s ease-out; | |
} | |
`; | |
document.head.appendChild(bounceStyle); | |
} | |
// 创建一个容器来包含所有字符 | |
const waveContainer = document.createElement('div'); | |
waveContainer.className = 'wave-container'; | |
resolveButton.innerHTML = ''; | |
resolveButton.appendChild(waveContainer); | |
// 跟踪波峰位置 | |
let peakPosition = -1; | |
let waveInterval; | |
// 打字机效果函数 | |
function typeLoadingText() { | |
if (charIndex < currentText.length) { | |
// 创建一个span元素包装字符 | |
const charSpan = document.createElement('span'); | |
charSpan.className = 'bounce-text'; | |
charSpan.textContent = currentText.charAt(charIndex); | |
// 添加到容器 | |
waveContainer.appendChild(charSpan); | |
charIndex++; | |
setTimeout(typeLoadingText, typingSpeed); | |
// 如果这是第一个字符,开始波动动画 | |
if (charIndex === 1) { | |
startWaveAnimation(); | |
} | |
} else { | |
// 当前文案打完,等待一段时间后切换到下一个文案 | |
setTimeout(() => { | |
// 清除波动动画 | |
if (waveInterval) { | |
clearInterval(waveInterval); | |
waveInterval = null; | |
} | |
// 切换到下一个文案 | |
charIndex = 0; | |
currentTextIndex = (currentTextIndex + 1) % loadingTexts.length; | |
currentText = loadingTexts[currentTextIndex]; | |
// 清空容器,准备下一个文案 | |
waveContainer.innerHTML = ''; | |
typeLoadingText(); | |
}, 4000); // 完整显示时间 | |
} | |
} | |
// 波动动画函数 | |
function startWaveAnimation() { | |
// 清除之前的间隔 | |
if (waveInterval) clearInterval(waveInterval); | |
// 启动波动动画 | |
waveInterval = setInterval(() => { | |
// 获取所有字符 | |
const chars = waveContainer.querySelectorAll('.bounce-text'); | |
if (chars.length === 0) return; | |
// 移动波峰位置 | |
peakPosition = (peakPosition + 1) % chars.length; | |
// 应用变换 | |
chars.forEach((char, index) => { | |
if (index === peakPosition) { | |
// 波峰位置 | |
char.style.transform = 'translateY(-8px)'; | |
} else if (Math.abs(index - peakPosition) === 1) { | |
// 波峰旁边的字符稍微上移 | |
char.style.transform = 'translateY(-4px)'; | |
} else { | |
// 其他字符回到原位 | |
char.style.transform = 'translateY(0)'; | |
} | |
}); | |
}, 100); // 每100ms更新一次波峰位置 | |
} | |
// 开始打字机效果 | |
typeLoadingText(); | |
// 存储文本到 sessionStorage | |
sessionStorage.setItem('uploadedText', text); | |
console.log(text) | |
// 获取上传的图片数据 | |
const uploadedImage = sessionStorage.getItem('uploadedImage'); | |
// 分析反馈内容,生成解读结果 | |
analyzeUserInput(text, uploadedImage).then(() => { | |
// 解读完成后跳转到反馈页面 | |
window.location.href = 'feedback.html'; | |
}).catch(error => { | |
console.error('解读失败:', error); | |
alert('解读失败,请重试'); | |
resolveButton.disabled = false; | |
resolveButton.textContent = '一键解读'; | |
}); | |
}); | |
} | |
// 分析用户输入和图片的函数 | |
async function analyzeUserInput(text, imageData) { | |
try { | |
const health = await fetch(`${BASE_URL}health`) | |
console.log(health) | |
const response = await fetch(`${BASE_URL}api/analyze`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
text: text, | |
image_data: imageData, | |
request_model_id: 'gpt-4.1-mini' | |
}) | |
}); | |
console.log(response) | |
const data = await response.json(); | |
console.log(data) | |
const rep = data['output'][0]['content'][0]['text']; | |
sessionStorage.setItem('analysisResult', rep); | |
} catch (error) { | |
console.error('分析失败:', error); | |
// 使用默认的分析结果 | |
const defaultAnalysis = `情绪值:😠😠😠\n\n | |
修改建议: | |
1. 色彩调整:在保持整体色调的基础上,加入一些明亮的点缀色,如淡黄色、淡紫蓝、淡红蓝微粉等。用于按钮、图标或重要文本,以增加吸引力。 | |
2. 增加层级感:利用饱和度(Glassmorphism)效果,为卡片/重要框添加模糊背景和阴影,营造出浮起感,引入3D元素或深浅变化,增加页面的深度和空间感。 | |
3. 增加互动性:为按钮和图标添加悬停动画或点击动画,提供用户及时反馈。引入微动画(Micro-interactions),如加载动画、切换动画等等,使页面更具活力。`; | |
sessionStorage.setItem('analysisResult', defaultAnalysis); | |
} | |
} | |
// 示例图片添加到参考案例 | |
const exampleCards = document.querySelectorAll('.example-card'); | |
if (exampleCards.length > 0) { | |
// 为示例卡片添加示例图片背景 | |
const exampleBgs = [ | |
'linear-gradient(45deg, #2d2d4e, #1e1e30)', | |
'linear-gradient(45deg, #2e2e2e, #1a1a1a)', | |
'linear-gradient(45deg, #332e42, #1f1b30)' | |
]; | |
exampleCards.forEach((card, index) => { | |
card.style.background = exampleBgs[index % exampleBgs.length]; | |
}); | |
} | |
// 加载并显示反馈页面的分析结果 | |
function loadAnalysisResult() { | |
const analysisResult = sessionStorage.getItem('analysisResult'); | |
console.log(analysisResult) | |
if (!analysisResult) return; | |
try { | |
// 设置情绪值 | |
if (analysisResult) { | |
const emotionIcons = document.querySelector('.emotion-icons'); | |
const emotionText = document.querySelector('.emotion-text'); | |
if (emotionIcons && emotionText) { | |
// 清空图标区域 | |
emotionIcons.innerHTML = ''; | |
// 提取情绪值部分 | |
const emojiText = analysisResult.split('情绪值:')[1].split('\n')[0]; | |
const emotionAnalyse = analysisResult.split('情绪分析:')[1].split('\n')[0]; | |
// 提取emoji | |
const emojis = [emojiText.codePointAt(0), emojiText.codePointAt(2), emojiText.codePointAt(4)]; | |
console.log(emojis) | |
emojis.forEach(emoji => { | |
console.log(emoji) | |
}) | |
if (emojis) { | |
// 只显示3个emoji,确保一致性 | |
const displayEmojis = emojis.slice(0, 3); | |
displayEmojis.forEach(emoji => { | |
const span = document.createElement('span'); | |
span.className = 'emoji'; | |
span.textContent = String.fromCodePoint(emoji); | |
emotionIcons.appendChild(span); | |
}); | |
} else { | |
// 默认情绪 | |
for (let i = 0; i < 3; i++) { | |
const span = document.createElement('span'); | |
span.className = 'emoji'; | |
span.textContent = '😠'; | |
emotionIcons.appendChild(span); | |
} | |
} | |
// 设置文字描述 | |
// 查找"情绪分析:"部分 | |
let textDescription = ''; | |
if (emotionAnalyse) { | |
textDescription = emotionAnalyse.trim(); | |
} else { | |
// 如果没有情绪分析部分,根据emoji生成默认文本 | |
if (emojis && emojis[0] === '😠') { | |
textDescription = '老板对设计非常不满意,建议全面改进'; | |
} else if (emojis && emojis[0] === '🙁') { | |
textDescription = '老板对设计不太满意,需要较多改进'; | |
} else if (emojis && emojis[0] === '😐') { | |
textDescription = '老板对设计感觉一般,有改进空间'; | |
} else if (emojis && emojis[0] === '🙂') { | |
textDescription = '老板对设计比较满意,小幅改进即可'; | |
} else if (emojis && emojis[0] === '😊') { | |
textDescription = '老板对设计非常满意,细节优化即可'; | |
} else { | |
textDescription = '需要基于反馈进行改进'; | |
} | |
} | |
emotionText.textContent = textDescription; | |
} | |
} | |
// 提取修改建议 | |
const suggestionsContainer = document.querySelector('.suggestions-container'); | |
if (suggestionsContainer) { | |
try { | |
// 使用split提取修改建议部分 | |
const suggestionsText = analysisResult.split('修改建议:')[1].split('搜索内容:')[0]; | |
sessionStorage.setItem('suggestionsText', suggestionsText) | |
if (suggestionsText) { | |
// 提取三个建议 | |
const suggestionLines = suggestionsText.trim().split('\n\n'); | |
const suggestions = []; | |
// 处理每一行建议 | |
for (let i = 0; i < suggestionLines.length; i++) { | |
const line = suggestionLines[i].trim(); | |
if (line.match(/^\d+\.\s/)) { | |
// 找到了序号开头的行,这是建议的标题和内容 | |
const suggestionText = line.replace(/^\d+\.\s/, ''); | |
// 拆分标题和内容(假设冒号或:分隔) | |
let title = suggestionText; | |
let content = ''; | |
if (suggestionText.includes(':')) { | |
[title, content] = suggestionText.split(':', 2); | |
} else if (suggestionText.includes(':')) { | |
[title, content] = suggestionText.split(':', 2); | |
} | |
suggestions.push({ | |
title: title.trim(), | |
content: content.trim() | |
}); | |
// 最多收集三个建议 | |
if (suggestions.length >= 3) break; | |
} | |
} | |
// 更新建议卡片 | |
if (suggestions.length > 0) { | |
const cardStyles = ['yellow-top', 'cyan-top', 'purple-top']; | |
suggestionsContainer.innerHTML = ''; | |
suggestions.forEach((suggestion, index) => { | |
const cardStyle = cardStyles[index % cardStyles.length]; | |
const card = document.createElement('div'); | |
card.className = `suggestion-card ${cardStyle}`; | |
const titleElement = document.createElement('h3'); | |
titleElement.className = 'suggestion-title'; | |
titleElement.textContent = suggestion.title; | |
const textElement = document.createElement('p'); | |
textElement.className = 'suggestion-text'; | |
textElement.textContent = suggestion.content; | |
card.appendChild(titleElement); | |
card.appendChild(textElement); | |
suggestionsContainer.appendChild(card); | |
}); | |
// 加载参考案例 | |
loadReferenceExamples(suggestions); | |
} | |
} | |
} catch (error) { | |
console.error('解析建议失败:', error); | |
} | |
} | |
} catch (error) { | |
console.error('解析分析结果失败:', error); | |
} | |
} | |
// 根据建议加载参考案例 | |
async function loadReferenceExamples(suggestions) { | |
const analysisResult = sessionStorage.getItem('analysisResult'); | |
const searchContent = analysisResult.split('搜索内容:')[1].trim(); | |
const examplesContainer = document.querySelector('.examples-container'); | |
if (!examplesContainer) return; | |
// 获取现有的示例卡片元素 | |
const exampleCards = examplesContainer.querySelectorAll('.example-card'); | |
if (exampleCards.length !== 2) { | |
console.error('未找到预期的两个示例卡片元素'); | |
return; | |
} | |
try { | |
// 显示加载状态 | |
exampleCards.forEach(card => { | |
const img = card.querySelector('.example-image'); | |
const desc = card.querySelector('.example-desc'); | |
const sourceLink = card.querySelector('.example-source a'); | |
if (img) { | |
img.style.display = 'none'; | |
} | |
if (desc) { | |
desc.innerHTML = ` | |
<div class="optimization-loading"> | |
<div class="loading-spinner"></div> | |
<div class="loading-text">正在搜索设计参考案例...</div> | |
</div> | |
`; | |
} | |
if (sourceLink) { | |
sourceLink.href = '#'; | |
sourceLink.textContent = '加载中...'; | |
} | |
}); | |
// 获取原始反馈文本和上传的图片 | |
const suggestionsText = sessionStorage.getItem('suggestionsText') || ''; | |
// 调用后端搜索API | |
const response = await fetch(`${BASE_URL}api/search`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
query: searchContent, | |
num_results: 2 | |
}) | |
}); | |
if (!response.ok) { | |
throw new Error('搜索请求失败'); | |
} | |
const searchData = await response.json(); | |
if (!searchData.items || searchData.items.length === 0) { | |
throw new Error('未找到搜索结果'); | |
} | |
// 处理搜索结果 | |
const examples = searchData.items.map(result => ({ | |
title: result.title, | |
sourceUrl: result.link, | |
description: result.snippet || '', | |
image: result.image || '' | |
})); | |
// 使用GPT生成描述 | |
for (let i = 0; i < examples.length; i++) { | |
const example = examples[i]; | |
try { | |
const response = await fetch(`${BASE_URL}api/analyze-case-study`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
user_input: `请分析这个设计案例:${example.title}\n${example.description}\n修改建议:${suggestionsText}\n基于原始设计反馈和修改建议,详细分析为什么这个设计案例可以解决用户面临的设计问题。描述要专业、具体,并关注设计细节。你只需要简单介绍案例内容和特点,然后简要分析即可,不要输出其他内容。`, | |
request_model_id: 'gpt-4.1-mini' | |
}) | |
}); | |
if (response.ok) { | |
const data = await response.json(); | |
if (data.output && data.output[0].content[0].text) { | |
example.description = data.output[0].content[0].text.trim(); | |
} | |
} | |
} catch (gptError) { | |
console.error('生成案例描述失败:', gptError); | |
// 使用默认描述 | |
example.description = i === 0 ? | |
'这个案例展示了清晰的视觉层次结构和吸引人的色彩搭配,能有效解决原设计中的平面感问题。通过对比度增强和微妙的动效,它能提高用户体验和互动性,同时保持整体设计的专业性。' : | |
'该设计运用了精细的色彩渐变和现代化的UI元素,完美解决了原始设计中的视觉单调问题。透过精心安排的布局和适当的留白,创造出流畅的用户浏览体验,同时增加了设计的精致感和专业度。'; | |
} | |
} | |
// 更新UI | |
examples.forEach((example, index) => { | |
const card = exampleCards[index]; | |
if (!card) return; | |
const img = card.querySelector('.example-image'); | |
const desc = card.querySelector('.example-desc'); | |
const source = card.querySelector('.example-source'); | |
if (img) { | |
img.style.display = 'block'; | |
img.src = example.image; | |
img.alt = example.title; | |
} | |
if (desc) { | |
desc.innerHTML = example.description; | |
} | |
if (source) { | |
const sourceLink = source.querySelector('a'); | |
if (sourceLink) { | |
sourceLink.href = example.sourceUrl; | |
sourceLink.textContent = example.title; | |
} | |
} | |
}); | |
} catch (error) { | |
console.error('加载参考案例失败:', error); | |
// 在错误情况下更新UI | |
exampleCards.forEach(card => { | |
const desc = card.querySelector('.example-desc'); | |
if (desc) { | |
desc.textContent = '加载参考案例失败,请刷新重试'; | |
desc.style.color = '#ff6b6b'; | |
} | |
}); | |
} | |
} | |
// 当在反馈页面时,加载分析结果 | |
if (window.location.pathname.includes('feedback.html')) { | |
loadAnalysisResult(); | |
// 优化方案功能 | |
setupOptimizationButtons(); | |
} | |
// 设置优化方案按钮功能 | |
function setupOptimizationButtons() { | |
const gpt4Button = document.getElementById('gpt4-btn'); | |
const mjButton = document.getElementById('mj-btn'); | |
if (gpt4Button) { | |
gpt4Button.addEventListener('click', function() { | |
generateGPT4Optimization(); | |
}); | |
} | |
if (mjButton) { | |
mjButton.addEventListener('click', function() { | |
generateDalleOptimization(); | |
}); | |
} | |
} | |
// 使用GPT-4生成优化方案 | |
async function generateGPT4Optimization() { | |
const gpt4Button = document.getElementById('gpt4-btn'); | |
const gpt4Content = document.getElementById('gpt4-content'); | |
if (!gpt4Button || !gpt4Content) return; | |
try { | |
// 禁用按钮 | |
gpt4Button.disabled = true; | |
gpt4Button.textContent = '生成中...'; | |
// 显示加载动画 | |
showLoadingState(gpt4Content, '正在生成优化方案,请稍候...'); | |
// 获取原始反馈和图片 | |
const uploadedText = sessionStorage.getItem('uploadedText') || ''; | |
const uploadedImage = sessionStorage.getItem('uploadedImage'); | |
const analysisResult = sessionStorage.getItem('analysisResult') || ''; | |
if (!uploadedImage) { | |
throw new Error('未找到原始设计图片'); | |
} | |
// 提取修改建议 | |
let suggestions = []; | |
if (analysisResult.includes('修改建议:')) { | |
const suggestionsText = analysisResult.split('修改建议:')[1].split('搜索内容:')[0]; | |
suggestions = suggestionsText | |
.split('\n') | |
.filter(line => line.match(/^\d+\./)) | |
.map(line => line.replace(/^\d+\.\s+/, '').trim()); | |
} | |
// 调用后端API | |
const response = await fetch(`${BASE_URL}api/optimize-design`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
text: uploadedText, | |
image_data: uploadedImage, | |
suggestions: suggestions, | |
request_model_id: 'gpt-image-1', | |
openai_key: sessionStorage.getItem('userOpenaiKey') || '', | |
user_key: sessionStorage.getItem('userKey') || '' | |
}) | |
}); | |
if (!response.ok) { | |
throw new Error('优化请求失败'); | |
} | |
const data = await response.json(); | |
if (data.data && data.data[0] && data.data[0].url) { | |
// 展示生成的图片 | |
showOptimizationResult(gpt4Content, data.data[0].url); | |
gpt4Button.disabled = false; | |
gpt4Button.textContent = '重新生成'; | |
} else { | |
throw new Error('API未返回图片数据'); | |
} | |
} catch (error) { | |
console.error('生成优化方案失败:', error); | |
gpt4Content.innerHTML = ` | |
<div class="optimization-placeholder"> | |
<p style="color: #ff6b6b;">生成失败: ${error.message || '请稍后重试'}</p> | |
</div> | |
`; | |
gpt4Button.disabled = false; | |
gpt4Button.textContent = '重试'; | |
} | |
} | |
async function generateDalleOptimization() { | |
const mjButton = document.getElementById('mj-btn'); | |
const mjContent = document.getElementById('mj-content'); | |
if (!mjButton || !mjContent) return; | |
try { | |
mjButton.disabled = true; | |
mjButton.textContent = '生成中...'; | |
showLoadingState(mjContent, '正在优化设计,请稍候...'); | |
// 获取原始反馈和建议 | |
const analysisResult = sessionStorage.getItem('analysisResult') || ''; | |
let suggestions = []; | |
if (analysisResult.includes('修改建议:')) { | |
const suggestionsText = analysisResult.split('修改建议:')[1].split('搜索内容:')[0]; | |
suggestions = suggestionsText | |
.split('\n') | |
.filter(line => line.match(/^\d+\./)) | |
.map(line => line.replace(/^\d+\.\s+/, '').trim()); | |
} | |
// 获取原始图片 | |
const uploadedImage = sessionStorage.getItem('uploadedImage'); | |
if (!uploadedImage) { | |
throw new Error('未找到原始设计图片'); | |
} | |
// 构建提示词 | |
const uploadedText = sessionStorage.getItem('uploadedText') || ''; | |
// 调用后端API | |
const response = await fetch(`${BASE_URL}api/optimize-design`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
text: uploadedText, | |
image_data: uploadedImage, | |
suggestions: suggestions, | |
request_model_id: 'dall-e-2', | |
openai_key: sessionStorage.getItem('userOpenaiKey') || '', | |
user_key: sessionStorage.getItem('userKey') || '' | |
}) | |
}); | |
if (response.status === 503) { | |
const errData = await response.json(); | |
alert(errData.detail) | |
throw new Error('用户无生图权限') | |
} | |
const data = await response.json(); | |
if (data.data && data.data[0] && data.data[0].url) { | |
// 展示生成的图片 | |
showOptimizationResult(mjContent, data.data[0].url); | |
mjButton.disabled = false; | |
mjButton.textContent = '重新生成'; | |
} else { | |
throw new Error('API未返回图片数据'); | |
} | |
} catch (error) { | |
console.error('生成优化方案失败:', error); | |
mjContent.innerHTML = ` | |
<div class="optimization-placeholder"> | |
<p style="color: #ff6b6b;">生成失败: ${error.message || '请稍后重试'}</p> | |
</div> | |
`; | |
mjButton.disabled = false; | |
mjButton.textContent = '重试'; | |
} | |
} | |
// 显示加载状态 | |
function showLoadingState(container, message) { | |
container.innerHTML = ` | |
<div class="optimization-loading"> | |
<div class="loading-spinner"></div> | |
<div class="loading-text">${message}</div> | |
</div> | |
<div class="optimization-placeholder"> | |
<p>生成中,请稍候...</p> | |
</div> | |
`; | |
} | |
// 显示优化结果 | |
function showOptimizationResult(container, imageUrl) { | |
container.innerHTML = ` | |
<div class="optimization-result"> | |
<img src="${imageUrl}" alt="优化方案" /> | |
</div> | |
`; | |
} | |
// 润色功能 | |
const generateBtn = document.getElementById('generateBtn'); | |
if (generateBtn) { | |
generateBtn.addEventListener('click', function() { | |
const userInput = document.getElementById('userInput').value.trim(); | |
if (!userInput) { | |
alert('请输入需要润色的内容'); | |
return; | |
} | |
// 显示Toast提示 | |
const toast = document.getElementById('toast'); | |
toast.style.display = 'block'; | |
// 调用OpenAI API | |
callOptimizeText(userInput); | |
}); | |
} | |
// OpenAI API调用函数 | |
async function callOptimizeText(inputText) { | |
try { | |
// 获取原始反馈文本 | |
const originalFeedback = sessionStorage.getItem('uploadedText') || ''; | |
const response = await fetch(`${BASE_URL}api/optimize-text`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
original_feedback: originalFeedback, | |
user_input: inputText, | |
request_model_id: 'gpt-4.1-mini' | |
}) | |
}); | |
if (!response.ok) { | |
throw new Error('文本优化请求失败'); | |
} | |
const data = await response.json(); | |
// 隐藏Toast | |
const toast = document.getElementById('toast'); | |
toast.style.display = 'none'; | |
if (data.output && data.output[0].content[0].text.length > 0) { | |
// 显示API返回的结果 | |
const optimizedText = data['output'][0]['content'][0]['text']; | |
const resultContent = document.getElementById('resultContent'); | |
const resultCard = document.getElementById('resultCard'); | |
resultContent.innerText = optimizedText; | |
resultCard.style.display = 'block'; | |
// 平滑滚动到结果区域 | |
resultCard.scrollIntoView({ behavior: 'smooth' }); | |
} else { | |
handleAPIError('未获取到有效响应'); | |
} | |
} catch (error) { | |
handleAPIError(error); | |
} | |
} | |
// 处理API错误 | |
function handleAPIError(error) { | |
console.error('API调用失败:', error); | |
// 隐藏Toast | |
const toast = document.getElementById('toast'); | |
toast.style.display = 'none'; | |
// 显示错误信息,同时提供备选方案 | |
const resultContent = document.getElementById('resultContent'); | |
const resultCard = document.getElementById('resultCard'); | |
// 备选文案,当API调用失败时使用 | |
const userInput = document.getElementById('userInput').value.trim(); | |
let fallbackText = ''; | |
if (userInput.includes('不行') || userInput.includes('不能')) { | |
fallbackText = '感谢您的建议,我们团队已经考虑过这个方案,但受到一些技术限制,暂时无法实现。我们已经记录下您的想法,并会在后续版本中尝试优化解决。'; | |
} else if (userInput.includes('丑') || userInput.includes('难看')) { | |
fallbackText = '非常感谢您的审美建议!我们设计团队正在不断优化视觉体验,您提出的这些意见非常有价值,我们会在下一版本中优先考虑调整。'; | |
} else { | |
fallbackText = '您的意见我们已经记录,团队会认真研究并在后续迭代中考虑采纳。感谢您的宝贵反馈,这对我们产品的完善非常重要!'; | |
} | |
resultContent.innerText = fallbackText; | |
resultCard.style.display = 'block'; | |
// 平滑滚动到结果区域 | |
resultCard.scrollIntoView({ behavior: 'smooth' }); | |
} | |
// 添加图片转base64函数 | |
async function convertImageToBase64(imageUrl) { | |
try { | |
const response = await fetch(imageUrl); | |
const blob = await response.blob(); | |
return new Promise((resolve, reject) => { | |
const reader = new FileReader(); | |
reader.onloadend = () => { | |
// 返回完整的Data URL | |
resolve(reader.result); | |
}; | |
reader.onerror = reject; | |
reader.readAsDataURL(blob); | |
}); | |
} catch (error) { | |
console.error('转换图片为base64失败:', error); | |
return imageUrl; // 如果失败,返回原始URL | |
} | |
} | |
// API Key Dialog functionality | |
const apiKeyLink = document.getElementById('apiKeyLink'); | |
const apiKeyDialog = document.getElementById('apiKeyDialog'); | |
const apiKeyInput = document.getElementById('apiKeyInput'); | |
const saveApiKey = document.getElementById('saveApiKey'); | |
const cancelApiKey = document.getElementById('cancelApiKey'); | |
if (apiKeyLink && apiKeyDialog) { | |
// Show dialog when clicking the link | |
apiKeyLink.addEventListener('click', function(e) { | |
e.preventDefault(); | |
apiKeyDialog.style.display = 'flex'; | |
// Load existing key if available | |
const existingKey = sessionStorage.getItem('userOpenaiKey'); | |
if (existingKey) { | |
apiKeyInput.value = existingKey; | |
} | |
}); | |
// Save API Key | |
saveApiKey.addEventListener('click', function() { | |
const key = apiKeyInput.value.trim(); | |
if (key) { | |
sessionStorage.setItem('userOpenaiKey', key); | |
apiKeyDialog.style.display = 'none'; | |
// Show success message | |
const toast = document.getElementById('toast'); | |
toast.textContent = 'API Key已保存'; | |
toast.style.display = 'block'; | |
setTimeout(() => { | |
toast.style.display = 'none'; | |
}, 2000); | |
} | |
}); | |
// Cancel button | |
cancelApiKey.addEventListener('click', function() { | |
apiKeyDialog.style.display = 'none'; | |
}); | |
// Close dialog when clicking outside | |
apiKeyDialog.addEventListener('click', function(e) { | |
if (e.target === apiKeyDialog) { | |
apiKeyDialog.style.display = 'none'; | |
} | |
}); | |
} | |
// User Key Dialog functionality | |
const userKeyLink = document.getElementById('userKeyLink'); | |
const userKeyDialog = document.getElementById('userKeyDialog'); | |
const userKeyInput = document.getElementById('userKeyInput'); | |
const saveUserKey = document.getElementById('saveUserKey'); | |
const cancelUserKey = document.getElementById('cancelUserKey'); | |
if (userKeyLink && userKeyDialog) { | |
// Show dialog when clicking the link | |
userKeyLink.addEventListener('click', function(e) { | |
e.preventDefault(); | |
userKeyDialog.style.display = 'flex'; | |
// Load existing key if available | |
const existingKey = sessionStorage.getItem('userKey'); | |
if (existingKey) { | |
userKeyInput.value = existingKey; | |
} | |
}); | |
// Save User Key | |
saveUserKey.addEventListener('click', function() { | |
const key = userKeyInput.value.trim(); | |
if (key) { | |
sessionStorage.setItem('userKey', key); | |
userKeyDialog.style.display = 'none'; | |
// Show success message | |
const toast = document.getElementById('toast'); | |
toast.textContent = '用户密钥已保存'; | |
toast.style.display = 'block'; | |
setTimeout(() => { | |
toast.style.display = 'none'; | |
}, 2000); | |
} | |
}); | |
// Cancel button | |
cancelUserKey.addEventListener('click', function() { | |
userKeyDialog.style.display = 'none'; | |
}); | |
// Close dialog when clicking outside | |
userKeyDialog.addEventListener('click', function(e) { | |
if (e.target === userKeyDialog) { | |
userKeyDialog.style.display = 'none'; | |
} | |
}); | |
} | |
}); |