初始提交
This commit is contained in:
126
.gitignore
vendored
Normal file
126
.gitignore
vendored
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Production build
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage/
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
public
|
||||||
|
|
||||||
|
# Storybook build outputs
|
||||||
|
.out
|
||||||
|
.storybook-out
|
||||||
|
|
||||||
|
# Temporary folders
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS generated files
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Project specific
|
||||||
|
*.local
|
||||||
|
*.secret
|
||||||
|
*.key
|
||||||
|
*.pem
|
||||||
|
*.p12
|
||||||
|
*.pfx
|
||||||
|
|
||||||
|
# Cache
|
||||||
|
.cache/
|
||||||
|
*.cache
|
||||||
|
|
||||||
|
# Test files
|
||||||
|
test/
|
||||||
|
tests/
|
||||||
|
*.test.*
|
||||||
|
*.spec.*
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
docs/
|
||||||
|
*.md
|
||||||
|
!README.md
|
||||||
|
|
||||||
|
# Backup files
|
||||||
|
*.bak
|
||||||
|
*.backup
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 OVINC CN
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
185
README.md
Normal file
185
README.md
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# OpenRouter Image Generator Web应用
|
||||||
|
|
||||||
|
这是一个基于OpenRouter API的图像生成Web应用,实现了与原始Python代码相同的功能,但提供了用户友好的Web界面。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
### 核心功能
|
||||||
|
- 🎨 **图像生成**: 通过OpenRouter API生成高质量图像
|
||||||
|
- 📤 **图像上传**: 支持拖拽上传参考图像
|
||||||
|
- 💬 **对话界面**: 直观的聊天式交互界面
|
||||||
|
- 🖼️ **图像画廊**: 展示和管理生成的图像
|
||||||
|
- ⚙️ **API配置**: 灵活的API设置和连接测试
|
||||||
|
|
||||||
|
### 技术特性
|
||||||
|
- 🌐 **响应式设计**: 适配各种设备屏幕
|
||||||
|
- 🎯 **实时预览**: 即时显示上传的图像
|
||||||
|
- 💾 **本地存储**: 自动保存用户设置
|
||||||
|
- 🔗 **连接状态**: 实时显示API连接状态
|
||||||
|
- 📱 **移动友好**: 支持触摸操作
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 获取OpenRouter API密钥
|
||||||
|
|
||||||
|
1. 访问 [OpenRouter官网](https://openrouter.ai/)
|
||||||
|
2. 注册账户并登录
|
||||||
|
3. 在控制台中生成API密钥
|
||||||
|
|
||||||
|
### 2. 配置应用
|
||||||
|
|
||||||
|
1. 打开 `index.html` 文件
|
||||||
|
2. 在"API设置"面板中输入您的API密钥
|
||||||
|
3. 点击"测试连接"按钮验证配置
|
||||||
|
|
||||||
|
### 3. 使用应用
|
||||||
|
|
||||||
|
#### 上传参考图像(可选)
|
||||||
|
- 拖拽图像文件到上传区域
|
||||||
|
- 或点击"选择图像"按钮
|
||||||
|
- 支持多种图像格式(JPG、PNG、GIF等)
|
||||||
|
|
||||||
|
#### 生成图像
|
||||||
|
1. 在对话输入框中输入您的图像生成请求
|
||||||
|
2. 点击"发送"按钮或按Enter键
|
||||||
|
3. 等待API处理并生成结果
|
||||||
|
|
||||||
|
#### 管理生成的图像
|
||||||
|
- 在图像画廊中查看所有生成的图像
|
||||||
|
- 悬停在图像上显示操作按钮
|
||||||
|
- 下载图像或复制图像URL
|
||||||
|
|
||||||
|
## 配置选项
|
||||||
|
|
||||||
|
### API设置
|
||||||
|
- **API Key**: 您的OpenRouter API密钥
|
||||||
|
- **Base URL**: OpenRouter API的基础URL(默认:https://openrouter.ai/api/v1)
|
||||||
|
- **模型**: 选择使用的AI模型
|
||||||
|
- Google Gemini 2.5 Flash Image Preview (Free)
|
||||||
|
- OpenAI GPT-4 Vision Preview
|
||||||
|
- Anthropic Claude 3 Sonnet
|
||||||
|
- **超时时间**: 请求超时时间(秒)
|
||||||
|
- **代理**: 可选的代理服务器设置
|
||||||
|
|
||||||
|
### 支持的模型
|
||||||
|
- `google/gemini-2.5-flash-image-preview:free` - 免费的Google Gemini模型
|
||||||
|
- `openai/gpt-4-vision-preview` - OpenAI的GPT-4视觉预览版
|
||||||
|
- `anthropic/claude-3-sonnet-20240229` - Anthropic的Claude 3 Sonnet模型
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 基本图像生成
|
||||||
|
```
|
||||||
|
请生成一张美丽的日落风景图像
|
||||||
|
```
|
||||||
|
|
||||||
|
### 基于参考图像的生成
|
||||||
|
1. 上传一张参考图像
|
||||||
|
2. 输入提示:`请根据上传的图像风格,生成一张类似的现代建筑图像`
|
||||||
|
|
||||||
|
### 复杂场景生成
|
||||||
|
```
|
||||||
|
请生成一张未来城市的图像,包含飞行汽车、玻璃建筑和绿色植物,风格要写实且具有科技感
|
||||||
|
```
|
||||||
|
|
||||||
|
## 技术实现
|
||||||
|
|
||||||
|
### 前端技术栈
|
||||||
|
- **HTML5**: 语义化标记和现代Web API
|
||||||
|
- **CSS3**: 响应式设计和动画效果
|
||||||
|
- **JavaScript**: 原生JS实现所有交互功能
|
||||||
|
- **Bootstrap 5**: UI组件和响应式布局
|
||||||
|
- **Font Awesome**: 图标库
|
||||||
|
|
||||||
|
### 核心功能实现
|
||||||
|
|
||||||
|
#### API调用
|
||||||
|
```javascript
|
||||||
|
async function generateImage(message) {
|
||||||
|
const payload = {
|
||||||
|
model: model,
|
||||||
|
messages: messages,
|
||||||
|
stream: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${apiKey}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 图像处理
|
||||||
|
- 支持Base64编码的图像数据
|
||||||
|
- 文件拖拽上传功能
|
||||||
|
- 图像预览和管理
|
||||||
|
- 图像下载和URL复制
|
||||||
|
|
||||||
|
#### 状态管理
|
||||||
|
- 本地存储用户设置
|
||||||
|
- 实时连接状态显示
|
||||||
|
- 加载状态指示器
|
||||||
|
- 错误处理和用户反馈
|
||||||
|
|
||||||
|
## 浏览器兼容性
|
||||||
|
|
||||||
|
- ✅ Chrome 80+
|
||||||
|
- ✅ Firefox 75+
|
||||||
|
- ✅ Safari 13+
|
||||||
|
- ✅ Edge 80+
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **API限制**: 请注意OpenRouter API的使用限制和费用
|
||||||
|
2. **图像大小**: 上传的图像文件大小建议不超过10MB
|
||||||
|
3. **网络要求**: 需要稳定的网络连接以访问OpenRouter API
|
||||||
|
4. **隐私安全**: API密钥存储在浏览器本地,请勿在公共设备上使用
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
|
||||||
|
### 常见问题
|
||||||
|
|
||||||
|
**Q: 连接测试失败**
|
||||||
|
- 检查API密钥是否正确
|
||||||
|
- 确认网络连接正常
|
||||||
|
- 验证Base URL设置
|
||||||
|
|
||||||
|
**Q: 图像生成失败**
|
||||||
|
- 检查输入的提示词是否合适
|
||||||
|
- 确认选择的模型支持图像生成
|
||||||
|
- 查看浏览器控制台错误信息
|
||||||
|
|
||||||
|
**Q: 图像上传失败**
|
||||||
|
- 检查图像格式是否支持
|
||||||
|
- 确认图像文件大小是否合理
|
||||||
|
- 尝试使用不同的浏览器
|
||||||
|
|
||||||
|
### 调试技巧
|
||||||
|
1. 打开浏览器开发者工具(F12)
|
||||||
|
2. 查看Console标签的错误信息
|
||||||
|
3. 检查Network标签的API请求状态
|
||||||
|
4. 验证API响应数据格式
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
本项目基于MIT许可证开源,详见LICENSE文件。
|
||||||
|
|
||||||
|
## 贡献
|
||||||
|
|
||||||
|
欢迎提交Issue和Pull Request来改进这个项目!
|
||||||
|
|
||||||
|
## 联系方式
|
||||||
|
|
||||||
|
如有问题或建议,请通过以下方式联系:
|
||||||
|
- GitHub Issues
|
||||||
|
- Email: [您的邮箱]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**注意**: 这是一个前端演示应用,生产环境使用时请注意API密钥的安全性。
|
||||||
75
config.json
Normal file
75
config.json
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"app": {
|
||||||
|
"name": "OpenRouter Image Generator",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "基于OpenRouter API的图像生成Web应用",
|
||||||
|
"author": "OVINC CN",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"api": {
|
||||||
|
"default_base_url": "https://openrouter.ai/api/v1",
|
||||||
|
"default_timeout": 600,
|
||||||
|
"default_model": "google/gemini-2.5-flash-image-preview:free",
|
||||||
|
"supported_models": [
|
||||||
|
{
|
||||||
|
"id": "google/gemini-2.5-flash-image-preview:free",
|
||||||
|
"name": "Google Gemini 2.5 Flash Image Preview",
|
||||||
|
"description": "免费的Google Gemini模型,支持图像生成和视觉理解",
|
||||||
|
"pricing": "free"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "openai/gpt-4-vision-preview",
|
||||||
|
"name": "OpenAI GPT-4 Vision Preview",
|
||||||
|
"description": "OpenAI的GPT-4视觉预览版,强大的图像理解和生成能力",
|
||||||
|
"pricing": "paid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "anthropic/claude-3-sonnet-20240229",
|
||||||
|
"name": "Anthropic Claude 3 Sonnet",
|
||||||
|
"description": "Anthropic的Claude 3 Sonnet模型,优秀的图像分析能力",
|
||||||
|
"pricing": "paid"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ui": {
|
||||||
|
"theme": {
|
||||||
|
"primary_color": "#667eea",
|
||||||
|
"secondary_color": "#764ba2",
|
||||||
|
"background_gradient": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"drag_drop": true,
|
||||||
|
"image_preview": true,
|
||||||
|
"chat_history": true,
|
||||||
|
"image_gallery": true,
|
||||||
|
"settings_panel": true,
|
||||||
|
"connection_status": true
|
||||||
|
},
|
||||||
|
"limits": {
|
||||||
|
"max_file_size": 10485760,
|
||||||
|
"max_files_per_upload": 5,
|
||||||
|
"supported_image_formats": ["image/jpeg", "image/png", "image/gif", "image/webp"],
|
||||||
|
"max_chat_history": 100,
|
||||||
|
"max_generated_images": 50
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"settings_key": "openRouterSettings",
|
||||||
|
"chat_history_key": "openRouterChatHistory",
|
||||||
|
"generated_images_key": "openRouterGeneratedImages"
|
||||||
|
},
|
||||||
|
"endpoints": {
|
||||||
|
"models": "/models",
|
||||||
|
"chat_completions": "/chat/completions",
|
||||||
|
"image_generation": "/images/generations"
|
||||||
|
},
|
||||||
|
"error_messages": {
|
||||||
|
"no_api_key": "请先输入API Key",
|
||||||
|
"connection_failed": "连接失败,请检查网络和API设置",
|
||||||
|
"invalid_response": "API响应格式错误",
|
||||||
|
"image_generation_failed": "图像生成失败",
|
||||||
|
"file_too_large": "文件大小超过限制",
|
||||||
|
"unsupported_format": "不支持的文件格式",
|
||||||
|
"upload_failed": "文件上传失败"
|
||||||
|
}
|
||||||
|
}
|
||||||
162
index.html
Normal file
162
index.html
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>OpenRouter Image Generator</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||||
|
<link href="styles.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h1 class="mb-0">
|
||||||
|
<i class="fas fa-image me-2"></i>
|
||||||
|
OpenRouter Image Generator
|
||||||
|
</h1>
|
||||||
|
<p class="mb-0 mt-2">基于OpenRouter API的智能图像生成工具</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<!-- 设置面板 -->
|
||||||
|
<div class="settings-panel">
|
||||||
|
<h5><i class="fas fa-cog me-2"></i>API设置</h5>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="apiKey" class="form-label">API Key</label>
|
||||||
|
<input type="password" class="form-control" id="apiKey" placeholder="输入您的OpenRouter API Key">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="baseUrl" class="form-label">Base URL</label>
|
||||||
|
<input type="url" class="form-control" id="baseUrl" value="https://openrouter.ai/api/v1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="model" class="form-label">模型</label>
|
||||||
|
<select class="form-select" id="model">
|
||||||
|
<option value="google/gemini-2.5-flash-image-preview:free">Google Gemini 2.5 Flash Image Preview (Free)</option>
|
||||||
|
<option value="openai/gpt-4-vision-preview">OpenAI GPT-4 Vision Preview</option>
|
||||||
|
<option value="anthropic/claude-3-sonnet-20240229">Anthropic Claude 3 Sonnet</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="timeout" class="form-label">超时时间 (秒)</label>
|
||||||
|
<input type="number" class="form-control" id="timeout" value="600">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="proxy" class="form-label">代理 (可选)</label>
|
||||||
|
<input type="url" class="form-control" id="proxy" placeholder="http://proxy:port">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<span class="status-indicator status-disconnected" id="connectionStatus"></span>
|
||||||
|
<span id="connectionText">未连接</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-outline-primary me-2" onclick="testConnection()">
|
||||||
|
<i class="fas fa-plug me-2"></i>测试连接
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-warning" onclick="app.clearStorage()">
|
||||||
|
<i class="fas fa-broom me-2"></i>清除存储数据
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 图像上传区域 -->
|
||||||
|
<div class="mb-4">
|
||||||
|
<h5><i class="fas fa-upload me-2"></i>上传参考图像</h5>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="border-2 border-dashed rounded p-4 text-center h-100 d-flex flex-column justify-content-center" id="dropZone">
|
||||||
|
<i class="fas fa-cloud-upload-alt fa-3x text-muted mb-3"></i>
|
||||||
|
<p class="text-muted">拖拽图像到此处或点击选择文件</p>
|
||||||
|
<input type="file" class="d-none" id="imageInput" accept="image/*">
|
||||||
|
<button class="btn btn-outline-primary" onclick="document.getElementById('imageInput').click()">
|
||||||
|
选择图像
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div id="imagePreview" class="h-100 d-flex align-items-center justify-content-center">
|
||||||
|
<p class="text-muted">未选择图像</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 聊天输入区域 -->
|
||||||
|
<div class="mb-4">
|
||||||
|
<h5><i class="fas fa-comments me-2"></i>对话输入</h5>
|
||||||
|
<div class="input-group">
|
||||||
|
<textarea class="form-control" id="messageInput" rows="3" placeholder="输入您的图像生成请求..."></textarea>
|
||||||
|
<button class="btn btn-primary" onclick="sendMessage()">
|
||||||
|
<i class="fas fa-paper-plane me-2"></i>发送
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 加载指示器 -->
|
||||||
|
<div class="loading-spinner" id="loadingSpinner">
|
||||||
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
<span class="visually-hidden">加载中...</span>
|
||||||
|
</div>
|
||||||
|
<p class="mt-2">正在生成图像,请稍候...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 聊天历史 -->
|
||||||
|
<div class="mb-4">
|
||||||
|
<h5><i class="fas fa-history me-2"></i>对话历史</h5>
|
||||||
|
<div id="chatHistory" style="max-height: 400px; overflow-y: auto;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 生成的图像画廊 -->
|
||||||
|
<div class="mb-4">
|
||||||
|
<h5><i class="fas fa-images me-2"></i>生成的图像</h5>
|
||||||
|
<div class="image-gallery" id="imageGallery"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 新的图像查看模态框 -->
|
||||||
|
<div class="modal fade" id="imageViewerModal" tabindex="-1" aria-labelledby="imageViewerModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="imageViewerModalLabel">图像查看</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body text-center">
|
||||||
|
<img id="viewerModalImage" src="" alt="查看图像" class="img-fluid">
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="viewerDownloadImage">
|
||||||
|
<i class="fas fa-download me-2"></i>下载图像
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
462
styles.css
Normal file
462
styles.css
Normal file
@@ -0,0 +1,462 @@
|
|||||||
|
/* OpenRouter Image Generator - 自定义样式 */
|
||||||
|
|
||||||
|
/* 全局样式 */
|
||||||
|
:root {
|
||||||
|
--primary-color: #667eea;
|
||||||
|
--secondary-color: #764ba2;
|
||||||
|
--success-color: #28a745;
|
||||||
|
--danger-color: #dc3545;
|
||||||
|
--warning-color: #ffc107;
|
||||||
|
--info-color: #17a2b8;
|
||||||
|
--light-color: #f8f9fa;
|
||||||
|
--dark-color: #343a40;
|
||||||
|
--border-radius: 15px;
|
||||||
|
--box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||||
|
--transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 基础样式重置 */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: var(--dark-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主容器 */
|
||||||
|
.main-container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片样式 */
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
box-shadow: var(--box-shadow);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: var(--border-radius) var(--border-radius) 0 0 !important;
|
||||||
|
padding: 20px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表单控件样式 */
|
||||||
|
.form-control, .form-select {
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #e9ecef;
|
||||||
|
padding: 12px;
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus, .form-select:focus {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮样式 */
|
||||||
|
.btn {
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px 30px;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: var(--transition);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||||
|
background: linear-gradient(135deg, var(--secondary-color) 0%, var(--primary-color) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-primary {
|
||||||
|
border: 2px solid var(--primary-color);
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-primary:hover {
|
||||||
|
background: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-danger {
|
||||||
|
border: 2px solid var(--danger-color);
|
||||||
|
color: var(--danger-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-danger:hover {
|
||||||
|
background: var(--danger-color);
|
||||||
|
border-color: var(--danger-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图像预览样式 */
|
||||||
|
.image-preview {
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图像预览区域 */
|
||||||
|
#imagePreview {
|
||||||
|
max-height: 20rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 拖拽区域样式 */
|
||||||
|
#dropZone {
|
||||||
|
border: 2px dashed #ccc;
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: var(--transition);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dropZone:hover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
background: rgba(102, 126, 234, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
#dropZone.dragover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
background: rgba(102, 126, 234, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 聊天消息样式 */
|
||||||
|
.chat-message {
|
||||||
|
background: var(--light-color);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-left: 4px solid var(--primary-color);
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message:hover {
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message.user {
|
||||||
|
background: #e3f2fd;
|
||||||
|
border-left-color: #2196f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message.assistant {
|
||||||
|
background: #f3e5f5;
|
||||||
|
border-left-color: #9c27b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加载动画 */
|
||||||
|
.loading-spinner {
|
||||||
|
display: none;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-border {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
border-width: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 设置面板样式 */
|
||||||
|
.settings-panel {
|
||||||
|
background: var(--light-color);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图像画廊样式 */
|
||||||
|
.image-gallery {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-item {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-item:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-item img {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-item:hover .image-overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 状态指示器 */
|
||||||
|
.status-indicator {
|
||||||
|
display: inline-block;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 5px;
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-connected {
|
||||||
|
background-color: var(--success-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-disconnected {
|
||||||
|
background-color: var(--danger-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 动画效果 */
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.7);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 10px rgba(40, 167, 69, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeIn 0.5s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.main-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-gallery {
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control, .form-select {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.image-gallery {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 工具提示样式 */
|
||||||
|
.tooltip {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip .tooltiptext {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 200px;
|
||||||
|
background-color: #555;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 5px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
bottom: 125%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -100px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover .tooltiptext {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条样式 */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: #f1f1f1;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--primary-color);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 特殊效果 */
|
||||||
|
.glass-effect {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gradient {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加载骨架屏 */
|
||||||
|
.skeleton {
|
||||||
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: loading 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
0% {
|
||||||
|
background-position: 200% 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: -200% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图像查看模态框样式 */
|
||||||
|
#viewerModalImage {
|
||||||
|
max-height: 80vh;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewerModalImage:hover {
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图像查看模态框特定样式 */
|
||||||
|
#imageViewerModal .modal-content {
|
||||||
|
border-radius: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#imageViewerModal .modal-header {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#imageViewerModal .modal-footer {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮网格布局 - 2排2列 */
|
||||||
|
.btn-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-grid-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-grid-row .btn {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 错误状态 */
|
||||||
|
.error-state {
|
||||||
|
border-color: var(--danger-color) !important;
|
||||||
|
background-color: rgba(220, 53, 69, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 成功状态 */
|
||||||
|
.success-state {
|
||||||
|
border-color: var(--success-color) !important;
|
||||||
|
background-color: rgba(40, 167, 69, 0.1) !important;
|
||||||
|
}
|
||||||
138
test.html
Normal file
138
test.html
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>OpenRouter Image Generator - 测试页面</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||||
|
<link href="styles.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container mt-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h2 class="text-center">OpenRouter Image Generator - 测试页面</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<i class="fas fa-info-circle me-2"></i>
|
||||||
|
这是测试页面,用于验证应用是否正常工作。请检查浏览器控制台是否有错误信息。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<h5>功能测试清单:</h5>
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<i class="fas fa-check-circle text-success me-2"></i>
|
||||||
|
页面加载正常
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<i class="fas fa-check-circle text-success me-2"></i>
|
||||||
|
JavaScript文件加载成功
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<i class="fas fa-check-circle text-success me-2"></i>
|
||||||
|
事件监听器初始化完成
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<i class="fas fa-check-circle text-success me-2"></i>
|
||||||
|
UI控制器正常工作
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<i class="fas fa-check-circle text-success me-2"></i>
|
||||||
|
文件处理功能正常
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<a href="index.html" class="btn btn-primary">
|
||||||
|
<i class="fas fa-home me-2"></i>
|
||||||
|
返回主应用
|
||||||
|
</a>
|
||||||
|
<button class="btn btn-secondary" onclick="testFunctions()">
|
||||||
|
<i class="fas fa-flask me-2"></i>
|
||||||
|
测试核心功能
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="testResults" class="mt-3"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script>
|
||||||
|
function testFunctions() {
|
||||||
|
const results = document.getElementById('testResults');
|
||||||
|
results.innerHTML = '<div class="alert alert-info">正在测试功能...</div>';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
let testResults = '<div class="alert alert-success"><h5>测试结果:</h5><ul>';
|
||||||
|
|
||||||
|
// 测试1: 检查app对象是否存在
|
||||||
|
if (typeof app !== 'undefined') {
|
||||||
|
testResults += '<li class="text-success">✓ app对象存在</li>';
|
||||||
|
} else {
|
||||||
|
testResults += '<li class="text-danger">✗ app对象不存在</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试2: 检查uiController是否存在
|
||||||
|
if (typeof uiController !== 'undefined') {
|
||||||
|
testResults += '<li class="text-success">✓ uiController存在</li>';
|
||||||
|
} else {
|
||||||
|
testResults += '<li class="text-danger">✗ uiController不存在</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试3: 检查apiService是否存在
|
||||||
|
if (typeof apiService !== 'undefined') {
|
||||||
|
testResults += '<li class="text-success">✓ apiService存在</li>';
|
||||||
|
} else {
|
||||||
|
testResults += '<li class="text-danger">✗ apiService不存在</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试4: 检查utils是否存在
|
||||||
|
if (typeof utils !== 'undefined') {
|
||||||
|
testResults += '<li class="text-success">✓ utils存在</li>';
|
||||||
|
} else {
|
||||||
|
testResults += '<li class="text-danger">✗ utils不存在</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试5: 检查fileHandler是否存在
|
||||||
|
if (typeof fileHandler !== 'undefined') {
|
||||||
|
testResults += '<li class="text-success">✓ fileHandler存在</li>';
|
||||||
|
} else {
|
||||||
|
testResults += '<li class="text-danger">✗ fileHandler不存在</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试6: 检查CONFIG是否存在
|
||||||
|
if (typeof CONFIG !== 'undefined') {
|
||||||
|
testResults += '<li class="text-success">✓ CONFIG存在</li>';
|
||||||
|
} else {
|
||||||
|
testResults += '<li class="text-danger">✗ CONFIG不存在</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
testResults += '</ul></div>';
|
||||||
|
results.innerHTML = testResults;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载完成后自动测试
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
console.log('测试页面加载完成');
|
||||||
|
console.log('检查全局对象:');
|
||||||
|
console.log('app:', typeof app);
|
||||||
|
console.log('uiController:', typeof uiController);
|
||||||
|
console.log('apiService:', typeof apiService);
|
||||||
|
console.log('utils:', typeof utils);
|
||||||
|
console.log('fileHandler:', typeof fileHandler);
|
||||||
|
console.log('CONFIG:', typeof CONFIG);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user