mirror of
https://ghproxy.net/https://github.com/skylarGW/ai-image-generator.git
synced 2025-08-17 19:21:11 +08:00
Add files via upload
初始版本:AI图片生成器
This commit is contained in:
parent
e6e2277a15
commit
88f89e7742
8 changed files with 2027 additions and 0 deletions
340
ai-service.js
Normal file
340
ai-service.js
Normal file
|
@ -0,0 +1,340 @@
|
|||
// AI图片生成服务类
|
||||
class AIImageService {
|
||||
constructor() {
|
||||
this.apiKey = null;
|
||||
this.currentService = API_CONFIG.defaultService;
|
||||
this.generationQueue = [];
|
||||
this.isGenerating = false;
|
||||
}
|
||||
|
||||
// 设置API密钥
|
||||
setApiKey(service, apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
this.currentService = service;
|
||||
localStorage.setItem(`${service}_api_key`, apiKey);
|
||||
}
|
||||
|
||||
// 获取存储的API密钥
|
||||
getStoredApiKey(service) {
|
||||
return localStorage.getItem(`${service}_api_key`);
|
||||
}
|
||||
|
||||
// 构建提示词
|
||||
buildPrompt(formData) {
|
||||
let prompt = formData.description || '';
|
||||
|
||||
// 如果没有描述,根据其他信息构建基础提示词
|
||||
if (!prompt.trim()) {
|
||||
prompt = '高质量商业图片';
|
||||
}
|
||||
|
||||
// 添加风格信息
|
||||
if (formData.style.type) {
|
||||
const styleMap = {
|
||||
'minimalist': '简约现代风格, 干净简洁的设计',
|
||||
'luxury': '奢华高端风格, 精致优雅的质感',
|
||||
'lifestyle': '生活方式风格, 自然真实的场景',
|
||||
'product-focus': '产品聚焦风格, 突出产品特色',
|
||||
'seasonal': '季节主题风格, 应季的氛围感'
|
||||
};
|
||||
prompt += `, ${styleMap[formData.style.type]}`;
|
||||
}
|
||||
|
||||
// 添加色彩方案
|
||||
if (formData.style.colorScheme) {
|
||||
const colorMap = {
|
||||
'warm': '暖色调配色方案, 温暖舒适的色彩',
|
||||
'cool': '冷色调配色方案, 清爽专业的色彩',
|
||||
'neutral': '中性色调配色方案, 平衡和谐的色彩',
|
||||
'vibrant': '鲜艳色彩配色方案, 活力四射的色彩',
|
||||
'monochrome': '单色调配色方案, 统一协调的色彩'
|
||||
};
|
||||
prompt += `, ${colorMap[formData.style.colorScheme]}`;
|
||||
}
|
||||
|
||||
// 添加平台优化信息
|
||||
if (formData.platforms.length > 0) {
|
||||
prompt += ', 适合电商平台展示';
|
||||
|
||||
const platformOptimizations = [];
|
||||
if (formData.platforms.includes('tiktok')) {
|
||||
platformOptimizations.push('适合短视频展示, 动感有趣');
|
||||
}
|
||||
if (formData.platforms.includes('instagram')) {
|
||||
platformOptimizations.push('适合社交媒体分享, 视觉冲击力强');
|
||||
}
|
||||
if (formData.platforms.includes('amazon')) {
|
||||
platformOptimizations.push('适合产品展示, 清晰的产品细节');
|
||||
}
|
||||
if (formData.platforms.includes('temu')) {
|
||||
platformOptimizations.push('适合跨境电商, 国际化视觉风格');
|
||||
}
|
||||
|
||||
if (platformOptimizations.length > 0) {
|
||||
prompt += ', ' + platformOptimizations.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
// 添加目标受众信息
|
||||
if (formData.targetAudience && formData.targetAudience.trim()) {
|
||||
prompt += `, 面向${formData.targetAudience}的视觉设计`;
|
||||
}
|
||||
|
||||
// 添加尺寸和质量要求
|
||||
const { width, height } = formData.dimensions;
|
||||
if (width && height) {
|
||||
if (width === height) {
|
||||
prompt += ', 方形构图';
|
||||
} else if (width > height) {
|
||||
prompt += ', 横向构图';
|
||||
} else {
|
||||
prompt += ', 竖向构图';
|
||||
}
|
||||
}
|
||||
|
||||
// 添加专业质量描述
|
||||
prompt += ', 高质量摄影, 专业打光, 4K分辨率, 商业用途, 精美细节';
|
||||
|
||||
console.log('构建的完整提示词:', prompt);
|
||||
return prompt;
|
||||
}
|
||||
|
||||
// 使用Stability AI生成图片
|
||||
async generateWithStability(prompt, params) {
|
||||
const response = await fetch(API_CONFIG.services.stability.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.apiKey}`,
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
text_prompts: [
|
||||
{
|
||||
text: prompt,
|
||||
weight: 1
|
||||
}
|
||||
],
|
||||
cfg_scale: params.cfg_scale || 7,
|
||||
height: params.height || 1024,
|
||||
width: params.width || 1024,
|
||||
samples: params.samples || 1,
|
||||
steps: params.steps || 30,
|
||||
style_preset: 'photographic'
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(`Stability AI错误: ${error.message || response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
return result.artifacts.map(artifact => ({
|
||||
url: `data:image/png;base64,${artifact.base64}`,
|
||||
seed: artifact.seed
|
||||
}));
|
||||
}
|
||||
|
||||
// 使用OpenAI DALL-E生成图片
|
||||
async generateWithOpenAI(prompt, params) {
|
||||
const response = await fetch(API_CONFIG.services.openai.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.apiKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
prompt: prompt,
|
||||
n: params.samples || 1,
|
||||
size: `${params.width || 1024}x${params.height || 1024}`,
|
||||
response_format: 'url'
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(`OpenAI错误: ${error.error?.message || response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
return result.data.map(item => ({
|
||||
url: item.url,
|
||||
revised_prompt: item.revised_prompt
|
||||
}));
|
||||
}
|
||||
|
||||
// 模拟图片生成(用于演示)
|
||||
async simulateGeneration(prompt, params) {
|
||||
// 模拟API调用延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
// 根据提示词内容选择合适的演示图片
|
||||
const demoImages = this.selectDemoImages(prompt);
|
||||
|
||||
// 返回模拟结果
|
||||
return demoImages.map((imageUrl, index) => ({
|
||||
url: imageUrl,
|
||||
prompt: prompt,
|
||||
seed: Math.floor(Math.random() * 1000000),
|
||||
generated_at: new Date().toISOString(),
|
||||
demo_note: '这是演示模式生成的图片,实际AI生成会更符合您的需求'
|
||||
}));
|
||||
}
|
||||
|
||||
// 根据提示词选择合适的演示图片
|
||||
selectDemoImages(prompt) {
|
||||
const lowerPrompt = prompt.toLowerCase();
|
||||
|
||||
// 定义不同类型的演示图片
|
||||
const imageCategories = {
|
||||
// 人物相关
|
||||
people: [
|
||||
'https://images.unsplash.com/photo-1544005313-94ddf0286df2?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=1024&h=1024&fit=crop'
|
||||
],
|
||||
// 运动相关
|
||||
sports: [
|
||||
'https://images.unsplash.com/photo-1622279457486-62dcc4a431d6?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1551698618-1dfe5d97d256?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1554068865-24cecd4e34b8?w=1024&h=1024&fit=crop'
|
||||
],
|
||||
// 产品相关
|
||||
product: [
|
||||
'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1572635196237-14b3f281503f?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1560472354-b33ff0c44a43?w=1024&h=1024&fit=crop'
|
||||
],
|
||||
// 时尚相关
|
||||
fashion: [
|
||||
'https://images.unsplash.com/photo-1515372039744-b8f02a3ae446?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1509631179647-0177331693ae?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1445205170230-053b83016050?w=1024&h=1024&fit=crop'
|
||||
],
|
||||
// 生活方式
|
||||
lifestyle: [
|
||||
'https://images.unsplash.com/photo-1513475382585-d06e58bcb0e0?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1522202176988-66273c2fd55f?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1511632765486-a01980e01a18?w=1024&h=1024&fit=crop'
|
||||
],
|
||||
// 默认商业图片
|
||||
business: [
|
||||
'https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1553062407-98eeb64c6a62?w=1024&h=1024&fit=crop',
|
||||
'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=1024&h=1024&fit=crop'
|
||||
]
|
||||
};
|
||||
|
||||
// 根据关键词匹配图片类型
|
||||
let selectedCategory = 'business'; // 默认类型
|
||||
|
||||
if (lowerPrompt.includes('tennis') || lowerPrompt.includes('sport') || lowerPrompt.includes('运动') || lowerPrompt.includes('网球')) {
|
||||
selectedCategory = 'sports';
|
||||
} else if (lowerPrompt.includes('student') || lowerPrompt.includes('people') || lowerPrompt.includes('person') || lowerPrompt.includes('女性') || lowerPrompt.includes('男性') || lowerPrompt.includes('人物')) {
|
||||
selectedCategory = 'people';
|
||||
} else if (lowerPrompt.includes('product') || lowerPrompt.includes('商品') || lowerPrompt.includes('产品')) {
|
||||
selectedCategory = 'product';
|
||||
} else if (lowerPrompt.includes('fashion') || lowerPrompt.includes('dress') || lowerPrompt.includes('clothing') || lowerPrompt.includes('时尚') || lowerPrompt.includes('服装')) {
|
||||
selectedCategory = 'fashion';
|
||||
} else if (lowerPrompt.includes('lifestyle') || lowerPrompt.includes('生活') || lowerPrompt.includes('日常')) {
|
||||
selectedCategory = 'lifestyle';
|
||||
}
|
||||
|
||||
console.log(`根据提示词"${prompt}"选择了图片类型: ${selectedCategory}`);
|
||||
|
||||
// 返回选中类型的图片(随机选择1-2张)
|
||||
const images = imageCategories[selectedCategory];
|
||||
const numImages = Math.random() > 0.7 ? 2 : 1; // 30%概率生成2张图片
|
||||
|
||||
const selectedImages = [];
|
||||
for (let i = 0; i < numImages; i++) {
|
||||
const randomIndex = Math.floor(Math.random() * images.length);
|
||||
selectedImages.push(images[randomIndex]);
|
||||
}
|
||||
|
||||
return selectedImages;
|
||||
}
|
||||
|
||||
// 主要的图片生成方法
|
||||
async generateImages(formData) {
|
||||
try {
|
||||
this.isGenerating = true;
|
||||
|
||||
// 构建提示词
|
||||
const prompt = this.buildPrompt(formData);
|
||||
|
||||
// 准备参数
|
||||
const params = {
|
||||
width: formData.dimensions.width,
|
||||
height: formData.dimensions.height,
|
||||
samples: 1, // 先生成1张,后续可以让用户选择数量
|
||||
...API_CONFIG.defaultParams
|
||||
};
|
||||
|
||||
console.log('生成参数:', { prompt, params, service: this.currentService });
|
||||
|
||||
let results;
|
||||
|
||||
// 根据当前服务选择生成方法
|
||||
if (this.apiKey && this.currentService === 'stability') {
|
||||
results = await this.generateWithStability(prompt, params);
|
||||
} else if (this.apiKey && this.currentService === 'openai') {
|
||||
results = await this.generateWithOpenAI(prompt, params);
|
||||
} else {
|
||||
// 如果没有API密钥,使用模拟生成
|
||||
console.log('使用模拟生成模式');
|
||||
results = await this.simulateGeneration(prompt, params);
|
||||
}
|
||||
|
||||
// 添加元数据
|
||||
const enhancedResults = results.map(result => ({
|
||||
...result,
|
||||
id: 'img_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9),
|
||||
prompt: prompt,
|
||||
dimensions: params,
|
||||
service: this.currentService,
|
||||
created_at: new Date().toISOString(),
|
||||
formData: formData // 保存原始表单数据用于后续分析
|
||||
}));
|
||||
|
||||
// 保存到本地存储
|
||||
this.saveGenerationHistory(enhancedResults);
|
||||
|
||||
return enhancedResults;
|
||||
|
||||
} catch (error) {
|
||||
console.error('图片生成失败:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
this.isGenerating = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存生成历史
|
||||
saveGenerationHistory(results) {
|
||||
const history = JSON.parse(localStorage.getItem('generation_history') || '[]');
|
||||
history.push(...results);
|
||||
|
||||
// 只保留最近100条记录
|
||||
if (history.length > 100) {
|
||||
history.splice(0, history.length - 100);
|
||||
}
|
||||
|
||||
localStorage.setItem('generation_history', JSON.stringify(history));
|
||||
}
|
||||
|
||||
// 获取生成历史
|
||||
getGenerationHistory() {
|
||||
return JSON.parse(localStorage.getItem('generation_history') || '[]');
|
||||
}
|
||||
|
||||
// 检查服务状态
|
||||
async checkServiceStatus(service) {
|
||||
// 这里可以添加服务健康检查
|
||||
return { status: 'available', service: service };
|
||||
}
|
||||
}
|
||||
|
||||
// 创建全局实例
|
||||
const aiImageService = new AIImageService();
|
49
config.js
Normal file
49
config.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
// AI图片生成API配置
|
||||
const API_CONFIG = {
|
||||
// 可选的AI图片生成服务
|
||||
services: {
|
||||
// OpenAI DALL-E (推荐用于商业用途)
|
||||
openai: {
|
||||
name: 'OpenAI DALL-E',
|
||||
endpoint: 'https://api.openai.com/v1/images/generations',
|
||||
maxSize: '1024x1024',
|
||||
formats: ['png'],
|
||||
pricing: '$0.02/image'
|
||||
},
|
||||
|
||||
// Stability AI (开源,性价比高)
|
||||
stability: {
|
||||
name: 'Stability AI',
|
||||
endpoint: 'https://api.stability.ai/v1/generation/stable-diffusion-xl-1024-v1-0/text-to-image',
|
||||
maxSize: '1024x1024',
|
||||
formats: ['png', 'jpg'],
|
||||
pricing: '$0.01/image'
|
||||
},
|
||||
|
||||
// Replicate (多种模型选择)
|
||||
replicate: {
|
||||
name: 'Replicate',
|
||||
endpoint: 'https://api.replicate.com/v1/predictions',
|
||||
maxSize: '1024x1024',
|
||||
formats: ['png', 'jpg'],
|
||||
pricing: '$0.005/image'
|
||||
}
|
||||
},
|
||||
|
||||
// 默认使用的服务
|
||||
defaultService: 'stability',
|
||||
|
||||
// 图片生成参数
|
||||
defaultParams: {
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
steps: 30,
|
||||
cfg_scale: 7,
|
||||
samples: 1
|
||||
}
|
||||
};
|
||||
|
||||
// 导出配置
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = API_CONFIG;
|
||||
}
|
133
index.html
Normal file
133
index.html
Normal file
|
@ -0,0 +1,133 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AI图片生成器 - 跨境电商专用</title>
|
||||
<meta name="description" content="专为跨境电商商家打造的智能图片生成平台,支持Amazon、TikTok、Temu等多平台优化">
|
||||
<meta name="keywords" content="AI图片生成,跨境电商,Amazon,TikTok,Temu,商品图片,AI设计">
|
||||
<meta name="author" content="AI图片生成器">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="">
|
||||
<meta property="og:title" content="AI图片生成器 - 跨境电商专用">
|
||||
<meta property="og:description" content="专为跨境电商商家打造的智能图片生成平台">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:title" content="AI图片生成器 - 跨境电商专用">
|
||||
<meta property="twitter:description" content="专为跨境电商商家打造的智能图片生成平台">
|
||||
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>🎨 AI图片生成器</h1>
|
||||
<p>专为跨境电商商家打造的智能图片生成平台</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<form id="imageRequestForm" class="form-container">
|
||||
<section class="form-section">
|
||||
<h2>📏 图片规格需求</h2>
|
||||
<div class="input-group">
|
||||
<label for="width">宽度 (px):</label>
|
||||
<input type="number" id="width" name="width" value="1080" min="100" max="4000">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="height">高度 (px):</label>
|
||||
<input type="number" id="height" name="height" value="1080" min="100" max="4000">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="format">图片格式:</label>
|
||||
<select id="format" name="format">
|
||||
<option value="jpg">JPG</option>
|
||||
<option value="png">PNG</option>
|
||||
<option value="webp">WebP</option>
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="form-section">
|
||||
<h2>🎭 风格偏好</h2>
|
||||
<div class="input-group">
|
||||
<label for="style">图片风格:</label>
|
||||
<select id="style" name="style">
|
||||
<option value="">请选择风格</option>
|
||||
<option value="minimalist">简约现代</option>
|
||||
<option value="luxury">奢华高端</option>
|
||||
<option value="lifestyle">生活方式</option>
|
||||
<option value="product-focus">产品聚焦</option>
|
||||
<option value="seasonal">季节主题</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="color-scheme">色彩方案:</label>
|
||||
<select id="color-scheme" name="colorScheme">
|
||||
<option value="">请选择色彩</option>
|
||||
<option value="warm">暖色调</option>
|
||||
<option value="cool">冷色调</option>
|
||||
<option value="neutral">中性色</option>
|
||||
<option value="vibrant">鲜艳色彩</option>
|
||||
<option value="monochrome">单色调</option>
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="form-section">
|
||||
<h2>🛍️ 应用场景</h2>
|
||||
<div class="checkbox-group">
|
||||
<label><input type="checkbox" name="platform" value="amazon"> Amazon</label>
|
||||
<label><input type="checkbox" name="platform" value="shopify"> Shopify</label>
|
||||
<label><input type="checkbox" name="platform" value="tiktok"> TikTok</label>
|
||||
<label><input type="checkbox" name="platform" value="temu"> Temu</label>
|
||||
<label><input type="checkbox" name="platform" value="facebook"> Facebook广告</label>
|
||||
<label><input type="checkbox" name="platform" value="instagram"> Instagram</label>
|
||||
<label><input type="checkbox" name="platform" value="google"> Google广告</label>
|
||||
<label><input type="checkbox" name="platform" value="other"> 其他</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="form-section">
|
||||
<h2>📤 素材上传</h2>
|
||||
<div class="upload-group">
|
||||
<label for="product-images">产品图片:</label>
|
||||
<input type="file" id="product-images" name="productImages" multiple accept="image/*">
|
||||
<small>支持多张图片上传,格式:JPG, PNG, WebP</small>
|
||||
</div>
|
||||
<div class="upload-group">
|
||||
<label for="reference-images">风格参考图:</label>
|
||||
<input type="file" id="reference-images" name="referenceImages" multiple accept="image/*">
|
||||
<small>上传你喜欢的风格参考图</small>
|
||||
</div>
|
||||
<div class="upload-group">
|
||||
<label for="brand-guide">品牌视觉指南:</label>
|
||||
<input type="file" id="brand-guide" name="brandGuide" accept=".pdf,.doc,.docx,.jpg,.png">
|
||||
<small>品牌logo、色彩指南等文件</small>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="form-section">
|
||||
<h2>📝 详细描述</h2>
|
||||
<div class="input-group">
|
||||
<label for="description">图片描述需求:</label>
|
||||
<textarea id="description" name="description" rows="4"
|
||||
placeholder="请详细描述你想要的图片内容、场景、氛围等..."></textarea>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="target-audience">目标受众:</label>
|
||||
<input type="text" id="target-audience" name="targetAudience"
|
||||
placeholder="例如:25-35岁女性,注重生活品质">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<button type="submit" class="submit-btn">🚀 生成AI图片</button>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
469
results.css
Normal file
469
results.css
Normal file
|
@ -0,0 +1,469 @@
|
|||
/* 结果页面专用样式 */
|
||||
|
||||
/* 返回按钮 */
|
||||
.back-btn {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
padding: 10px 20px;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
margin-top: 15px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.back-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
|
||||
/* 进度容器 */
|
||||
.progress-container {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.progress-content h3 {
|
||||
color: #4a5568;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.progress-content p {
|
||||
color: #718096;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading-spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #667eea;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
/* 进度条 */
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: #e2e8f0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #667eea, #764ba2);
|
||||
border-radius: 4px;
|
||||
width: 0%;
|
||||
transition: width 0.5s ease;
|
||||
}
|
||||
|
||||
/* 结果容器 */
|
||||
.results-container {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.results-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.results-header h2 {
|
||||
color: #4a5568;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.results-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
.action-btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.action-btn.primary {
|
||||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.action-btn.secondary {
|
||||
background: #e2e8f0;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
.action-btn.download-btn {
|
||||
background: #38a169;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.action-btn.regenerate-btn {
|
||||
background: #ed8936;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.action-btn.retry-btn {
|
||||
background: #3182ce;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
/* 图片网格 */
|
||||
.images-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.image-card {
|
||||
background: #f7fafc;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.image-card img {
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.image-info {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.image-info h4 {
|
||||
margin: 0 0 8px 0;
|
||||
color: #2d3748;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.image-info p {
|
||||
margin: 0;
|
||||
color: #718096;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.image-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
font-size: 0.8rem;
|
||||
color: #a0aec0;
|
||||
}
|
||||
|
||||
/* 图片详情面板 */
|
||||
.image-details {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.8);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.details-content {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
max-width: 900px;
|
||||
width: 100%;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 20px;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
cursor: pointer;
|
||||
color: #718096;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.details-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 30px;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.details-image img {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.image-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.details-info h3 {
|
||||
color: #2d3748;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.info-item label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
color: #4a5568;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.info-item span,
|
||||
.info-item p {
|
||||
color: #718096;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.info-item p {
|
||||
background: #f7fafc;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 错误容器 */
|
||||
.error-container {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.error-content h3 {
|
||||
color: #e53e3e;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.error-content p {
|
||||
color: #718096;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.error-actions {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* API设置面板 */
|
||||
.api-setup {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.8);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.setup-content {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
padding: 30px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.setup-content h3 {
|
||||
color: #2d3748;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.setup-content p {
|
||||
color: #718096;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.service-options {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.service-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 15px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.service-option:hover {
|
||||
border-color: #667eea;
|
||||
background: #f7fafc;
|
||||
}
|
||||
|
||||
.service-option input[type="radio"] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.service-info strong {
|
||||
display: block;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.service-info small {
|
||||
color: #718096;
|
||||
}
|
||||
|
||||
.api-input {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.api-input label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
color: #2d3748;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.api-input input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.api-input small {
|
||||
display: block;
|
||||
color: #718096;
|
||||
margin-top: 5px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.setup-actions {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 演示模式提示 */
|
||||
.demo-notice {
|
||||
grid-column: 1 / -1;
|
||||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||
color: white;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.demo-notice-content h3 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.demo-notice-content p {
|
||||
margin: 8px 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.demo-notice-content .action-btn {
|
||||
margin-top: 15px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.demo-notice-content .action-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.details-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.results-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.images-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.setup-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
143
results.html
Normal file
143
results.html
Normal file
|
@ -0,0 +1,143 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>生成结果 - AI图片生成器</title>
|
||||
<meta name="description" content="查看您的AI生成图片结果">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="results.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>🎨 生成结果</h1>
|
||||
<p>您的AI图片已生成完成</p>
|
||||
<button onclick="goBack()" class="back-btn">← 返回表单</button>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<!-- 生成进度区域 -->
|
||||
<div id="generation-progress" class="progress-container" style="display: none;">
|
||||
<div class="progress-content">
|
||||
<div class="loading-spinner"></div>
|
||||
<h3>正在生成您的AI图片...</h3>
|
||||
<p id="progress-text">正在处理您的需求</p>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" id="progress-fill"></div>
|
||||
</div>
|
||||
<small>预计需要30-60秒</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 结果展示区域 -->
|
||||
<div id="results-container" class="results-container" style="display: none;">
|
||||
<div class="results-header">
|
||||
<h2>🖼️ 生成的图片</h2>
|
||||
<div class="results-actions">
|
||||
<button onclick="downloadAll()" class="action-btn download-btn">📥 下载全部</button>
|
||||
<button onclick="regenerateImages()" class="action-btn regenerate-btn">🔄 重新生成</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="images-grid" class="images-grid">
|
||||
<!-- 生成的图片将在这里显示 -->
|
||||
</div>
|
||||
|
||||
<!-- 图片详情面板 -->
|
||||
<div id="image-details" class="image-details" style="display: none;">
|
||||
<div class="details-content">
|
||||
<button onclick="closeDetails()" class="close-btn">×</button>
|
||||
<div class="details-grid">
|
||||
<div class="details-image">
|
||||
<img id="detail-image" src="" alt="生成的图片">
|
||||
<div class="image-actions">
|
||||
<button onclick="downloadImage()" class="action-btn">📥 下载</button>
|
||||
<button onclick="shareImage()" class="action-btn">📤 分享</button>
|
||||
<button onclick="editImage()" class="action-btn">✏️ 编辑</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="details-info">
|
||||
<h3>图片信息</h3>
|
||||
<div class="info-item">
|
||||
<label>尺寸:</label>
|
||||
<span id="detail-dimensions">-</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>生成时间:</label>
|
||||
<span id="detail-time">-</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>使用提示词:</label>
|
||||
<p id="detail-prompt">-</p>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>生成服务:</label>
|
||||
<span id="detail-service">-</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>种子值:</label>
|
||||
<span id="detail-seed">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 错误提示区域 -->
|
||||
<div id="error-container" class="error-container" style="display: none;">
|
||||
<div class="error-content">
|
||||
<h3>❌ 生成失败</h3>
|
||||
<p id="error-message">发生了未知错误</p>
|
||||
<div class="error-actions">
|
||||
<button onclick="retryGeneration()" class="action-btn retry-btn">🔄 重试</button>
|
||||
<button onclick="goBack()" class="action-btn">← 返回表单</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- API密钥设置面板 -->
|
||||
<div id="api-setup" class="api-setup" style="display: none;">
|
||||
<div class="setup-content">
|
||||
<h3>🔑 API密钥设置</h3>
|
||||
<p>为了使用真实的AI图片生成服务,请设置您的API密钥:</p>
|
||||
|
||||
<div class="service-options">
|
||||
<label class="service-option">
|
||||
<input type="radio" name="service" value="stability" checked>
|
||||
<div class="service-info">
|
||||
<strong>Stability AI</strong>
|
||||
<small>性价比高,$0.01/图片</small>
|
||||
</div>
|
||||
</label>
|
||||
<label class="service-option">
|
||||
<input type="radio" name="service" value="openai">
|
||||
<div class="service-info">
|
||||
<strong>OpenAI DALL-E</strong>
|
||||
<small>质量优秀,$0.02/图片</small>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="api-input">
|
||||
<label for="api-key">API密钥:</label>
|
||||
<input type="password" id="api-key" placeholder="请输入您的API密钥">
|
||||
<small>您的API密钥将安全存储在本地浏览器中</small>
|
||||
</div>
|
||||
|
||||
<div class="setup-actions">
|
||||
<button onclick="saveApiKey()" class="action-btn primary">保存并继续</button>
|
||||
<button onclick="useDemo()" class="action-btn secondary">使用演示模式</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="config.js"></script>
|
||||
<script src="ai-service.js"></script>
|
||||
<script src="results.js"></script>
|
||||
</body>
|
||||
</html>
|
384
results.js
Normal file
384
results.js
Normal file
|
@ -0,0 +1,384 @@
|
|||
// 结果页面JavaScript逻辑
|
||||
let currentFormData = null;
|
||||
let generatedImages = [];
|
||||
let currentImageIndex = 0;
|
||||
|
||||
// 页面加载完成后执行
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 从URL参数或localStorage获取表单数据
|
||||
loadFormData();
|
||||
|
||||
// 检查是否需要设置API密钥
|
||||
checkApiSetup();
|
||||
});
|
||||
|
||||
// 加载表单数据
|
||||
function loadFormData() {
|
||||
// 首先尝试从URL参数获取数据
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const urlData = urlParams.get('data');
|
||||
|
||||
if (urlData) {
|
||||
try {
|
||||
currentFormData = JSON.parse(decodeURIComponent(urlData));
|
||||
console.log('从URL参数加载的表单数据:', currentFormData);
|
||||
return;
|
||||
} catch (e) {
|
||||
console.error('URL参数解析失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果URL参数没有数据,尝试从localStorage获取
|
||||
try {
|
||||
const savedData = localStorage.getItem('current_form_data');
|
||||
if (savedData) {
|
||||
currentFormData = JSON.parse(savedData);
|
||||
console.log('从localStorage加载的表单数据:', currentFormData);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('localStorage读取失败:', e);
|
||||
}
|
||||
|
||||
// 如果都没有数据,显示错误并返回表单页面
|
||||
console.error('没有找到表单数据');
|
||||
alert('没有找到表单数据,请重新填写表单');
|
||||
goBack();
|
||||
}
|
||||
|
||||
// 检查API设置
|
||||
function checkApiSetup() {
|
||||
const hasStabilityKey = aiImageService.getStoredApiKey('stability');
|
||||
const hasOpenAIKey = aiImageService.getStoredApiKey('openai');
|
||||
|
||||
if (!hasStabilityKey && !hasOpenAIKey) {
|
||||
// 显示API设置面板
|
||||
document.getElementById('api-setup').style.display = 'flex';
|
||||
} else {
|
||||
// 直接开始生成
|
||||
startGeneration();
|
||||
}
|
||||
}
|
||||
|
||||
// 保存API密钥
|
||||
function saveApiKey() {
|
||||
const selectedService = document.querySelector('input[name="service"]:checked').value;
|
||||
const apiKey = document.getElementById('api-key').value.trim();
|
||||
|
||||
if (!apiKey) {
|
||||
alert('请输入API密钥');
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存API密钥
|
||||
aiImageService.setApiKey(selectedService, apiKey);
|
||||
|
||||
// 隐藏设置面板
|
||||
document.getElementById('api-setup').style.display = 'none';
|
||||
|
||||
// 开始生成
|
||||
startGeneration();
|
||||
}
|
||||
|
||||
// 使用演示模式
|
||||
function useDemo() {
|
||||
document.getElementById('api-setup').style.display = 'none';
|
||||
startGeneration();
|
||||
}
|
||||
|
||||
// 开始生成图片
|
||||
async function startGeneration() {
|
||||
// 显示进度界面
|
||||
showProgress();
|
||||
|
||||
try {
|
||||
// 模拟进度更新
|
||||
updateProgress(20, '正在分析您的需求...');
|
||||
await sleep(1000);
|
||||
|
||||
updateProgress(40, '正在构建AI提示词...');
|
||||
await sleep(1000);
|
||||
|
||||
updateProgress(60, '正在生成图片...');
|
||||
await sleep(1000);
|
||||
|
||||
// 调用AI服务生成图片
|
||||
const results = await aiImageService.generateImages(currentFormData);
|
||||
|
||||
updateProgress(80, '正在处理图片...');
|
||||
await sleep(500);
|
||||
|
||||
updateProgress(100, '生成完成!');
|
||||
await sleep(500);
|
||||
|
||||
// 显示结果
|
||||
generatedImages = results;
|
||||
showResults(results);
|
||||
|
||||
} catch (error) {
|
||||
console.error('生成失败:', error);
|
||||
showError(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示进度
|
||||
function showProgress() {
|
||||
document.getElementById('generation-progress').style.display = 'block';
|
||||
document.getElementById('results-container').style.display = 'none';
|
||||
document.getElementById('error-container').style.display = 'none';
|
||||
}
|
||||
|
||||
// 更新进度
|
||||
function updateProgress(percentage, text) {
|
||||
document.getElementById('progress-fill').style.width = percentage + '%';
|
||||
document.getElementById('progress-text').textContent = text;
|
||||
}
|
||||
|
||||
// 显示结果
|
||||
function showResults(images) {
|
||||
document.getElementById('generation-progress').style.display = 'none';
|
||||
document.getElementById('results-container').style.display = 'block';
|
||||
document.getElementById('error-container').style.display = 'none';
|
||||
|
||||
const imagesGrid = document.getElementById('images-grid');
|
||||
imagesGrid.innerHTML = '';
|
||||
|
||||
// 如果是演示模式,添加提示信息
|
||||
if (images.length > 0 && images[0].demo_note) {
|
||||
const demoNotice = document.createElement('div');
|
||||
demoNotice.className = 'demo-notice';
|
||||
demoNotice.innerHTML = `
|
||||
<div class="demo-notice-content">
|
||||
<h3>🎭 演示模式</h3>
|
||||
<p>当前显示的是演示图片,已根据您的提示词进行了智能匹配。</p>
|
||||
<p>要获得真正的AI生成图片,请设置API密钥后重新生成。</p>
|
||||
<button onclick="showApiSetup()" class="action-btn primary">设置API密钥</button>
|
||||
</div>
|
||||
`;
|
||||
imagesGrid.appendChild(demoNotice);
|
||||
}
|
||||
|
||||
images.forEach((image, index) => {
|
||||
const imageCard = createImageCard(image, index);
|
||||
imagesGrid.appendChild(imageCard);
|
||||
});
|
||||
}
|
||||
|
||||
// 创建图片卡片
|
||||
function createImageCard(image, index) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'image-card';
|
||||
card.onclick = () => showImageDetails(index);
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = image.url;
|
||||
img.alt = `生成的图片 ${index + 1}`;
|
||||
img.onerror = () => {
|
||||
img.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZjBmMGYwIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCwgc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzk5OTk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPuWbvueJh+WKoOi9veWksei0pTwvdGV4dD48L3N2Zz4=';
|
||||
};
|
||||
|
||||
const info = document.createElement('div');
|
||||
info.className = 'image-info';
|
||||
|
||||
const title = document.createElement('h4');
|
||||
title.textContent = `AI生成图片 #${index + 1}`;
|
||||
|
||||
const description = document.createElement('p');
|
||||
description.textContent = image.prompt ?
|
||||
(image.prompt.length > 100 ? image.prompt.substring(0, 100) + '...' : image.prompt) :
|
||||
'基于您的需求生成的图片';
|
||||
|
||||
const meta = document.createElement('div');
|
||||
meta.className = 'image-meta';
|
||||
|
||||
const dimensions = document.createElement('span');
|
||||
dimensions.textContent = `${image.dimensions?.width || 1024}×${image.dimensions?.height || 1024}`;
|
||||
|
||||
const time = document.createElement('span');
|
||||
time.textContent = formatTime(image.created_at);
|
||||
|
||||
meta.appendChild(dimensions);
|
||||
meta.appendChild(time);
|
||||
|
||||
info.appendChild(title);
|
||||
info.appendChild(description);
|
||||
info.appendChild(meta);
|
||||
|
||||
card.appendChild(img);
|
||||
card.appendChild(info);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
// 显示图片详情
|
||||
function showImageDetails(index) {
|
||||
currentImageIndex = index;
|
||||
const image = generatedImages[index];
|
||||
|
||||
document.getElementById('detail-image').src = image.url;
|
||||
document.getElementById('detail-dimensions').textContent =
|
||||
`${image.dimensions?.width || 1024}×${image.dimensions?.height || 1024}`;
|
||||
document.getElementById('detail-time').textContent = formatTime(image.created_at);
|
||||
document.getElementById('detail-prompt').textContent = image.prompt || '无提示词信息';
|
||||
document.getElementById('detail-service').textContent = image.service || '演示模式';
|
||||
document.getElementById('detail-seed').textContent = image.seed || '随机';
|
||||
|
||||
document.getElementById('image-details').style.display = 'flex';
|
||||
}
|
||||
|
||||
// 关闭详情面板
|
||||
function closeDetails() {
|
||||
document.getElementById('image-details').style.display = 'none';
|
||||
}
|
||||
|
||||
// 下载图片
|
||||
function downloadImage() {
|
||||
const image = generatedImages[currentImageIndex];
|
||||
downloadImageFile(image.url, `ai-generated-${currentImageIndex + 1}.png`);
|
||||
}
|
||||
|
||||
// 下载所有图片
|
||||
function downloadAll() {
|
||||
generatedImages.forEach((image, index) => {
|
||||
setTimeout(() => {
|
||||
downloadImageFile(image.url, `ai-generated-${index + 1}.png`);
|
||||
}, index * 500); // 延迟下载避免浏览器阻止
|
||||
});
|
||||
}
|
||||
|
||||
// 下载图片文件
|
||||
function downloadImageFile(url, filename) {
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
// 分享图片
|
||||
function shareImage() {
|
||||
const image = generatedImages[currentImageIndex];
|
||||
|
||||
if (navigator.share) {
|
||||
// 使用Web Share API
|
||||
navigator.share({
|
||||
title: 'AI生成的图片',
|
||||
text: '看看我用AI生成的图片!',
|
||||
url: image.url
|
||||
}).catch(console.error);
|
||||
} else {
|
||||
// 复制链接到剪贴板
|
||||
navigator.clipboard.writeText(image.url).then(() => {
|
||||
showNotification('图片链接已复制到剪贴板', 'success');
|
||||
}).catch(() => {
|
||||
showNotification('分享功能暂不可用', 'error');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑图片(未来功能)
|
||||
function editImage() {
|
||||
showNotification('图片编辑功能即将推出', 'info');
|
||||
}
|
||||
|
||||
// 重新生成图片
|
||||
async function regenerateImages() {
|
||||
if (confirm('确定要重新生成图片吗?这将替换当前的结果。')) {
|
||||
await startGeneration();
|
||||
}
|
||||
}
|
||||
|
||||
// 重试生成
|
||||
async function retryGeneration() {
|
||||
await startGeneration();
|
||||
}
|
||||
|
||||
// 显示错误
|
||||
function showError(message) {
|
||||
document.getElementById('generation-progress').style.display = 'none';
|
||||
document.getElementById('results-container').style.display = 'none';
|
||||
document.getElementById('error-container').style.display = 'block';
|
||||
|
||||
document.getElementById('error-message').textContent = message;
|
||||
}
|
||||
|
||||
// 返回表单页面
|
||||
function goBack() {
|
||||
window.location.href = 'index.html';
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
function formatTime(timestamp) {
|
||||
if (!timestamp) return '刚刚';
|
||||
|
||||
const date = new Date(timestamp);
|
||||
const now = new Date();
|
||||
const diff = now - date;
|
||||
|
||||
if (diff < 60000) return '刚刚';
|
||||
if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前';
|
||||
if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前';
|
||||
|
||||
return date.toLocaleDateString('zh-CN');
|
||||
}
|
||||
|
||||
// 睡眠函数
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// 显示通知
|
||||
function showNotification(message, type) {
|
||||
const notification = document.createElement('div');
|
||||
notification.style.position = 'fixed';
|
||||
notification.style.top = '20px';
|
||||
notification.style.right = '20px';
|
||||
notification.style.padding = '15px 20px';
|
||||
notification.style.borderRadius = '8px';
|
||||
notification.style.color = 'white';
|
||||
notification.style.fontWeight = '600';
|
||||
notification.style.zIndex = '1001';
|
||||
notification.style.maxWidth = '400px';
|
||||
notification.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
|
||||
|
||||
switch(type) {
|
||||
case 'success':
|
||||
notification.style.background = '#38a169';
|
||||
notification.innerHTML = '✅ ' + message;
|
||||
break;
|
||||
case 'error':
|
||||
notification.style.background = '#e53e3e';
|
||||
notification.innerHTML = '❌ ' + message;
|
||||
break;
|
||||
case 'info':
|
||||
notification.style.background = '#3182ce';
|
||||
notification.innerHTML = 'ℹ️ ' + message;
|
||||
break;
|
||||
default:
|
||||
notification.style.background = '#718096';
|
||||
notification.innerHTML = message;
|
||||
}
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
setTimeout(() => {
|
||||
if (notification.parentNode) {
|
||||
notification.parentNode.removeChild(notification);
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 显示API设置面板
|
||||
function showApiSetup() {
|
||||
document.getElementById('api-setup').style.display = 'flex';
|
||||
}
|
||||
|
||||
// 键盘事件处理
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
closeDetails();
|
||||
// 也可以关闭API设置面板
|
||||
document.getElementById('api-setup').style.display = 'none';
|
||||
}
|
||||
});
|
293
script.js
Normal file
293
script.js
Normal file
|
@ -0,0 +1,293 @@
|
|||
// DOM加载完成后执行
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const form = document.getElementById('imageRequestForm');
|
||||
const submitBtn = document.querySelector('.submit-btn');
|
||||
|
||||
// 表单提交处理
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
handleFormSubmit();
|
||||
});
|
||||
|
||||
// 文件上传预览功能
|
||||
setupFilePreview();
|
||||
|
||||
// 实时表单验证
|
||||
setupFormValidation();
|
||||
});
|
||||
|
||||
// 处理表单提交
|
||||
function handleFormSubmit() {
|
||||
const submitBtn = document.querySelector('.submit-btn');
|
||||
const originalText = submitBtn.textContent;
|
||||
|
||||
// 显示加载状态
|
||||
submitBtn.innerHTML = '<span class="loading"></span> 正在准备生成...';
|
||||
submitBtn.disabled = true;
|
||||
|
||||
// 收集表单数据
|
||||
const formData = collectFormData();
|
||||
|
||||
// 验证表单数据
|
||||
if (!validateFormData(formData)) {
|
||||
resetSubmitButton(submitBtn, originalText);
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存表单数据到localStorage(作为备份)
|
||||
try {
|
||||
localStorage.setItem('current_form_data', JSON.stringify(formData));
|
||||
console.log('表单数据已保存到localStorage');
|
||||
} catch (e) {
|
||||
console.warn('localStorage不可用,将使用URL参数传递数据');
|
||||
}
|
||||
|
||||
// 将表单数据编码为URL参数
|
||||
const encodedData = encodeURIComponent(JSON.stringify(formData));
|
||||
|
||||
// 短暂延迟后跳转到结果页面,携带数据参数
|
||||
setTimeout(() => {
|
||||
window.location.href = `results.html?data=${encodedData}`;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 收集表单数据
|
||||
function collectFormData() {
|
||||
const form = document.getElementById('imageRequestForm');
|
||||
const formData = new FormData(form);
|
||||
|
||||
// 转换为对象格式
|
||||
const data = {
|
||||
dimensions: {
|
||||
width: parseInt(formData.get('width')) || 1024,
|
||||
height: parseInt(formData.get('height')) || 1024,
|
||||
format: formData.get('format') || 'jpg'
|
||||
},
|
||||
style: {
|
||||
type: formData.get('style') || '',
|
||||
colorScheme: formData.get('colorScheme') || ''
|
||||
},
|
||||
platforms: formData.getAll('platform'),
|
||||
description: formData.get('description') || '',
|
||||
targetAudience: formData.get('targetAudience') || '',
|
||||
files: {
|
||||
productImages: Array.from(formData.getAll('productImages')).map(file => file.name),
|
||||
referenceImages: Array.from(formData.getAll('referenceImages')).map(file => file.name),
|
||||
brandGuide: formData.get('brandGuide') ? formData.get('brandGuide').name : null
|
||||
}
|
||||
};
|
||||
|
||||
console.log('收集到的表单数据:', data);
|
||||
return data;
|
||||
}
|
||||
|
||||
// 验证表单数据
|
||||
function validateFormData(data) {
|
||||
const errors = [];
|
||||
|
||||
// 验证尺寸
|
||||
if (data.dimensions.width < 100 || data.dimensions.width > 4000) {
|
||||
errors.push('图片宽度必须在100-4000像素之间');
|
||||
}
|
||||
|
||||
if (data.dimensions.height < 100 || data.dimensions.height > 4000) {
|
||||
errors.push('图片高度必须在100-4000像素之间');
|
||||
}
|
||||
|
||||
// 验证必填字段
|
||||
if (!data.description || data.description.trim().length < 10) {
|
||||
errors.push('请提供至少10个字符的图片描述');
|
||||
}
|
||||
|
||||
if (data.platforms.length === 0) {
|
||||
errors.push('请至少选择一个应用平台');
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
showError('请修正以下问题:\n' + errors.join('\n'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 模拟图片生成API调用
|
||||
function simulateImageGeneration(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 模拟网络延迟
|
||||
setTimeout(() => {
|
||||
// 模拟成功率90%
|
||||
if (Math.random() > 0.1) {
|
||||
resolve({
|
||||
success: true,
|
||||
imageId: 'img_' + Date.now(),
|
||||
estimatedTime: '2-3分钟'
|
||||
});
|
||||
} else {
|
||||
reject(new Error('服务器暂时繁忙'));
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
// 设置文件上传预览
|
||||
function setupFilePreview() {
|
||||
const fileInputs = document.querySelectorAll('input[type="file"]');
|
||||
|
||||
fileInputs.forEach(input => {
|
||||
input.addEventListener('change', function(e) {
|
||||
const files = e.target.files;
|
||||
showFileInfo(input, files);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 显示文件信息
|
||||
function showFileInfo(input, files) {
|
||||
// 移除之前的文件信息
|
||||
const existingInfo = input.parentNode.querySelector('.file-info');
|
||||
if (existingInfo) {
|
||||
existingInfo.remove();
|
||||
}
|
||||
|
||||
if (files.length > 0) {
|
||||
const fileInfo = document.createElement('div');
|
||||
fileInfo.className = 'file-info';
|
||||
fileInfo.style.marginTop = '10px';
|
||||
fileInfo.style.padding = '10px';
|
||||
fileInfo.style.background = '#e6fffa';
|
||||
fileInfo.style.borderRadius = '6px';
|
||||
fileInfo.style.fontSize = '0.9rem';
|
||||
|
||||
const fileList = Array.from(files).map(file =>
|
||||
`📎 ${file.name} (${formatFileSize(file.size)})`
|
||||
).join('<br>');
|
||||
|
||||
fileInfo.innerHTML = `<strong>已选择文件:</strong><br>${fileList}`;
|
||||
input.parentNode.appendChild(fileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化文件大小
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// 设置表单验证
|
||||
function setupFormValidation() {
|
||||
const inputs = document.querySelectorAll('input, select, textarea');
|
||||
|
||||
inputs.forEach(input => {
|
||||
input.addEventListener('blur', function() {
|
||||
validateField(this);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 验证单个字段
|
||||
function validateField(field) {
|
||||
const value = field.value.trim();
|
||||
let isValid = true;
|
||||
let message = '';
|
||||
|
||||
// 移除之前的错误提示
|
||||
removeFieldError(field);
|
||||
|
||||
// 根据字段类型进行验证
|
||||
switch(field.name) {
|
||||
case 'width':
|
||||
case 'height':
|
||||
const num = parseInt(value);
|
||||
if (num < 100 || num > 4000) {
|
||||
isValid = false;
|
||||
message = '尺寸必须在100-4000像素之间';
|
||||
}
|
||||
break;
|
||||
case 'description':
|
||||
if (value.length < 10) {
|
||||
isValid = false;
|
||||
message = '描述至少需要10个字符';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
showFieldError(field, message);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示字段错误
|
||||
function showFieldError(field, message) {
|
||||
field.style.borderColor = '#e53e3e';
|
||||
|
||||
const errorDiv = document.createElement('div');
|
||||
errorDiv.className = 'field-error';
|
||||
errorDiv.style.color = '#e53e3e';
|
||||
errorDiv.style.fontSize = '0.8rem';
|
||||
errorDiv.style.marginTop = '5px';
|
||||
errorDiv.textContent = message;
|
||||
|
||||
field.parentNode.appendChild(errorDiv);
|
||||
}
|
||||
|
||||
// 移除字段错误
|
||||
function removeFieldError(field) {
|
||||
field.style.borderColor = '#e2e8f0';
|
||||
const errorDiv = field.parentNode.querySelector('.field-error');
|
||||
if (errorDiv) {
|
||||
errorDiv.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// 重置提交按钮
|
||||
function resetSubmitButton(btn, originalText) {
|
||||
btn.innerHTML = originalText;
|
||||
btn.disabled = false;
|
||||
}
|
||||
|
||||
// 显示成功消息
|
||||
function showSuccess(message) {
|
||||
showNotification(message, 'success');
|
||||
}
|
||||
|
||||
// 显示错误消息
|
||||
function showError(message) {
|
||||
showNotification(message, 'error');
|
||||
}
|
||||
|
||||
// 显示通知
|
||||
function showNotification(message, type) {
|
||||
// 创建通知元素
|
||||
const notification = document.createElement('div');
|
||||
notification.style.position = 'fixed';
|
||||
notification.style.top = '20px';
|
||||
notification.style.right = '20px';
|
||||
notification.style.padding = '15px 20px';
|
||||
notification.style.borderRadius = '8px';
|
||||
notification.style.color = 'white';
|
||||
notification.style.fontWeight = '600';
|
||||
notification.style.zIndex = '1000';
|
||||
notification.style.maxWidth = '400px';
|
||||
notification.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
|
||||
|
||||
if (type === 'success') {
|
||||
notification.style.background = '#38a169';
|
||||
notification.innerHTML = '✅ ' + message;
|
||||
} else {
|
||||
notification.style.background = '#e53e3e';
|
||||
notification.innerHTML = '❌ ' + message;
|
||||
}
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// 3秒后自动移除
|
||||
setTimeout(() => {
|
||||
if (notification.parentNode) {
|
||||
notification.parentNode.removeChild(notification);
|
||||
}
|
||||
}, 3000);
|
||||
}
|
216
styles.css
Normal file
216
styles.css
Normal file
|
@ -0,0 +1,216 @@
|
|||
/* 全局样式 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
header p {
|
||||
font-size: 1.2rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 表单容器 */
|
||||
.form-container {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
/* 表单区块 */
|
||||
.form-section {
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.form-section:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.form-section h2 {
|
||||
color: #4a5568;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 输入组样式 */
|
||||
.input-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 600;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.input-group input,
|
||||
.input-group select,
|
||||
.input-group textarea {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
.input-group input:focus,
|
||||
.input-group select:focus,
|
||||
.input-group textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
/* 复选框组 */
|
||||
.checkbox-group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.checkbox-group label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px;
|
||||
background: #f7fafc;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.checkbox-group label:hover {
|
||||
background: #edf2f7;
|
||||
}
|
||||
|
||||
.checkbox-group input[type="checkbox"] {
|
||||
width: auto;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 上传组样式 */
|
||||
.upload-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.upload-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.upload-group input[type="file"] {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 2px dashed #cbd5e0;
|
||||
border-radius: 8px;
|
||||
background: #f7fafc;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.upload-group input[type="file"]:hover {
|
||||
border-color: #667eea;
|
||||
background: #edf2f7;
|
||||
}
|
||||
|
||||
.upload-group small {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
color: #718096;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 提交按钮 */
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.submit-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid #667eea;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue