Add files via upload

初始版本:AI图片生成器
This commit is contained in:
skylarGW 2025-08-09 20:27:03 +08:00 committed by GitHub
parent e6e2277a15
commit 88f89e7742
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 2027 additions and 0 deletions

340
ai-service.js Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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); }
}