| | <!DOCTYPE html> |
| | <html lang="zh-CN"> |
| |
|
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>M5Stack LLM8850 文生图展示</title> |
| | <style> |
| | :root { |
| | --primary-color: #3498db; |
| | --selected-color: #e74c3c; |
| | --success-color: #27ae60; |
| | --warning-color: #f39c12; |
| | } |
| | |
| | body { |
| | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| | margin: 0; |
| | padding: 20px; |
| | background: #f0f3f5; |
| | } |
| | |
| | .container { |
| | max-width: 100%; |
| | margin: 0 auto; |
| | background: white; |
| | border-radius: 12px; |
| | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); |
| | padding: 2rem; |
| | position: relative; |
| | } |
| | |
| | |
| | .company-logo { |
| | position: absolute; |
| | top: 20px; |
| | right: 20px; |
| | width: 80px; |
| | height: auto; |
| | } |
| | |
| | h1 { |
| | text-align: center; |
| | color: #2c3e50; |
| | margin-bottom: 2rem; |
| | font-weight: 600; |
| | margin-right: 100px; |
| | |
| | } |
| | |
| | |
| | #selected-terms-box { |
| | position: sticky; |
| | top: 0; |
| | background: rgba(255, 255, 255, 0.95); |
| | backdrop-filter: blur(5px); |
| | z-index: 10; |
| | padding: 1rem; |
| | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08); |
| | margin-bottom: 1.5rem; |
| | border-radius: 8px; |
| | } |
| | |
| | .selected-terms-label { |
| | font-weight: 500; |
| | color: #2c3e50; |
| | margin-bottom: 0.5rem; |
| | font-size: 0.9rem; |
| | } |
| | |
| | .selected-term { |
| | display: inline-flex; |
| | align-items: center; |
| | padding: 0.4rem 0.8rem; |
| | background: var(--selected-color); |
| | color: white; |
| | border-radius: 20px; |
| | margin: 0.3rem; |
| | font-size: 0.9em; |
| | animation: popIn 0.2s ease; |
| | cursor: pointer; |
| | transition: all 0.2s ease; |
| | } |
| | |
| | .selected-term:hover { |
| | background: #c0392b; |
| | transform: scale(1.05); |
| | } |
| | |
| | .controls { |
| | display: inline-flex; |
| | gap: 0.5rem; |
| | margin-left: 1rem; |
| | vertical-align: middle; |
| | } |
| | |
| | .btn { |
| | padding: 0.6rem 1.2rem; |
| | border: none; |
| | border-radius: 20px; |
| | font-size: 0.9rem; |
| | cursor: pointer; |
| | transition: all 0.2s ease; |
| | font-weight: 500; |
| | } |
| | |
| | #generate-btn { |
| | background: var(--primary-color); |
| | color: white; |
| | padding: 1rem 2rem; |
| | |
| | font-size: 1.1rem; |
| | font-weight: 600; |
| | } |
| | |
| | #random-btn { |
| | background: var(--success-color); |
| | color: white; |
| | } |
| | |
| | #clear-terms-btn { |
| | background: var(--warning-color); |
| | color: white; |
| | } |
| | |
| | #clear-images-btn { |
| | background: #95a5a6; |
| | color: white; |
| | } |
| | |
| | .btn:hover { |
| | transform: translateY(-2px); |
| | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); |
| | } |
| | |
| | .btn:disabled { |
| | background: #bdc3c7; |
| | cursor: not-allowed; |
| | transform: none; |
| | box-shadow: none; |
| | } |
| | |
| | @keyframes popIn { |
| | from { |
| | transform: scale(0.8); |
| | opacity: 0; |
| | } |
| | |
| | to { |
| | transform: scale(1); |
| | opacity: 1; |
| | } |
| | } |
| | |
| | |
| | .progress-container { |
| | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | padding: 1rem; |
| | border-radius: 8px; |
| | margin-bottom: 1rem; |
| | color: white; |
| | display: none; |
| | animation: slideDown 0.3s ease; |
| | } |
| | |
| | .progress-container.active { |
| | display: block; |
| | } |
| | |
| | .progress-bar { |
| | width: 100%; |
| | height: 6px; |
| | background: rgba(255, 255, 255, 0.3); |
| | border-radius: 3px; |
| | overflow: hidden; |
| | margin: 0.5rem 0; |
| | } |
| | |
| | .progress-fill { |
| | height: 100%; |
| | background: linear-gradient(90deg, #fff, #f39c12); |
| | width: 0%; |
| | transition: width 0.3s ease; |
| | border-radius: 3px; |
| | } |
| | |
| | .progress-info { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | font-size: 0.9rem; |
| | } |
| | |
| | .progress-step { |
| | opacity: 0.9; |
| | } |
| | |
| | .fun-fact { |
| | background: rgba(255, 255, 255, 0.1); |
| | padding: 0.5rem; |
| | border-radius: 4px; |
| | margin-top: 0.5rem; |
| | font-style: italic; |
| | font-size: 0.85rem; |
| | animation: fadeIn 0.5s ease; |
| | } |
| | |
| | @keyframes slideDown { |
| | from { |
| | opacity: 0; |
| | transform: translateY(-20px); |
| | } |
| | |
| | to { |
| | opacity: 1; |
| | transform: translateY(0); |
| | } |
| | } |
| | |
| | @keyframes fadeIn { |
| | from { |
| | opacity: 0; |
| | transform: translateY(10px); |
| | } |
| | |
| | to { |
| | opacity: 1; |
| | transform: translateY(0); |
| | } |
| | } |
| | |
| | |
| | #term-categories { |
| | display: grid; |
| | grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); |
| | gap: 1.5rem; |
| | } |
| | |
| | .category { |
| | background: #ffffff; |
| | border-radius: 8px; |
| | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | overflow: hidden; |
| | } |
| | |
| | .category-header { |
| | padding: 1rem; |
| | background: var(--primary-color); |
| | color: white; |
| | font-weight: 500; |
| | position: sticky; |
| | top: 0; |
| | z-index: 1; |
| | } |
| | |
| | .scroll-container { |
| | max-height: 240px; |
| | overflow-y: hidden; |
| | padding: 1rem; |
| | position: relative; |
| | } |
| | |
| | .term-item { |
| | padding: 0.8rem 1.2rem; |
| | margin: 0.5rem 0; |
| | border-radius: 6px; |
| | background: #f8f9fa; |
| | cursor: pointer; |
| | transition: all 0.2s ease; |
| | position: relative; |
| | overflow: hidden; |
| | display: flex; |
| | align-items: center; |
| | min-height: 48px; |
| | } |
| | |
| | .term-content { |
| | display: flex; |
| | flex-direction: column; |
| | gap: 0.2rem; |
| | } |
| | |
| | .chinese { |
| | font-weight: 500; |
| | color: #2c3e50; |
| | } |
| | |
| | .term { |
| | font-size: 0.85rem; |
| | color: #7f8c8d; |
| | font-style: italic; |
| | } |
| | |
| | .term-item.selected { |
| | background: var(--primary-color); |
| | color: white; |
| | } |
| | |
| | .term-item.selected .term { |
| | color: rgba(255, 255, 255, 0.8); |
| | } |
| | |
| | .term-item:hover { |
| | transform: translateX(5px); |
| | box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.1); |
| | } |
| | |
| | |
| | .main-content { |
| | display: grid; |
| | grid-template-columns: 1fr 500px; |
| | gap: 2rem; |
| | min-height: 70vh; |
| | } |
| | |
| | .left-column { |
| | display: grid; |
| | grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); |
| | gap: 1.2rem; |
| | align-content: start; |
| | } |
| | |
| | .right-column { |
| | background: #f8f9fa; |
| | border-radius: 8px; |
| | padding: 1rem; |
| | display: flex; |
| | flex-direction: column; |
| | } |
| | |
| | .images-header { |
| | text-align: center; |
| | color: #2c3e50; |
| | margin-bottom: 1rem; |
| | font-size: 1.1rem; |
| | font-weight: 500; |
| | flex-shrink: 0; |
| | } |
| | |
| | |
| | #images-container { |
| | display: grid; |
| | grid-template-columns: 1fr 1fr; |
| | gap: 1rem; |
| | flex: 1; |
| | } |
| | |
| | .image-item { |
| | background: white; |
| | border-radius: 8px; |
| | padding: 0.5rem; |
| | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | animation: zoomIn 0.3s ease; |
| | align-self: start; |
| | } |
| | |
| | .image-item img { |
| | width: 100%; |
| | height: auto; |
| | border-radius: 6px; |
| | display: block; |
| | } |
| | |
| | .image-terms { |
| | font-size: 0.75rem; |
| | color: #7f8c8d; |
| | margin-top: 0.5rem; |
| | padding: 0.5rem; |
| | background: #ecf0f1; |
| | border-radius: 4px; |
| | word-wrap: break-word; |
| | line-height: 1.3; |
| | } |
| | |
| | .empty-state { |
| | grid-column: 1 / -1; |
| | text-align: center; |
| | color: #7f8c8d; |
| | padding: 2rem; |
| | font-style: italic; |
| | } |
| | |
| | @keyframes zoomIn { |
| | from { |
| | opacity: 0; |
| | transform: scale(0.9); |
| | } |
| | |
| | to { |
| | opacity: 1; |
| | transform: scale(1); |
| | } |
| | } |
| | |
| | |
| | @media (max-width: 1400px) { |
| | .main-content { |
| | grid-template-columns: 1fr 450px; |
| | } |
| | } |
| | |
| | @media (max-width: 1200px) { |
| | .main-content { |
| | grid-template-columns: 1fr; |
| | } |
| | } |
| | |
| | @media (max-width: 768px) { |
| | .container { |
| | padding: 1rem; |
| | } |
| | |
| | .company-logo { |
| | width: 60px; |
| | top: 10px; |
| | right: 10px; |
| | } |
| | |
| | h1 { |
| | margin-right: 70px; |
| | font-size: 1.5rem; |
| | } |
| | |
| | #term-categories { |
| | grid-template-columns: 1fr; |
| | } |
| | |
| | #images-container { |
| | grid-template-columns: 1fr; |
| | } |
| | |
| | .controls { |
| | flex-direction: column; |
| | gap: 0.5rem; |
| | margin-left: 0; |
| | margin-top: 1rem; |
| | } |
| | |
| | .btn { |
| | padding: 0.8rem 1.5rem; |
| | } |
| | |
| | #generate-btn { |
| | padding: 1rem 1.5rem; |
| | } |
| | } |
| | |
| | |
| | @media (min-width: 1600px) { |
| | .container { |
| | max-width: 95%; |
| | } |
| | |
| | .main-content { |
| | grid-template-columns: 1fr 600px; |
| | } |
| | |
| | .left-column { |
| | grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); |
| | } |
| | } |
| | </style> |
| | </head> |
| |
|
| | <body> |
| | <div class="container"> |
| | |
| | <img src="https://docs.m5stack.com/assets/m5logo2022.svg" alt="M5Stack Logo" class="company-logo"> |
| |
|
| | <h1>M5Stack LLM8850 文生图展示</h1> |
| |
|
| | <div id="selected-terms-box"> |
| | <div class="selected-terms-label">已选择的提示词 Selected Prompts:</div> |
| | <div id="selected-terms-list"></div> |
| | <div class="controls"> |
| | <button id="generate-btn" class="btn">立即生成 Generate Now</button> |
| | <button id="random-btn" class="btn">随机描述 Random Terms</button> |
| | <button id="clear-terms-btn" class="btn">清除提示词 Clear Terms</button> |
| | <button id="clear-images-btn" class="btn">清除图片 Clear Images</button> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="progress-container" id="progress-container"> |
| | <div class="progress-info"> |
| | <span>AI 正在创作中... AI is creating...</span> |
| | <span id="progress-percent">0%</span> |
| | </div> |
| | <div class="progress-bar"> |
| | <div class="progress-fill" id="progress-fill"></div> |
| | </div> |
| | <div class="progress-step" id="progress-step">初始化中... Initializing...</div> |
| | <div class="fun-fact" id="fun-fact"></div> |
| | </div> |
| |
|
| | <div class="main-content"> |
| | <div class="left-column"> |
| | <div id="term-categories"></div> |
| | </div> |
| |
|
| | <div class="right-column"> |
| | <div class="images-header">生成历史 Generation History</div> |
| | <div id="images-container"> |
| | <div class="empty-state"> |
| | 选择词条并点击生成按钮开始创作<br> |
| | Select terms and click generate to start creating |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <script> |
| | let scrollControllers = new Map(); |
| | |
| | const funFacts = [ |
| | "正在分析你的创意想法... Analyzing your creative ideas...", |
| | "AI 大脑正在思考最佳构图... AI brain thinking about optimal composition...", |
| | "调色板准备中,寻找完美色彩... Preparing palette, finding perfect colors...", |
| | "像素精灵们正在排队组合... Pixel sprites lining up for combination...", |
| | "魔法正在将文字转化为视觉... Magic transforming text to visuals...", |
| | "创意工厂全速运转中... Creative factory at full speed...", |
| | "每个细节都在精心雕琢... Every detail carefully crafted...", |
| | "光影大师正在调整明暗... Light master adjusting brightness...", |
| | "笔刷正在画布上飞舞... Brushes dancing on canvas...", |
| | "艺术灵感正在汇聚成形... Artistic inspiration taking shape...", |
| | "创造力引擎火力全开... Creativity engine at full power...", |
| | "美学算法正在优化细节... Aesthetic algorithm optimizing details...", |
| | "视觉交响乐即将奏响... Visual symphony about to play...", |
| | "想象力正在突破边界... Imagination breaking boundaries...", |
| | "奇迹工坊正在施展魔法... Miracle workshop casting magic..." |
| | ]; |
| | |
| | |
| | const unetSteps = [ |
| | "预处理输入图层... Preprocessing input layers...", |
| | "第1层卷积神经网络处理... Layer 1 CNN processing...", |
| | "第2层特征提取中... Layer 2 feature extraction...", |
| | "第3层语义理解... Layer 3 semantic understanding...", |
| | "第4层细节优化... Layer 4 detail optimization...", |
| | "第5层色彩调和... Layer 5 color harmony...", |
| | "第6层纹理生成... Layer 6 texture generation...", |
| | "第7层构图调整... Layer 7 composition adjustment...", |
| | "第8层光影处理... Layer 8 lighting processing...", |
| | "第9层细节增强... Layer 9 detail enhancement...", |
| | "第10层质量检查... Layer 10 quality check...", |
| | "第11层风格融合... Layer 11 style fusion...", |
| | "第12层最终调色... Layer 12 final coloring...", |
| | "第13层锐化处理... Layer 13 sharpening...", |
| | "第14层噪声去除... Layer 14 noise removal...", |
| | "第15层对比度优化... Layer 15 contrast optimization...", |
| | "第16层饱和度调节... Layer 16 saturation adjustment...", |
| | "第17层最终渲染... Layer 17 final rendering...", |
| | "第18层质量验证... Layer 18 quality validation...", |
| | "第19层输出准备... Layer 19 output preparation...", |
| | "第20层完成处理... Layer 20 completion..." |
| | ]; |
| | |
| | async function loadAndDisplayTerms() { |
| | try { |
| | const response = await fetch('./person.json'); |
| | const data = await response.json(); |
| | const proprietaryTerms = data.prompt_terms.filter(term => term.type === "专用"); |
| | const categories = proprietaryTerms.reduce((acc, term) => { |
| | const subtype = term.subtype || "通用 General"; |
| | acc[subtype] = acc[subtype] || []; |
| | acc[subtype].push(term); |
| | return acc; |
| | }, {}); |
| | const container = document.getElementById('term-categories'); |
| | Object.entries(categories).forEach(([subtype, terms]) => { |
| | const category = document.createElement('div'); |
| | category.className = 'category'; |
| | const header = document.createElement('div'); |
| | header.className = 'category-header'; |
| | header.textContent = subtype; |
| | const scrollContainer = document.createElement('div'); |
| | scrollContainer.className = 'scroll-container'; |
| | terms.forEach(term => { |
| | const item = document.createElement('div'); |
| | item.className = 'term-item'; |
| | item.dataset.term = term.term; |
| | item.innerHTML = ` |
| | <div class="term-content"> |
| | <div class="chinese">${term.chinese}</div> |
| | <div class="term">${term.term}</div> |
| | </div> |
| | `; |
| | scrollContainer.appendChild(item); |
| | }); |
| | category.append(header, scrollContainer); |
| | container.appendChild(category); |
| | if (scrollContainer.scrollHeight > scrollContainer.clientHeight) { |
| | setupAutoScroll(scrollContainer); |
| | } |
| | }); |
| | } catch (error) { |
| | console.error('数据加载失败 Data loading failed:', error); |
| | } |
| | } |
| | |
| | function setupAutoScroll(container) { |
| | let isPaused = false; |
| | let scrollPos = 0; |
| | const scrollSpeed = 0.5; |
| | const scrollHeight = container.scrollHeight - container.clientHeight; |
| | container.addEventListener('mouseenter', () => isPaused = true); |
| | container.addEventListener('mouseleave', () => isPaused = false); |
| | const scroll = () => { |
| | if (!isPaused && scrollHeight > 0) { |
| | scrollPos = (scrollPos + scrollSpeed) % scrollHeight; |
| | const easedPos = scrollPos < scrollHeight / 2 ? scrollPos : scrollHeight - scrollPos; |
| | container.scrollTop = easedPos; |
| | } |
| | requestAnimationFrame(scroll); |
| | }; |
| | scroll(); |
| | } |
| | |
| | function updateSelections() { |
| | const selectedItems = Array.from(document.querySelectorAll('.term-item.selected')); |
| | const container = document.getElementById('selected-terms-list'); |
| | |
| | container.innerHTML = selectedItems.map(item => { |
| | const chinese = item.querySelector('.chinese').textContent; |
| | const english = item.querySelector('.term').textContent; |
| | return `<span class="selected-term" data-term="${english}">${chinese} ${english}</span>`; |
| | }).join(''); |
| | } |
| | |
| | function showProgress() { |
| | const container = document.getElementById('progress-container'); |
| | const progressFill = document.getElementById('progress-fill'); |
| | const progressStep = document.getElementById('progress-step'); |
| | const progressPercent = document.getElementById('progress-percent'); |
| | const funFact = document.getElementById('fun-fact'); |
| | container.classList.add('active'); |
| | let currentStep = 0; |
| | const totalSteps = 20; |
| | const stepDuration = 240; |
| | return new Promise((resolve) => { |
| | const interval = setInterval(() => { |
| | currentStep++; |
| | const progress = (currentStep / totalSteps) * 100; |
| | progressFill.style.width = progress + '%'; |
| | progressPercent.textContent = Math.round(progress) + '%'; |
| | progressStep.textContent = unetSteps[currentStep - 1] || '处理中... Processing...'; |
| | if (currentStep % 3 === 0) { |
| | const randomFact = funFacts[Math.floor(Math.random() * funFacts.length)]; |
| | funFact.textContent = randomFact; |
| | } |
| | if (currentStep >= totalSteps) { |
| | clearInterval(interval); |
| | progressStep.textContent = '即将完成... Almost done...'; |
| | setTimeout(() => { |
| | container.classList.remove('active'); |
| | resolve(); |
| | }, 500); |
| | } |
| | }, stepDuration); |
| | }); |
| | } |
| | |
| | function addImageToHistory(selectedTerms) { |
| | const imagesContainer = document.getElementById('images-container'); |
| | |
| | const emptyState = imagesContainer.querySelector('.empty-state'); |
| | if (emptyState) { |
| | emptyState.remove(); |
| | } |
| | const imgContainer = document.createElement('div'); |
| | imgContainer.className = 'image-item'; |
| | const img = document.createElement('img'); |
| | img.src = 'generated_image.png?' + new Date().getTime(); |
| | img.alt = 'Generated Image'; |
| | const termsDiv = document.createElement('div'); |
| | termsDiv.className = 'image-terms'; |
| | termsDiv.textContent = '关键词 Keywords: ' + selectedTerms.join(', '); |
| | imgContainer.appendChild(img); |
| | imgContainer.appendChild(termsDiv); |
| | |
| | imagesContainer.prepend(imgContainer); |
| | |
| | while (imagesContainer.children.length > 20) { |
| | imagesContainer.removeChild(imagesContainer.lastChild); |
| | } |
| | } |
| | |
| | function clearAllTerms() { |
| | |
| | document.querySelectorAll('.term-item.selected').forEach(item => { |
| | item.classList.remove('selected'); |
| | }); |
| | updateSelections(); |
| | } |
| | |
| | function clearAllImages() { |
| | const imagesContainer = document.getElementById('images-container'); |
| | imagesContainer.innerHTML = '<div class="empty-state">选择词条并点击生成按钮开始创作<br>Select terms and click generate to start creating</div>'; |
| | } |
| | |
| | |
| | function selectRandomTerms() { |
| | |
| | clearAllTerms(); |
| | |
| | |
| | const categories = document.querySelectorAll('.category'); |
| | const randomTerms = []; |
| | |
| | categories.forEach(category => { |
| | const termItems = category.querySelectorAll('.term-item'); |
| | if (termItems.length > 0) { |
| | |
| | const randomIndex = Math.floor(Math.random() * termItems.length); |
| | const selectedItem = termItems[randomIndex]; |
| | selectedItem.classList.add('selected'); |
| | randomTerms.push({ |
| | chinese: selectedItem.querySelector('.chinese').textContent, |
| | english: selectedItem.querySelector('.term').textContent |
| | }); |
| | } |
| | }); |
| | |
| | |
| | updateSelections(); |
| | |
| | |
| | if (randomTerms.length > 0) { |
| | console.log('随机选择的词条:', randomTerms); |
| | |
| | const notification = document.createElement('div'); |
| | notification.style.cssText = ` |
| | position: fixed; |
| | top: 20px; |
| | left: 50%; |
| | transform: translateX(-50%); |
| | background: var(--success-color); |
| | color: white; |
| | padding: 0.8rem 1.5rem; |
| | border-radius: 20px; |
| | z-index: 1000; |
| | animation: slideDown 0.3s ease; |
| | `; |
| | notification.textContent = `已随机选择 ${randomTerms.length} 个词条 Randomly selected ${randomTerms.length} terms`; |
| | document.body.appendChild(notification); |
| | |
| | |
| | setTimeout(() => { |
| | notification.remove(); |
| | }, 3000); |
| | } |
| | } |
| | |
| | |
| | document.addEventListener('click', (e) => { |
| | |
| | if (e.target.closest('.term-item')) { |
| | const item = e.target.closest('.term-item'); |
| | item.classList.toggle('selected'); |
| | updateSelections(); |
| | } |
| | |
| | if (e.target.closest('.selected-term')) { |
| | const term = e.target.closest('.selected-term').dataset.term; |
| | const termItem = document.querySelector(`.term-item[data-term="${term}"]`); |
| | if (termItem) { |
| | termItem.classList.remove('selected'); |
| | updateSelections(); |
| | } |
| | } |
| | }); |
| | |
| | window.addEventListener('load', () => { |
| | loadAndDisplayTerms(); |
| | const generateBtn = document.getElementById('generate-btn'); |
| | const randomBtn = document.getElementById('random-btn'); |
| | const clearTermsBtn = document.getElementById('clear-terms-btn'); |
| | const clearImagesBtn = document.getElementById('clear-images-btn'); |
| | |
| | |
| | generateBtn.addEventListener('click', async () => { |
| | const selectedTerms = Array.from(document.querySelectorAll('.term-item.selected')) |
| | .map(item => item.dataset.term); |
| | if (selectedTerms.length === 0) { |
| | alert('请至少选择一个词条 Please select at least one term'); |
| | return; |
| | } |
| | generateBtn.disabled = true; |
| | const progressPromise = showProgress(); |
| | try { |
| | const [response] = await Promise.all([ |
| | fetch('/generate', { |
| | method: 'POST', |
| | headers: { |
| | 'Content-Type': 'application/json' |
| | }, |
| | body: JSON.stringify({ terms: selectedTerms }) |
| | }), |
| | progressPromise |
| | ]); |
| | if (response.ok) { |
| | addImageToHistory(selectedTerms); |
| | } else { |
| | const error = await response.json(); |
| | alert(`生成失败 Generation failed: ${error.error}`); |
| | } |
| | } catch (error) { |
| | console.error('生成请求失败 Generation request failed:', error); |
| | alert('生成请求失败,请检查网络连接 Generation request failed, please check network connection'); |
| | } finally { |
| | generateBtn.disabled = false; |
| | } |
| | }); |
| | randomBtn.addEventListener('click', () => { |
| | selectRandomTerms(); |
| | }); |
| | |
| | |
| | clearTermsBtn.addEventListener('click', clearAllTerms); |
| | |
| | |
| | clearImagesBtn.addEventListener('click', () => { |
| | if (confirm('确定要清除所有生成的图片吗? Are you sure you want to clear all generated images?')) { |
| | clearAllImages(); |
| | } |
| | }); |
| | }); |
| | </script> |
| | </body> |
| |
|
| | </html> |
| |
|