Spaces:
Sleeping
Sleeping
| document.addEventListener('DOMContentLoaded', () => { | |
| // State | |
| let features = [ | |
| { icon: 'fa-bolt', title: '极速构建', desc: '无需编写代码,所见即所得,快速生成高品质页面。' }, | |
| { icon: 'fa-mobile-screen', title: '响应式设计', desc: '完美适配手机、平板和桌面端,随时随地展示。' }, | |
| { icon: 'fa-code', title: '代码导出', desc: '支持一键导出标准 HTML/Tailwind 代码,方便二次开发。' } | |
| ]; | |
| // DOM Elements | |
| const inputs = { | |
| brand: document.getElementById('input-brand'), | |
| headline: document.getElementById('input-headline'), | |
| subhead: document.getElementById('input-subhead'), | |
| ctaText: document.getElementById('input-cta-text'), | |
| ctaLink: document.getElementById('input-cta-link'), | |
| copyright: document.getElementById('input-copyright') | |
| }; | |
| const featuresContainer = document.getElementById('features-container'); | |
| const addFeatureBtn = document.getElementById('add-feature-btn'); | |
| const previewFrame = document.getElementById('preview-frame'); | |
| const themeRadios = document.querySelectorAll('input[name="theme"]'); | |
| const downloadBtn = document.getElementById('downloadBtn'); | |
| // Tab Switching | |
| document.querySelectorAll('.tab-btn').forEach(btn => { | |
| btn.addEventListener('click', (e) => { | |
| document.querySelectorAll('.tab-btn').forEach(b => { | |
| b.classList.remove('active', 'bg-indigo-100', 'text-indigo-700'); | |
| b.classList.add('text-gray-500', 'hover:bg-gray-100'); | |
| }); | |
| e.target.classList.add('active', 'bg-indigo-100', 'text-indigo-700'); | |
| e.target.classList.remove('text-gray-500', 'hover:bg-gray-100'); | |
| document.querySelectorAll('.tab-pane').forEach(p => p.classList.add('hidden')); | |
| document.getElementById(`tab-${e.target.dataset.tab}`).classList.remove('hidden'); | |
| }); | |
| }); | |
| // Feature Management | |
| function renderFeaturesList() { | |
| featuresContainer.innerHTML = ''; | |
| features.forEach((feature, index) => { | |
| const div = document.createElement('div'); | |
| div.className = 'bg-white border border-gray-200 rounded-lg p-3 relative group'; | |
| div.innerHTML = ` | |
| <button class="delete-feature absolute top-2 right-2 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-opacity" data-index="${index}"> | |
| <i class="fa-solid fa-trash"></i> | |
| </button> | |
| <div class="mb-2"> | |
| <label class="block text-xs text-gray-500 mb-1">图标 (FontAwesome Class)</label> | |
| <input type="text" class="feature-icon w-full text-xs border-gray-300 rounded p-1 border" value="${feature.icon}" data-index="${index}"> | |
| </div> | |
| <div class="mb-2"> | |
| <input type="text" class="feature-title w-full font-bold text-sm border-gray-300 rounded p-1 border" value="${feature.title}" data-index="${index}" placeholder="标题"> | |
| </div> | |
| <div> | |
| <textarea class="feature-desc w-full text-xs border-gray-300 rounded p-1 border" rows="2" data-index="${index}" placeholder="描述">${feature.desc}</textarea> | |
| </div> | |
| `; | |
| featuresContainer.appendChild(div); | |
| }); | |
| // Add event listeners for dynamic inputs | |
| document.querySelectorAll('.feature-icon').forEach(el => el.addEventListener('input', (e) => { | |
| features[e.target.dataset.index].icon = e.target.value; | |
| updatePreview(); | |
| })); | |
| document.querySelectorAll('.feature-title').forEach(el => el.addEventListener('input', (e) => { | |
| features[e.target.dataset.index].title = e.target.value; | |
| updatePreview(); | |
| })); | |
| document.querySelectorAll('.feature-desc').forEach(el => el.addEventListener('input', (e) => { | |
| features[e.target.dataset.index].desc = e.target.value; | |
| updatePreview(); | |
| })); | |
| document.querySelectorAll('.delete-feature').forEach(el => el.addEventListener('click', (e) => { | |
| features.splice(e.currentTarget.dataset.index, 1); | |
| renderFeaturesList(); | |
| updatePreview(); | |
| })); | |
| } | |
| addFeatureBtn.addEventListener('click', () => { | |
| features.push({ icon: 'fa-star', title: '新特性', desc: '描述这个特性的优势...' }); | |
| renderFeaturesList(); | |
| updatePreview(); | |
| }); | |
| // Core Generation Logic | |
| function getThemeColors(theme) { | |
| switch(theme) { | |
| case 'dark': | |
| return { | |
| bg: 'bg-gray-900', | |
| text: 'text-white', | |
| primary: 'bg-indigo-600 hover:bg-indigo-700', | |
| cardBg: 'bg-gray-800', | |
| cardText: 'text-gray-300', | |
| navBg: 'bg-gray-900/90', | |
| footerBg: 'bg-gray-950' | |
| }; | |
| case 'minimal': | |
| return { | |
| bg: 'bg-white', | |
| text: 'text-gray-900', | |
| primary: 'bg-black hover:bg-gray-800', | |
| cardBg: 'bg-gray-50', | |
| cardText: 'text-gray-600', | |
| navBg: 'bg-white/90', | |
| footerBg: 'bg-gray-100' | |
| }; | |
| case 'startup': | |
| return { | |
| bg: 'bg-white', | |
| text: 'text-gray-800', | |
| primary: 'bg-emerald-500 hover:bg-emerald-600', | |
| cardBg: 'bg-white shadow-lg border border-gray-100', | |
| cardText: 'text-gray-600', | |
| navBg: 'bg-white/90', | |
| footerBg: 'bg-gray-50' | |
| }; | |
| case 'modern': | |
| default: | |
| return { | |
| bg: 'bg-white', | |
| text: 'text-gray-900', | |
| primary: 'bg-blue-600 hover:bg-blue-700', | |
| cardBg: 'bg-white shadow-md border border-gray-100', | |
| cardText: 'text-gray-500', | |
| navBg: 'bg-white/90', | |
| footerBg: 'bg-gray-50' | |
| }; | |
| } | |
| } | |
| function generateHTML() { | |
| const selectedTheme = document.querySelector('input[name="theme"]:checked').value; | |
| const colors = getThemeColors(selectedTheme); | |
| const data = { | |
| brand: inputs.brand.value, | |
| headline: inputs.headline.value, | |
| subhead: inputs.subhead.value, | |
| ctaText: inputs.ctaText.value, | |
| ctaLink: inputs.ctaLink.value, | |
| copyright: inputs.copyright.value | |
| }; | |
| const featuresHTML = features.map(f => ` | |
| <div class="${colors.cardBg} p-6 rounded-xl transition-transform hover:-translate-y-1"> | |
| <div class="w-12 h-12 rounded-lg ${colors.primary} bg-opacity-10 flex items-center justify-center mb-4 text-2xl"> | |
| <i class="fa-solid ${f.icon} ${selectedTheme === 'dark' ? 'text-indigo-400' : (selectedTheme === 'startup' ? 'text-emerald-600' : 'text-blue-600')}"></i> | |
| </div> | |
| <h3 class="text-xl font-bold mb-2 ${colors.text}">${f.title}</h3> | |
| <p class="${colors.cardText}">${f.desc}</p> | |
| </div> | |
| `).join(''); | |
| return ` | |
| <!DOCTYPE html> | |
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>${data.brand}</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> | |
| </head> | |
| <body class="${colors.bg} font-sans antialiased selection:bg-blue-500 selection:text-white"> | |
| <!-- Navbar --> | |
| <nav class="fixed w-full z-50 ${colors.navBg} backdrop-blur-sm border-b border-gray-200/10"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="flex justify-between items-center h-16"> | |
| <div class="flex-shrink-0 font-bold text-xl ${colors.text}"> | |
| ${data.brand} | |
| </div> | |
| <div class="hidden md:block"> | |
| <a href="${data.ctaLink}" class="${colors.primary} text-white px-5 py-2 rounded-full font-medium transition-colors shadow-lg shadow-blue-500/30"> | |
| ${data.ctaText} | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| </nav> | |
| <!-- Hero --> | |
| <section class="pt-32 pb-20 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto text-center"> | |
| <h1 class="text-4xl sm:text-6xl font-extrabold tracking-tight ${colors.text} mb-6 leading-tight"> | |
| ${data.headline.replace(/\n/g, '<br>')} | |
| </h1> | |
| <p class="text-xl sm:text-2xl ${colors.cardText} mb-10 max-w-3xl mx-auto"> | |
| ${data.subhead} | |
| </p> | |
| <div class="flex justify-center gap-4"> | |
| <a href="${data.ctaLink}" class="${colors.primary} text-white px-8 py-4 rounded-full font-bold text-lg transition-transform hover:scale-105 shadow-xl"> | |
| ${data.ctaText} | |
| </a> | |
| <a href="#features" class="px-8 py-4 rounded-full font-bold text-lg border ${selectedTheme === 'dark' ? 'border-gray-700 text-white hover:bg-gray-800' : 'border-gray-300 text-gray-700 hover:bg-gray-50'} transition-colors"> | |
| 了解更多 | |
| </a> | |
| </div> | |
| </section> | |
| <!-- Features --> | |
| <section id="features" class="py-20 ${selectedTheme === 'minimal' ? 'bg-gray-50' : ''}"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-8"> | |
| ${featuresHTML} | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Footer --> | |
| <footer class="${colors.footerBg} py-12 border-t border-gray-200/10"> | |
| <div class="max-w-7xl mx-auto px-4 text-center ${colors.cardText}"> | |
| <p>${data.copyright}</p> | |
| </div> | |
| </footer> | |
| </body> | |
| </html>`; | |
| } | |
| function updatePreview() { | |
| const html = generateHTML(); | |
| const iframe = document.getElementById('preview-frame'); | |
| iframe.srcdoc = html; | |
| } | |
| // Initialize | |
| renderFeaturesList(); | |
| updatePreview(); | |
| // Event Listeners | |
| Object.values(inputs).forEach(input => { | |
| input.addEventListener('input', updatePreview); | |
| }); | |
| themeRadios.forEach(radio => { | |
| radio.addEventListener('change', updatePreview); | |
| }); | |
| // Download Logic | |
| downloadBtn.addEventListener('click', () => { | |
| const zip = new JSZip(); | |
| const html = generateHTML(); | |
| zip.file("index.html", html); | |
| zip.file("README.txt", "Generated by One-Page Builder.\nDeploy this index.html anywhere (GitHub Pages, Vercel, Netlify)."); | |
| zip.generateAsync({type:"blob"}) | |
| .then(function(content) { | |
| saveAs(content, "website-export.zip"); | |
| }); | |
| }); | |
| }); | |