feat: 添加模型管理功能和使用指南页面
新增模型管理功能,包括模型列表展示、复制功能和样式 添加使用指南和配置教程页面,包含操作流程图和客户端配置说明 更新侧边栏导航和组件加载器以支持新页面
This commit is contained in:
parent
a6656e6b9a
commit
9d383fec26
9 changed files with 2642 additions and 0 deletions
|
|
@ -100,6 +100,8 @@ async function initializeComponents() {
|
|||
// 最后加载所有 section 组件
|
||||
const sectionComponents = [
|
||||
{ path: `${basePath}section-dashboard.html`, container: '#content-container', position: 'beforeend' },
|
||||
{ path: `${basePath}section-guide.html`, container: '#content-container', position: 'beforeend' },
|
||||
{ path: `${basePath}section-tutorial.html`, container: '#content-container', position: 'beforeend' },
|
||||
{ path: `${basePath}section-config.html`, container: '#content-container', position: 'beforeend' },
|
||||
{ path: `${basePath}section-upload-config.html`, container: '#content-container', position: 'beforeend' },
|
||||
{ path: `${basePath}section-providers.html`, container: '#content-container', position: 'beforeend' },
|
||||
|
|
|
|||
|
|
@ -26,12 +26,15 @@ const translations = {
|
|||
// Navigation
|
||||
'nav.main': '主导航',
|
||||
'nav.dashboard': '仪表盘',
|
||||
'nav.guide': '使用指南',
|
||||
'nav.tutorial': '配置教程',
|
||||
'nav.config': '配置管理',
|
||||
'nav.providers': '提供商池管理',
|
||||
'nav.upload': '凭据文件管理',
|
||||
'nav.usage': '用量查询',
|
||||
'nav.logs': '实时日志',
|
||||
'nav.plugins': '插件管理',
|
||||
'nav.models': '可用模型',
|
||||
|
||||
// Dashboard
|
||||
'dashboard.title': '系统概览',
|
||||
|
|
@ -523,6 +526,138 @@ const translations = {
|
|||
'plugins.load.failed': '加载插件列表失败',
|
||||
'plugins.restart.required': '更改已保存',
|
||||
|
||||
// Models
|
||||
'models.title': '可用模型列表',
|
||||
'models.note': '点击模型名称可直接复制到剪贴板',
|
||||
'models.empty': '暂无可用模型',
|
||||
'models.loadError': '加载模型列表失败',
|
||||
'models.copied': '已复制',
|
||||
'models.clickToCopy': '点击复制',
|
||||
|
||||
// Guide
|
||||
'guide.title': '使用指南',
|
||||
'guide.intro.title': '项目简介',
|
||||
'guide.intro.desc': 'AIClient2API 是一个突破客户端限制的 API 代理服务,将 Gemini、Antigravity、Qwen Code、Kiro 等原本仅限客户端内使用的免费大模型,转换为可供任何应用调用的标准 OpenAI 兼容接口。',
|
||||
'guide.intro.feature1.title': '统一接入',
|
||||
'guide.intro.feature1.desc': '通过标准 OpenAI 兼容协议,一次配置即可接入多种大模型',
|
||||
'guide.intro.feature2.title': '突破限制',
|
||||
'guide.intro.feature2.desc': '利用 OAuth 授权机制,有效突破免费 API 速率和配额限制',
|
||||
'guide.intro.feature3.title': '协议转换',
|
||||
'guide.intro.feature3.desc': '支持 OpenAI、Claude、Gemini 三大协议间的智能转换',
|
||||
'guide.intro.feature4.title': '账号池管理',
|
||||
'guide.intro.feature4.desc': '支持多账号轮询、自动故障转移和配置降级',
|
||||
'guide.providers.title': '支持的模型提供商',
|
||||
'guide.providers.badge.oauth': 'OAuth 授权',
|
||||
'guide.providers.badge.experimental': '实验性',
|
||||
'guide.providers.badge.free': '免费使用',
|
||||
'guide.providers.badge.official': '官方 API',
|
||||
'guide.providers.gemini.desc': '通过 Google OAuth 认证访问 Gemini 模型,支持 gemini-2.0-flash-exp 等模型',
|
||||
'guide.providers.antigravity.desc': '通过 Google 内部接口访问 Gemini 3 Pro、Claude Sonnet 4.5 等模型',
|
||||
'guide.providers.kiro.desc': '通过 Kiro 客户端免费使用 Claude Opus 4.5、Claude Sonnet 4.5 等模型',
|
||||
'guide.providers.qwen.desc': '通过阿里云 OAuth 认证访问 Qwen3 Coder Plus 等模型',
|
||||
'guide.providers.claude.desc': '使用 Claude 官方 API 或第三方代理访问 Claude 系列模型',
|
||||
'guide.providers.openai.desc': '使用 OpenAI 官方 API 或第三方代理访问 GPT 系列模型',
|
||||
'guide.providers.iflow.desc': '通过 iFlow OAuth 认证访问 Qwen、Kimi、DeepSeek、GLM 等模型',
|
||||
'guide.providers.orchids.desc': '通过 Orchids 平台免费使用 Claude Sonnet 4.5 等模型',
|
||||
'guide.client.title': '客户端配置指南',
|
||||
'guide.client.desc': '以下是常见 AI 客户端的配置方法,将 API 端点设置为本服务地址即可使用:',
|
||||
'guide.client.cherry.step1': '打开设置 → 模型服务商',
|
||||
'guide.client.cherry.step2': '添加自定义服务商',
|
||||
'guide.client.cherry.step3': '设置 API 地址为: http://localhost:3000/{provider}',
|
||||
'guide.client.cherry.step4': '填入 API Key(配置文件中的 REQUIRED_API_KEY)',
|
||||
'guide.client.cline.step1': '打开 VS Code 设置',
|
||||
'guide.client.cline.step2': '搜索 Cline 或 Continue 配置',
|
||||
'guide.client.cline.step3': '设置 API Base URL 为: http://localhost:3000/{provider}/v1',
|
||||
'guide.client.cline.step4': '填入 API Key 和模型名称',
|
||||
'guide.client.note': '提示:将 {provider} 替换为实际的提供商路径,如 gemini-cli-oauth、claude-kiro-oauth 等。可在仪表盘的路由示例中查看完整路径。',
|
||||
'guide.ollama.title': 'Ollama 协议使用',
|
||||
'guide.ollama.desc': '本项目支持 Ollama 协议,可以通过统一接口访问所有支持的模型。',
|
||||
'guide.ollama.listModels': '列出所有可用模型',
|
||||
'guide.ollama.chat': '聊天接口',
|
||||
'guide.faq.title': '常见问题',
|
||||
'guide.faq.q1': 'Q: 请求返回 404 错误怎么办?',
|
||||
'guide.faq.a1': 'A: 检查接口路径是否正确。某些客户端会自动在 Base URL 后追加路径,导致路径重复。请查看控制台中的实际请求 URL,移除多余的路径部分。',
|
||||
'guide.faq.q2': 'Q: 请求返回 429 错误怎么办?',
|
||||
'guide.faq.a2': 'A: 429 表示请求频率过高。建议配置多个账号到提供商池,启用轮询机制;或配置 Fallback 链实现跨类型降级。',
|
||||
'guide.faq.q3': 'Q: OAuth 授权失败怎么办?',
|
||||
'guide.faq.a3': 'A: 确保 OAuth 回调端口可访问(Gemini: 8085, Antigravity: 8086, Kiro: 19876-19880)。Docker 用户需确保已正确映射这些端口。',
|
||||
'guide.faq.q4': 'Q: 如何查看可用的模型列表?',
|
||||
'guide.faq.a4': 'A: 在侧边栏点击"可用模型"页面,可以查看所有已配置提供商支持的模型列表。点击模型名称可直接复制。',
|
||||
'guide.faq.q5': 'Q: 流式响应中断怎么办?',
|
||||
'guide.faq.a5': 'A: 检查网络稳定性,增加客户端请求超时时间。如使用代理,确保代理支持长连接。',
|
||||
|
||||
// Guide - Flow
|
||||
'guide.flow.title': '操作流程图',
|
||||
'guide.flow.step1.title': '配置管理',
|
||||
'guide.flow.step1.desc': '在「配置管理」页面设置基本参数',
|
||||
'guide.flow.step2.title': '生成授权',
|
||||
'guide.flow.step2.desc': '在「提供商池管理」页面生成 OAuth 授权',
|
||||
'guide.flow.step3.title': '管理凭据',
|
||||
'guide.flow.step3.desc': '在「凭据文件管理」页面查看和管理凭据',
|
||||
'guide.flow.step4.title': '开始使用',
|
||||
'guide.flow.step4.desc': '在「仪表盘」查看路由示例并开始调用 API',
|
||||
|
||||
// Tutorial
|
||||
'tutorial.title': '配置教程',
|
||||
'tutorial.config.title': '配置文件说明',
|
||||
'tutorial.config.desc': '所有配置文件都存放在 configs/ 目录下。主要配置文件包括:',
|
||||
'tutorial.config.badge.required': '必需',
|
||||
'tutorial.config.badge.optional': '可选',
|
||||
'tutorial.config.file.config': '主配置文件,包含 API Key、端口、模型提供商等核心设置',
|
||||
'tutorial.config.file.pools': '提供商池配置,用于多账号轮询和故障转移',
|
||||
'tutorial.config.file.plugins': '插件配置,用于启用或禁用系统插件',
|
||||
'tutorial.config.file.pwd': '后台登录密码文件,默认密码为 admin123',
|
||||
'tutorial.main.title': '主配置详解 (config.json)',
|
||||
'tutorial.main.table.param': '参数',
|
||||
'tutorial.main.table.type': '类型',
|
||||
'tutorial.main.table.default': '默认值',
|
||||
'tutorial.main.table.desc': '说明',
|
||||
'tutorial.main.basic.title': '基础配置',
|
||||
'tutorial.main.basic.apikey': '访问本服务所需的 API Key',
|
||||
'tutorial.main.basic.port': '服务监听端口',
|
||||
'tutorial.main.basic.host': '服务监听地址',
|
||||
'tutorial.main.basic.provider': '默认模型提供商',
|
||||
'tutorial.main.prompt.title': '系统提示配置',
|
||||
'tutorial.main.prompt.file': '系统提示文件路径',
|
||||
'tutorial.main.prompt.mode': '系统提示模式:overwrite(覆盖) 或 append(追加)',
|
||||
'tutorial.main.retry.title': '重试配置',
|
||||
'tutorial.main.retry.max': '最大重试次数',
|
||||
'tutorial.main.retry.delay': '重试基础延迟(毫秒)',
|
||||
'tutorial.main.retry.error': '提供商最大错误次数,超过后标记为不健康',
|
||||
'tutorial.main.example.title': '配置示例',
|
||||
'tutorial.pool.title': '提供商池配置 (provider_pools.json)',
|
||||
'tutorial.pool.desc': '提供商池用于配置多个账号,实现负载均衡和故障转移。每个提供商类型可以配置多个账号节点。',
|
||||
'tutorial.pool.node.title': '节点配置参数',
|
||||
'tutorial.pool.node.uuid': '节点唯一标识,自动生成',
|
||||
'tutorial.pool.node.name': '节点自定义名称',
|
||||
'tutorial.pool.node.oauth': 'OAuth 凭据文件路径',
|
||||
'tutorial.pool.node.health': '是否启用健康检查',
|
||||
'tutorial.pool.node.model': '健康检查使用的模型',
|
||||
'tutorial.pool.node.unsupported': '该节点不支持的模型列表',
|
||||
'tutorial.pool.node.disabled': '是否禁用该节点',
|
||||
'tutorial.pool.example.title': '配置示例',
|
||||
'tutorial.fallback.title': 'Fallback 降级配置',
|
||||
'tutorial.fallback.desc': '当某一提供商类型的所有账号都不可用时,可以自动切换到配置的备用提供商。',
|
||||
'tutorial.fallback.chain.title': '跨类型 Fallback 链',
|
||||
'tutorial.fallback.chain.desc': '在 config.json 中配置 providerFallbackChain,指定每个提供商类型的备用类型:',
|
||||
'tutorial.fallback.model.title': '跨协议模型映射',
|
||||
'tutorial.fallback.model.desc': '当主提供商不可用时,可以将特定模型映射到其他协议的提供商:',
|
||||
'tutorial.proxy.title': '代理配置',
|
||||
'tutorial.proxy.desc': '支持为特定提供商配置代理,用于网络受限环境。',
|
||||
'tutorial.proxy.config.title': '代理配置参数',
|
||||
'tutorial.proxy.url': '代理地址,支持 HTTP、HTTPS、SOCKS5',
|
||||
'tutorial.proxy.providers': '启用代理的提供商列表',
|
||||
'tutorial.proxy.example.title': '配置示例',
|
||||
'tutorial.proxy.note': '支持的代理类型:HTTP (http://)、HTTPS (https://)、SOCKS5 (socks5://)',
|
||||
'tutorial.oauth.title': 'OAuth 授权配置',
|
||||
'tutorial.oauth.desc': '各提供商的 OAuth 凭据文件默认存储位置:',
|
||||
'tutorial.oauth.note': '推荐通过 Web UI 控制台的"提供商池管理"页面点击"生成授权"按钮进行可视化授权,系统会自动保存凭据文件。',
|
||||
'tutorial.log.title': '日志配置',
|
||||
'tutorial.log.prompt.title': '提示日志配置',
|
||||
'tutorial.log.mode': '日志模式:none(关闭)、console(控制台)、file(文件)',
|
||||
'tutorial.log.basename': '日志文件基础名称',
|
||||
'tutorial.log.example.title': '配置示例',
|
||||
|
||||
// Common
|
||||
'common.confirm': '确定',
|
||||
'common.cancel': '取消',
|
||||
|
|
@ -588,12 +723,15 @@ const translations = {
|
|||
// Navigation
|
||||
'nav.main': 'Main Navigation',
|
||||
'nav.dashboard': 'Dashboard',
|
||||
'nav.guide': 'User Guide',
|
||||
'nav.tutorial': 'Configuration Tutorial',
|
||||
'nav.config': 'Configuration',
|
||||
'nav.providers': 'Provider Pools',
|
||||
'nav.upload': 'Credential Files',
|
||||
'nav.usage': 'Usage Query',
|
||||
'nav.logs': 'Real-time Logs',
|
||||
'nav.plugins': 'Plugin Management',
|
||||
'nav.models': 'Available Models',
|
||||
|
||||
// Dashboard
|
||||
'dashboard.title': 'System Overview',
|
||||
|
|
@ -1085,6 +1223,138 @@ const translations = {
|
|||
'plugins.load.failed': 'Failed to load plugins list',
|
||||
'plugins.restart.required': 'Changes saved',
|
||||
|
||||
// Models
|
||||
'models.title': 'Available Models',
|
||||
'models.note': 'Click model name to copy to clipboard',
|
||||
'models.empty': 'No models available',
|
||||
'models.loadError': 'Failed to load models',
|
||||
'models.copied': 'Copied',
|
||||
'models.clickToCopy': 'Click to copy',
|
||||
|
||||
// Guide
|
||||
'guide.title': 'User Guide',
|
||||
'guide.intro.title': 'Introduction',
|
||||
'guide.intro.desc': 'AIClient2API is an API proxy service that breaks client restrictions, converting free large models like Gemini, Antigravity, Qwen Code, and Kiro into standard OpenAI-compatible interfaces that any application can call.',
|
||||
'guide.intro.feature1.title': 'Unified Access',
|
||||
'guide.intro.feature1.desc': 'Access multiple large models with a single configuration through standard OpenAI-compatible protocol',
|
||||
'guide.intro.feature2.title': 'Break Limits',
|
||||
'guide.intro.feature2.desc': 'Effectively bypass free API rate and quota limits using OAuth authorization',
|
||||
'guide.intro.feature3.title': 'Protocol Conversion',
|
||||
'guide.intro.feature3.desc': 'Support intelligent conversion between OpenAI, Claude, and Gemini protocols',
|
||||
'guide.intro.feature4.title': 'Account Pool',
|
||||
'guide.intro.feature4.desc': 'Support multi-account polling, automatic failover, and configuration degradation',
|
||||
'guide.providers.title': 'Supported Model Providers',
|
||||
'guide.providers.badge.oauth': 'OAuth',
|
||||
'guide.providers.badge.experimental': 'Experimental',
|
||||
'guide.providers.badge.free': 'Free',
|
||||
'guide.providers.badge.official': 'Official API',
|
||||
'guide.providers.gemini.desc': 'Access Gemini models via Google OAuth, supporting gemini-2.0-flash-exp and more',
|
||||
'guide.providers.antigravity.desc': 'Access Gemini 3 Pro, Claude Sonnet 4.5 via Google internal interface',
|
||||
'guide.providers.kiro.desc': 'Free access to Claude Opus 4.5, Claude Sonnet 4.5 via Kiro client',
|
||||
'guide.providers.qwen.desc': 'Access Qwen3 Coder Plus via Alibaba Cloud OAuth',
|
||||
'guide.providers.claude.desc': 'Access Claude models via official API or third-party proxy',
|
||||
'guide.providers.openai.desc': 'Access GPT models via official API or third-party proxy',
|
||||
'guide.providers.iflow.desc': 'Access Qwen, Kimi, DeepSeek, GLM via iFlow OAuth',
|
||||
'guide.providers.orchids.desc': 'Free access to Claude Sonnet 4.5 via Orchids platform',
|
||||
'guide.client.title': 'Client Configuration Guide',
|
||||
'guide.client.desc': 'Here are configuration methods for common AI clients. Set the API endpoint to this service address:',
|
||||
'guide.client.cherry.step1': 'Open Settings → Model Providers',
|
||||
'guide.client.cherry.step2': 'Add custom provider',
|
||||
'guide.client.cherry.step3': 'Set API URL to: http://localhost:3000/{provider}',
|
||||
'guide.client.cherry.step4': 'Enter API Key (REQUIRED_API_KEY from config)',
|
||||
'guide.client.cline.step1': 'Open VS Code Settings',
|
||||
'guide.client.cline.step2': 'Search for Cline or Continue configuration',
|
||||
'guide.client.cline.step3': 'Set API Base URL to: http://localhost:3000/{provider}/v1',
|
||||
'guide.client.cline.step4': 'Enter API Key and model name',
|
||||
'guide.client.note': 'Tip: Replace {provider} with the actual provider path, such as gemini-cli-oauth, claude-kiro-oauth, etc. See the routing examples on the dashboard for full paths.',
|
||||
'guide.ollama.title': 'Ollama Protocol Usage',
|
||||
'guide.ollama.desc': 'This project supports Ollama protocol, allowing unified access to all supported models.',
|
||||
'guide.ollama.listModels': 'List all available models',
|
||||
'guide.ollama.chat': 'Chat interface',
|
||||
'guide.faq.title': 'FAQ',
|
||||
'guide.faq.q1': 'Q: What to do if request returns 404 error?',
|
||||
'guide.faq.a1': 'A: Check if the API path is correct. Some clients automatically append paths to Base URL, causing duplication. Check the actual request URL in the console and remove redundant path parts.',
|
||||
'guide.faq.q2': 'Q: What to do if request returns 429 error?',
|
||||
'guide.faq.a2': 'A: 429 means request rate is too high. Configure multiple accounts in the provider pool with polling; or configure Fallback chain for cross-type degradation.',
|
||||
'guide.faq.q3': 'Q: What to do if OAuth authorization fails?',
|
||||
'guide.faq.a3': 'A: Ensure OAuth callback ports are accessible (Gemini: 8085, Antigravity: 8086, Kiro: 19876-19880). Docker users need to map these ports correctly.',
|
||||
'guide.faq.q4': 'Q: How to view available models?',
|
||||
'guide.faq.a4': 'A: Click "Available Models" in the sidebar to view all models supported by configured providers. Click model name to copy.',
|
||||
'guide.faq.q5': 'Q: What to do if streaming response is interrupted?',
|
||||
'guide.faq.a5': 'A: Check network stability, increase client request timeout. If using proxy, ensure it supports long connections.',
|
||||
|
||||
// Guide - Flow
|
||||
'guide.flow.title': 'Operation Flowchart',
|
||||
'guide.flow.step1.title': 'Configuration',
|
||||
'guide.flow.step1.desc': 'Set basic parameters in "Configuration" page',
|
||||
'guide.flow.step2.title': 'Generate Auth',
|
||||
'guide.flow.step2.desc': 'Generate OAuth authorization in "Provider Pools" page',
|
||||
'guide.flow.step3.title': 'Manage Credentials',
|
||||
'guide.flow.step3.desc': 'View and manage credentials in "Credential Files" page',
|
||||
'guide.flow.step4.title': 'Start Using',
|
||||
'guide.flow.step4.desc': 'View routing examples in "Dashboard" and start calling API',
|
||||
|
||||
// Tutorial
|
||||
'tutorial.title': 'Configuration Tutorial',
|
||||
'tutorial.config.title': 'Configuration Files',
|
||||
'tutorial.config.desc': 'All configuration files are stored in the configs/ directory. Main configuration files include:',
|
||||
'tutorial.config.badge.required': 'Required',
|
||||
'tutorial.config.badge.optional': 'Optional',
|
||||
'tutorial.config.file.config': 'Main config file with API Key, port, model provider settings',
|
||||
'tutorial.config.file.pools': 'Provider pool config for multi-account polling and failover',
|
||||
'tutorial.config.file.plugins': 'Plugin config for enabling/disabling system plugins',
|
||||
'tutorial.config.file.pwd': 'Admin password file, default password is admin123',
|
||||
'tutorial.main.title': 'Main Config Details (config.json)',
|
||||
'tutorial.main.table.param': 'Parameter',
|
||||
'tutorial.main.table.type': 'Type',
|
||||
'tutorial.main.table.default': 'Default',
|
||||
'tutorial.main.table.desc': 'Description',
|
||||
'tutorial.main.basic.title': 'Basic Configuration',
|
||||
'tutorial.main.basic.apikey': 'API Key required to access this service',
|
||||
'tutorial.main.basic.port': 'Service listening port',
|
||||
'tutorial.main.basic.host': 'Service listening address',
|
||||
'tutorial.main.basic.provider': 'Default model provider',
|
||||
'tutorial.main.prompt.title': 'System Prompt Configuration',
|
||||
'tutorial.main.prompt.file': 'System prompt file path',
|
||||
'tutorial.main.prompt.mode': 'System prompt mode: overwrite or append',
|
||||
'tutorial.main.retry.title': 'Retry Configuration',
|
||||
'tutorial.main.retry.max': 'Maximum retry count',
|
||||
'tutorial.main.retry.delay': 'Base retry delay (milliseconds)',
|
||||
'tutorial.main.retry.error': 'Max provider error count before marking unhealthy',
|
||||
'tutorial.main.example.title': 'Configuration Example',
|
||||
'tutorial.pool.title': 'Provider Pool Config (provider_pools.json)',
|
||||
'tutorial.pool.desc': 'Provider pool configures multiple accounts for load balancing and failover. Each provider type can have multiple account nodes.',
|
||||
'tutorial.pool.node.title': 'Node Configuration Parameters',
|
||||
'tutorial.pool.node.uuid': 'Unique node identifier, auto-generated',
|
||||
'tutorial.pool.node.name': 'Custom node name',
|
||||
'tutorial.pool.node.oauth': 'OAuth credentials file path',
|
||||
'tutorial.pool.node.health': 'Enable health check',
|
||||
'tutorial.pool.node.model': 'Model used for health check',
|
||||
'tutorial.pool.node.unsupported': 'List of unsupported models for this node',
|
||||
'tutorial.pool.node.disabled': 'Whether to disable this node',
|
||||
'tutorial.pool.example.title': 'Configuration Example',
|
||||
'tutorial.fallback.title': 'Fallback Configuration',
|
||||
'tutorial.fallback.desc': 'When all accounts of a provider type are unavailable, automatically switch to configured backup providers.',
|
||||
'tutorial.fallback.chain.title': 'Cross-Type Fallback Chain',
|
||||
'tutorial.fallback.chain.desc': 'Configure providerFallbackChain in config.json to specify backup types for each provider:',
|
||||
'tutorial.fallback.model.title': 'Cross-Protocol Model Mapping',
|
||||
'tutorial.fallback.model.desc': 'When primary provider is unavailable, map specific models to other protocol providers:',
|
||||
'tutorial.proxy.title': 'Proxy Configuration',
|
||||
'tutorial.proxy.desc': 'Support proxy configuration for specific providers in restricted network environments.',
|
||||
'tutorial.proxy.config.title': 'Proxy Configuration Parameters',
|
||||
'tutorial.proxy.url': 'Proxy URL, supports HTTP, HTTPS, SOCKS5',
|
||||
'tutorial.proxy.providers': 'List of providers using proxy',
|
||||
'tutorial.proxy.example.title': 'Configuration Example',
|
||||
'tutorial.proxy.note': 'Supported proxy types: HTTP (http://), HTTPS (https://), SOCKS5 (socks5://)',
|
||||
'tutorial.oauth.title': 'OAuth Configuration',
|
||||
'tutorial.oauth.desc': 'Default storage locations for OAuth credentials of each provider:',
|
||||
'tutorial.oauth.note': 'Recommended: Use the "Generate Auth" button in Provider Pool Management page for visual authorization. Credentials will be saved automatically.',
|
||||
'tutorial.log.title': 'Log Configuration',
|
||||
'tutorial.log.prompt.title': 'Prompt Log Configuration',
|
||||
'tutorial.log.mode': 'Log mode: none, console, or file',
|
||||
'tutorial.log.basename': 'Log file base name',
|
||||
'tutorial.log.example.title': 'Configuration Example',
|
||||
|
||||
// Common
|
||||
'common.togglePassword': 'Show/Hide Password',
|
||||
'common.confirm': 'Confirm',
|
||||
|
|
|
|||
289
static/app/models-manager.js
Normal file
289
static/app/models-manager.js
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
/**
|
||||
* Models Manager - 管理可用模型列表的显示和复制功能
|
||||
* Models Manager - Manages the display and copy functionality of available models
|
||||
*/
|
||||
|
||||
import { t } from './i18n.js';
|
||||
|
||||
// 模型数据缓存
|
||||
let modelsCache = null;
|
||||
|
||||
/**
|
||||
* 获取所有提供商的可用模型
|
||||
* @returns {Promise<Object>} 模型数据
|
||||
*/
|
||||
async function fetchProviderModels() {
|
||||
if (modelsCache) {
|
||||
return modelsCache;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/provider-models', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${localStorage.getItem('authToken') || ''}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
modelsCache = await response.json();
|
||||
return modelsCache;
|
||||
} catch (error) {
|
||||
console.error('[Models Manager] Failed to fetch provider models:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制文本到剪贴板
|
||||
* @param {string} text - 要复制的文本
|
||||
* @returns {Promise<boolean>} 是否复制成功
|
||||
*/
|
||||
async function copyToClipboard(text) {
|
||||
try {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
await navigator.clipboard.writeText(text);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallback for older browsers
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-9999px';
|
||||
textArea.style.top = '-9999px';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
const successful = document.execCommand('copy');
|
||||
document.body.removeChild(textArea);
|
||||
return successful;
|
||||
} catch (error) {
|
||||
console.error('[Models Manager] Failed to copy to clipboard:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示复制成功的 Toast 提示
|
||||
* @param {string} modelName - 模型名称
|
||||
*/
|
||||
function showCopyToast(modelName) {
|
||||
const toastContainer = document.getElementById('toastContainer');
|
||||
if (!toastContainer) return;
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast toast-success';
|
||||
toast.innerHTML = `
|
||||
<i class="fas fa-check-circle"></i>
|
||||
<span>${t('models.copied') || '已复制'}: ${modelName}</span>
|
||||
`;
|
||||
|
||||
toastContainer.appendChild(toast);
|
||||
|
||||
// 自动移除
|
||||
setTimeout(() => {
|
||||
toast.classList.add('toast-fade-out');
|
||||
setTimeout(() => {
|
||||
toast.remove();
|
||||
}, 300);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染模型列表
|
||||
* @param {Object} models - 模型数据
|
||||
*/
|
||||
function renderModelsList(models) {
|
||||
const container = document.getElementById('modelsList');
|
||||
if (!container) return;
|
||||
|
||||
// 检查是否有模型数据
|
||||
const providerTypes = Object.keys(models);
|
||||
if (providerTypes.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="models-empty">
|
||||
<i class="fas fa-cube"></i>
|
||||
<p data-i18n="models.empty">${t('models.empty') || '暂无可用模型'}</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
// 渲染每个提供商的模型组
|
||||
let html = '';
|
||||
|
||||
for (const providerType of providerTypes) {
|
||||
const modelList = models[providerType];
|
||||
if (!modelList || modelList.length === 0) continue;
|
||||
|
||||
const providerDisplayName = getProviderDisplayName(providerType);
|
||||
const providerIcon = getProviderIcon(providerType);
|
||||
|
||||
html += `
|
||||
<div class="provider-models-group" data-provider="${providerType}">
|
||||
<div class="provider-models-header" onclick="window.toggleProviderModels('${providerType}')">
|
||||
<div class="provider-models-title">
|
||||
<i class="${providerIcon}"></i>
|
||||
<h3>${providerDisplayName}</h3>
|
||||
<span class="provider-models-count">${modelList.length}</span>
|
||||
</div>
|
||||
<div class="provider-models-toggle">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="provider-models-content" id="models-${providerType}">
|
||||
${modelList.map(model => `
|
||||
<div class="model-item" onclick="window.copyModelName('${escapeHtml(model)}', this)" title="${t('models.clickToCopy') || '点击复制'}">
|
||||
<div class="model-item-icon">
|
||||
<i class="fas fa-cube"></i>
|
||||
</div>
|
||||
<span class="model-item-name">${escapeHtml(model)}</span>
|
||||
<div class="model-item-copy">
|
||||
<i class="fas fa-copy"></i>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提供商显示名称
|
||||
* @param {string} providerType - 提供商类型
|
||||
* @returns {string} 显示名称
|
||||
*/
|
||||
function getProviderDisplayName(providerType) {
|
||||
const displayNames = {
|
||||
'gemini-cli-oauth': 'Gemini CLI (OAuth)',
|
||||
'gemini-antigravity': 'Gemini Antigravity',
|
||||
'claude-custom': 'Claude Custom',
|
||||
'claude-kiro-oauth': 'Claude Kiro (OAuth)',
|
||||
'claude-orchids-oauth': 'Claude Orchids (OAuth)',
|
||||
'openai-custom': 'OpenAI Custom',
|
||||
'openaiResponses-custom': 'OpenAI Responses Custom',
|
||||
'openai-qwen-oauth': 'Qwen (OAuth)',
|
||||
'openai-iflow': 'iFlow'
|
||||
};
|
||||
|
||||
return displayNames[providerType] || providerType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提供商图标
|
||||
* @param {string} providerType - 提供商类型
|
||||
* @returns {string} 图标类名
|
||||
*/
|
||||
function getProviderIcon(providerType) {
|
||||
if (providerType.includes('gemini')) {
|
||||
return 'fas fa-gem';
|
||||
} else if (providerType.includes('claude')) {
|
||||
return 'fas fa-robot';
|
||||
} else if (providerType.includes('openai') || providerType.includes('qwen') || providerType.includes('iflow')) {
|
||||
return 'fas fa-brain';
|
||||
}
|
||||
return 'fas fa-server';
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML 转义
|
||||
* @param {string} text - 原始文本
|
||||
* @returns {string} 转义后的文本
|
||||
*/
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换提供商模型列表的展开/折叠状态
|
||||
* @param {string} providerType - 提供商类型
|
||||
*/
|
||||
function toggleProviderModels(providerType) {
|
||||
const group = document.querySelector(`.provider-models-group[data-provider="${providerType}"]`);
|
||||
if (!group) return;
|
||||
|
||||
const header = group.querySelector('.provider-models-header');
|
||||
const content = group.querySelector('.provider-models-content');
|
||||
|
||||
if (content.classList.contains('collapsed')) {
|
||||
content.classList.remove('collapsed');
|
||||
header.classList.remove('collapsed');
|
||||
} else {
|
||||
content.classList.add('collapsed');
|
||||
header.classList.add('collapsed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制模型名称
|
||||
* @param {string} modelName - 模型名称
|
||||
* @param {HTMLElement} element - 点击的元素
|
||||
*/
|
||||
async function copyModelName(modelName, element) {
|
||||
const success = await copyToClipboard(modelName);
|
||||
|
||||
if (success) {
|
||||
// 添加复制成功的视觉反馈
|
||||
element.classList.add('copied');
|
||||
setTimeout(() => {
|
||||
element.classList.remove('copied');
|
||||
}, 1000);
|
||||
|
||||
// 显示 Toast 提示
|
||||
showCopyToast(modelName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化模型管理器
|
||||
*/
|
||||
async function initModelsManager() {
|
||||
const container = document.getElementById('modelsList');
|
||||
if (!container) return;
|
||||
|
||||
try {
|
||||
const models = await fetchProviderModels();
|
||||
renderModelsList(models);
|
||||
} catch (error) {
|
||||
container.innerHTML = `
|
||||
<div class="models-empty">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<p>${t('models.loadError') || '加载模型列表失败'}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新模型列表
|
||||
*/
|
||||
async function refreshModels() {
|
||||
modelsCache = null;
|
||||
await initModelsManager();
|
||||
}
|
||||
|
||||
// 导出到全局作用域供 HTML 调用
|
||||
window.toggleProviderModels = toggleProviderModels;
|
||||
window.copyModelName = copyModelName;
|
||||
window.refreshModels = refreshModels;
|
||||
|
||||
// 监听组件加载完成事件
|
||||
window.addEventListener('componentsLoaded', () => {
|
||||
initModelsManager();
|
||||
});
|
||||
|
||||
// 导出函数
|
||||
export {
|
||||
initModelsManager,
|
||||
refreshModels,
|
||||
fetchProviderModels
|
||||
};
|
||||
913
static/components/section-guide.css
Normal file
913
static/components/section-guide.css
Normal file
|
|
@ -0,0 +1,913 @@
|
|||
/* Guide Section Styles */
|
||||
|
||||
/* 操作流程图样式 */
|
||||
.process-flow {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 1.25rem;
|
||||
width: 200px;
|
||||
min-height: 200px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.flow-step:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.step-number {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
box-shadow: 0 4px 12px rgba(var(--primary-rgb, 59, 130, 246), 0.3);
|
||||
}
|
||||
|
||||
.step-content {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.step-content h4 {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.step-content p {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
margin: 0 0 0.75rem 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.step-content ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.step-content ul li {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
padding: 0.25rem 0;
|
||||
padding-left: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step-content ul li::before {
|
||||
content: "•";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--primary-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.step-content ul li code {
|
||||
font-size: 0.7rem;
|
||||
background: var(--bg-tertiary);
|
||||
padding: 0.1rem 0.3rem;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* 并行分支样式 */
|
||||
.flow-step-branch {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.branch-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.branch-option {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 0.75rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.branch-label {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.branch-divider {
|
||||
text-align: center;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.branch-divider::before,
|
||||
.branch-divider::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 40%;
|
||||
height: 1px;
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
.branch-divider::before {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.branch-divider::after {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.branch-option ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.branch-option ul li {
|
||||
font-size: 0.7rem;
|
||||
padding: 0.15rem 0;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
color: var(--primary-color);
|
||||
font-weight: bold;
|
||||
padding: 0 0.25rem;
|
||||
margin-top: 80px;
|
||||
}
|
||||
|
||||
/* 响应式流程图 */
|
||||
@media (max-width: 1200px) {
|
||||
.process-flow {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
width: 180px;
|
||||
min-height: 180px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
font-size: 1.25rem;
|
||||
margin-top: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.process-flow {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
min-height: auto;
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
margin-bottom: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
transform: rotate(90deg);
|
||||
margin-top: 0;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Guide Panel */
|
||||
.guide-panel {
|
||||
background: var(--bg-primary);
|
||||
padding: 1.5rem;
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
margin-bottom: 1.5rem;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.guide-panel h3 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 1rem 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.guide-panel h3 i {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.guide-content {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.guide-content > p {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* Feature Grid */
|
||||
.feature-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background: var(--bg-secondary);
|
||||
padding: 1.25rem;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border-color);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
border-color: var(--primary-30);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: var(--radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary-color);
|
||||
background: var(--primary-10);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.feature-card h4 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.feature-card p {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Provider List */
|
||||
.provider-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.provider-item {
|
||||
background: var(--bg-secondary);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border-color);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.provider-item:hover {
|
||||
border-color: var(--primary-30);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.provider-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.provider-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.provider-icon.gemini { color: #4285f4; }
|
||||
.provider-icon.antigravity { color: #ea4335; }
|
||||
.provider-icon.kiro { color: #9b59b6; }
|
||||
.provider-icon.qwen { color: #ff6a00; }
|
||||
.provider-icon.claude { color: #d97706; }
|
||||
.provider-icon.openai { color: #10a37f; }
|
||||
.provider-icon.iflow { color: #3b82f6; }
|
||||
.provider-icon.orchids { color: #22c55e; }
|
||||
|
||||
.provider-name {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.provider-badge {
|
||||
font-size: 0.7rem;
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 9999px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.provider-badge.oauth {
|
||||
background: var(--primary-10);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.provider-badge.experimental {
|
||||
background: var(--warning-bg);
|
||||
color: var(--warning-text);
|
||||
}
|
||||
|
||||
.provider-badge.free {
|
||||
background: var(--success-bg);
|
||||
color: var(--success-text);
|
||||
}
|
||||
|
||||
.provider-badge.official {
|
||||
background: var(--info-bg);
|
||||
color: var(--info-text);
|
||||
}
|
||||
|
||||
.provider-desc {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Client Config List */
|
||||
.client-config-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.client-config-item {
|
||||
background: var(--bg-secondary);
|
||||
padding: 1.25rem;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.client-config-item h4 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 1rem 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.client-config-item h4 i {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.config-steps ol {
|
||||
margin: 0;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.config-steps ol li {
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.config-steps ol li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.config-steps code {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.85rem;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.config-steps pre {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
overflow-x: auto;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.config-steps pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-primary);
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
/* Guide Note */
|
||||
.guide-note {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.75rem;
|
||||
background: var(--info-bg);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
margin-top: 1rem;
|
||||
border: 1px solid var(--info-border);
|
||||
}
|
||||
|
||||
.guide-note i {
|
||||
color: var(--info-text);
|
||||
font-size: 1rem;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
||||
.guide-note span {
|
||||
color: var(--info-text);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* API Example */
|
||||
.api-example {
|
||||
background: var(--bg-secondary);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.api-example h4 {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 0.75rem 0;
|
||||
}
|
||||
|
||||
.api-example pre {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
overflow-x: auto;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.api-example pre code {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-primary);
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
/* Model Prefix List */
|
||||
.model-prefix-list {
|
||||
background: var(--bg-secondary);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.model-prefix-list h4 {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 0.75rem 0;
|
||||
}
|
||||
|
||||
.model-prefix-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.model-prefix-list ul li {
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.model-prefix-list ul li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.model-prefix-list code {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.85rem;
|
||||
color: var(--primary-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* FAQ List */
|
||||
.faq-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.faq-item {
|
||||
background: var(--bg-secondary);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.faq-question {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.faq-answer {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.feature-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.provider-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.guide-panel {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark Theme */
|
||||
[data-theme="dark"] .guide-panel {
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .feature-card,
|
||||
[data-theme="dark"] .provider-item,
|
||||
[data-theme="dark"] .client-config-item,
|
||||
[data-theme="dark"] .api-example,
|
||||
[data-theme="dark"] .model-prefix-list,
|
||||
[data-theme="dark"] .faq-item {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .config-steps pre,
|
||||
[data-theme="dark"] .api-example pre {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
可用模型列表样式 (从 section-models.css 合并)
|
||||
======================================== */
|
||||
|
||||
/* 模型描述区域 */
|
||||
.models-description {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* 模型容器 */
|
||||
.models-container {
|
||||
background: var(--bg-secondary);
|
||||
padding: 1.25rem;
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.models-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.models-loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
padding: 2.5rem;
|
||||
color: var(--text-secondary);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.models-loading i {
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* 提供商模型组 */
|
||||
.provider-models-group {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
transition: var(--transition);
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
.provider-models-group:hover {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
/* 提供商标题 */
|
||||
.provider-models-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.875rem 1.25rem;
|
||||
background: linear-gradient(135deg, var(--bg-tertiary) 0%, var(--bg-secondary) 100%);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.provider-models-header:hover {
|
||||
background: linear-gradient(135deg, var(--primary-10) 0%, var(--bg-tertiary) 100%);
|
||||
}
|
||||
|
||||
.provider-models-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.provider-models-title i {
|
||||
font-size: 1.125rem;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.provider-models-title h3 {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.provider-models-count {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
padding: 0 0.4rem;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.provider-models-toggle {
|
||||
color: var(--text-secondary);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.provider-models-header.collapsed .provider-models-toggle {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
/* 模型列表内容 */
|
||||
.provider-models-content {
|
||||
padding: 0.875rem 1.25rem;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap: 0.625rem;
|
||||
}
|
||||
|
||||
.provider-models-content.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 单个模型项 */
|
||||
.model-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
padding: 0.625rem 0.875rem;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.model-item:hover {
|
||||
border-color: var(--primary-color);
|
||||
background: var(--primary-10);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.model-item:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.model-item-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
background: var(--primary-10);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--primary-color);
|
||||
flex-shrink: 0;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.model-item-name {
|
||||
flex: 1;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.model-item-copy {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
color: var(--text-tertiary);
|
||||
opacity: 0;
|
||||
transition: var(--transition);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.model-item:hover .model-item-copy {
|
||||
opacity: 1;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* 复制成功动画 */
|
||||
.model-item.copied {
|
||||
border-color: var(--success-color);
|
||||
background: var(--success-10);
|
||||
}
|
||||
|
||||
.model-item.copied .model-item-copy {
|
||||
opacity: 1;
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.model-item.copied::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--success-color);
|
||||
opacity: 0.1;
|
||||
animation: copyFlash 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes copyFlash {
|
||||
0% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.models-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2.5rem;
|
||||
color: var(--text-secondary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.models-empty i {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.models-empty p {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 高亮说明样式 */
|
||||
.models-description .highlight-note {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.875rem 1rem;
|
||||
background: linear-gradient(135deg, var(--info-bg) 0%, var(--info-bg-light, var(--info-bg)) 100%);
|
||||
border: 1px solid var(--info-border);
|
||||
border-radius: 0.5rem;
|
||||
color: var(--info-text);
|
||||
font-weight: 500;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.models-description .highlight-note i {
|
||||
color: var(--info-color, var(--info-text));
|
||||
font-size: 1.125rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.models-description .highlight-note span {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 模型列表响应式 */
|
||||
@media (max-width: 768px) {
|
||||
.provider-models-content {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.provider-models-header {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.provider-models-title h3 {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.model-item {
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 模型列表暗黑主题 */
|
||||
[data-theme="dark"] .provider-models-group {
|
||||
background: var(--bg-primary);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .provider-models-header {
|
||||
background: linear-gradient(135deg, var(--bg-tertiary) 0%, var(--bg-secondary) 100%);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .provider-models-header:hover {
|
||||
background: linear-gradient(135deg, var(--primary-10) 0%, var(--bg-tertiary) 100%);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .model-item {
|
||||
background: var(--bg-secondary);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .model-item:hover {
|
||||
background: var(--primary-10);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .models-description .highlight-note {
|
||||
background: linear-gradient(135deg, var(--info-bg) 0%, var(--info-bg-light, var(--info-bg)) 100%);
|
||||
border-color: var(--info-border);
|
||||
color: var(--info-text);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .models-container {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
242
static/components/section-guide.html
Normal file
242
static/components/section-guide.html
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
<link rel="stylesheet" href="components/section-guide.css">
|
||||
<!-- Guide Section -->
|
||||
<section id="guide" class="section" aria-labelledby="guide-title">
|
||||
<h2 id="guide-title" data-i18n="guide.title">使用指南</h2>
|
||||
|
||||
<!-- 项目简介 -->
|
||||
<div class="guide-panel">
|
||||
<h3><i class="fas fa-info-circle"></i> <span data-i18n="guide.intro.title">项目简介</span></h3>
|
||||
<div class="guide-content">
|
||||
<p data-i18n="guide.intro.desc">AIClient2API 是一个突破客户端限制的 API 代理服务,将 Gemini、Antigravity、Qwen Code、Kiro 等原本仅限客户端内使用的免费大模型,转换为可供任何应用调用的标准 OpenAI 兼容接口。</p>
|
||||
<div class="feature-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon"><i class="fas fa-plug"></i></div>
|
||||
<h4 data-i18n="guide.intro.feature1.title">统一接入</h4>
|
||||
<p data-i18n="guide.intro.feature1.desc">通过标准 OpenAI 兼容协议,一次配置即可接入多种大模型</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon"><i class="fas fa-rocket"></i></div>
|
||||
<h4 data-i18n="guide.intro.feature2.title">突破限制</h4>
|
||||
<p data-i18n="guide.intro.feature2.desc">利用 OAuth 授权机制,有效突破免费 API 速率和配额限制</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon"><i class="fas fa-exchange-alt"></i></div>
|
||||
<h4 data-i18n="guide.intro.feature3.title">协议转换</h4>
|
||||
<p data-i18n="guide.intro.feature3.desc">支持 OpenAI、Claude、Gemini 三大协议间的智能转换</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon"><i class="fas fa-server"></i></div>
|
||||
<h4 data-i18n="guide.intro.feature4.title">账号池管理</h4>
|
||||
<p data-i18n="guide.intro.feature4.desc">支持多账号轮询、自动故障转移和配置降级</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作流程图 -->
|
||||
<div class="guide-panel">
|
||||
<h3><i class="fas fa-project-diagram"></i> <span data-i18n="guide.flow.title">操作流程图</span></h3>
|
||||
<div class="guide-content">
|
||||
<div class="process-flow">
|
||||
<div class="flow-step">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-content">
|
||||
<h4 data-i18n="guide.flow.step1.title">配置管理</h4>
|
||||
<p data-i18n="guide.flow.step1.desc">在「配置管理」页面设置基本参数</p>
|
||||
<ul>
|
||||
<li>设置 API Key</li>
|
||||
<li>选择启动时初始化的模型提供商</li>
|
||||
<li>配置高级选项</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flow-arrow">→</div>
|
||||
|
||||
<div class="flow-step flow-step-branch">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-content">
|
||||
<h4 data-i18n="guide.flow.step2.title">生成授权</h4>
|
||||
<p data-i18n="guide.flow.step2.desc">在「提供商池管理」页面生成 OAuth 授权</p>
|
||||
<div class="branch-options">
|
||||
<div class="branch-option">
|
||||
<div class="branch-label">方式一:OAuth 授权</div>
|
||||
<ul>
|
||||
<li>点击「生成授权」按钮</li>
|
||||
<li>在弹窗中完成 OAuth 登录</li>
|
||||
<li>凭据自动保存</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="branch-divider">或</div>
|
||||
<div class="branch-option">
|
||||
<div class="branch-label">方式二:手动上传</div>
|
||||
<ul>
|
||||
<li>新增提供商节点</li>
|
||||
<li>上传已有的授权文件</li>
|
||||
<li>手动关联凭据路径</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flow-arrow">→</div>
|
||||
|
||||
<div class="flow-step">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-content">
|
||||
<h4 data-i18n="guide.flow.step3.title">管理凭据</h4>
|
||||
<p data-i18n="guide.flow.step3.desc">在「凭据文件管理」页面查看和管理凭据</p>
|
||||
<ul>
|
||||
<li>查看已生成的凭据文件</li>
|
||||
<li>自动关联到提供商池</li>
|
||||
<li>删除无效凭据</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flow-arrow">→</div>
|
||||
|
||||
<div class="flow-step">
|
||||
<div class="step-number">4</div>
|
||||
<div class="step-content">
|
||||
<h4 data-i18n="guide.flow.step4.title">开始使用</h4>
|
||||
<p data-i18n="guide.flow.step4.desc">在「仪表盘」查看路由示例并开始调用 API</p>
|
||||
<ul>
|
||||
<li>查看路由调用示例</li>
|
||||
<li>复制 API 端点地址</li>
|
||||
<li>在客户端中配置使用</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 客户端配置指南 -->
|
||||
<div class="guide-panel">
|
||||
<h3><i class="fas fa-desktop"></i> <span data-i18n="guide.client.title">客户端配置指南</span></h3>
|
||||
<div class="guide-content">
|
||||
<p data-i18n="guide.client.desc">以下是常见 AI 客户端的配置方法,将 API 端点设置为本服务地址即可使用:</p>
|
||||
|
||||
<div class="client-config-list">
|
||||
<div class="client-config-item">
|
||||
<h4><i class="fas fa-cherry"></i> Cherry Studio</h4>
|
||||
<div class="config-steps">
|
||||
<ol>
|
||||
<li data-i18n="guide.client.cherry.step1">打开设置 → 模型服务商</li>
|
||||
<li data-i18n="guide.client.cherry.step2">添加自定义服务商</li>
|
||||
<li data-i18n="guide.client.cherry.step3">设置 API 地址为: <code>http://localhost:3000/{provider}/v1</code></li>
|
||||
<li data-i18n="guide.client.cherry.step4">填入 API Key(配置文件中的 REQUIRED_API_KEY)</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="client-config-item">
|
||||
<h4><i class="fas fa-terminal"></i> Cline / Continue</h4>
|
||||
<div class="config-steps">
|
||||
<ol>
|
||||
<li data-i18n="guide.client.cline.step1">打开 VS Code 设置</li>
|
||||
<li data-i18n="guide.client.cline.step2">搜索 Cline 或 Continue 配置</li>
|
||||
<li data-i18n="guide.client.cline.step3">设置 API Base URL 为: <code>http://localhost:3000/{provider}/v1</code></li>
|
||||
<li data-i18n="guide.client.cline.step4">填入 API Key 和模型名称</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="client-config-item">
|
||||
<h4><i class="fas fa-code"></i> 通用 cURL 调用</h4>
|
||||
<div class="config-steps">
|
||||
<pre><code>curl http://localhost:3000/{provider}/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \
|
||||
-d '{
|
||||
"model": "模型名称",
|
||||
"messages": [{"role": "user", "content": "Hello!"}],
|
||||
"max_tokens": 1000
|
||||
}'</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="guide-note">
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
<span data-i18n="guide.client.note">提示:将 {provider} 替换为实际的提供商路径,如 gemini-cli-oauth、claude-kiro-oauth 等。可在仪表盘的路由示例中查看完整路径。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ollama 协议使用 -->
|
||||
<div class="guide-panel">
|
||||
<h3><i class="fas fa-llama"></i> <span data-i18n="guide.ollama.title">Ollama 协议使用</span></h3>
|
||||
<div class="guide-content">
|
||||
<p data-i18n="guide.ollama.desc">本项目支持 Ollama 协议,可以通过统一接口访问所有支持的模型。</p>
|
||||
|
||||
<div class="api-example">
|
||||
<h4 data-i18n="guide.ollama.listModels">列出所有可用模型</h4>
|
||||
<pre><code>curl http://localhost:3000/ollama/api/tags \
|
||||
-H "Authorization: Bearer YOUR_API_KEY"</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="api-example">
|
||||
<h4 data-i18n="guide.ollama.chat">聊天接口</h4>
|
||||
<pre><code>curl http://localhost:3000/ollama/api/chat \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \
|
||||
-d '{
|
||||
"model": "[Claude] claude-sonnet-4.5",
|
||||
"messages": [{"role": "user", "content": "你好"}]
|
||||
}'</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 可用模型列表 -->
|
||||
<div class="guide-panel">
|
||||
<h3><i class="fas fa-cube"></i> <span data-i18n="models.title">可用模型列表</span></h3>
|
||||
<div class="guide-content">
|
||||
<div class="models-description">
|
||||
<div class="highlight-note">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<span data-i18n="models.note">点击模型名称可直接复制到剪贴板</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Models Container -->
|
||||
<div class="models-container">
|
||||
<div id="modelsList" class="models-list">
|
||||
<!-- Models will be loaded here -->
|
||||
<div class="models-loading">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
<span data-i18n="common.loading">加载中...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 常见问题 -->
|
||||
<div class="guide-panel">
|
||||
<h3><i class="fas fa-question-circle"></i> <span data-i18n="guide.faq.title">常见问题</span></h3>
|
||||
<div class="guide-content">
|
||||
<div class="faq-list">
|
||||
<div class="faq-item">
|
||||
<div class="faq-question" data-i18n="guide.faq.q1">Q: 请求返回 404 错误怎么办?</div>
|
||||
<div class="faq-answer" data-i18n="guide.faq.a1">A: 检查接口路径是否正确。某些客户端会自动在 Base URL 后追加路径,导致路径重复。请查看控制台中的实际请求 URL,移除多余的路径部分。</div>
|
||||
</div>
|
||||
<div class="faq-item">
|
||||
<div class="faq-question" data-i18n="guide.faq.q2">Q: 请求返回 429 错误怎么办?</div>
|
||||
<div class="faq-answer" data-i18n="guide.faq.a2">A: 429 表示请求频率过高。建议配置多个账号到提供商池,启用轮询机制;或配置 Fallback 链实现跨类型降级。</div>
|
||||
</div>
|
||||
<div class="faq-item">
|
||||
<div class="faq-question" data-i18n="guide.faq.q3">Q: OAuth 授权失败怎么办?</div>
|
||||
<div class="faq-answer" data-i18n="guide.faq.a3">A: 确保 OAuth 回调端口可访问(Gemini: 8085, Antigravity: 8086, Kiro: 19876-19880)。Docker 用户需确保已正确映射这些端口。</div>
|
||||
</div>
|
||||
<div class="faq-item">
|
||||
<div class="faq-question" data-i18n="guide.faq.q5">Q: 流式响应中断怎么办?</div>
|
||||
<div class="faq-answer" data-i18n="guide.faq.a5">A: 检查网络稳定性,增加客户端请求超时时间。如使用代理,确保代理支持长连接。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
476
static/components/section-tutorial.css
Normal file
476
static/components/section-tutorial.css
Normal file
|
|
@ -0,0 +1,476 @@
|
|||
/* Tutorial Section Styles */
|
||||
|
||||
/* 操作流程图样式 */
|
||||
.process-flow {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 1.25rem;
|
||||
width: 180px;
|
||||
min-height: 200px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.flow-step:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.step-number {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
box-shadow: 0 4px 12px rgba(var(--primary-rgb, 59, 130, 246), 0.3);
|
||||
}
|
||||
|
||||
.step-content {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.step-content h4 {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.step-content p {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
margin: 0 0 0.75rem 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.step-content ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.step-content ul li {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
padding: 0.25rem 0;
|
||||
padding-left: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step-content ul li::before {
|
||||
content: "•";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--primary-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.step-content ul li code {
|
||||
font-size: 0.7rem;
|
||||
background: var(--code-bg);
|
||||
padding: 0.1rem 0.3rem;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
color: var(--primary-color);
|
||||
font-weight: bold;
|
||||
padding: 0 0.25rem;
|
||||
margin-top: 80px;
|
||||
}
|
||||
|
||||
/* 响应式流程图 */
|
||||
@media (max-width: 1200px) {
|
||||
.process-flow {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
width: 160px;
|
||||
min-height: 180px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
font-size: 1.25rem;
|
||||
margin-top: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.process-flow {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
min-height: auto;
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
margin-bottom: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
transform: rotate(90deg);
|
||||
margin-top: 0;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tutorial Panel */
|
||||
.tutorial-panel {
|
||||
background: var(--bg-primary);
|
||||
padding: 1.5rem;
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
margin-bottom: 1.5rem;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.tutorial-panel h3 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 1rem 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.tutorial-panel h3 i {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.tutorial-content {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.tutorial-content > p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.tutorial-content code {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.85rem;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Config File List */
|
||||
.config-file-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.config-file-item {
|
||||
background: var(--bg-secondary);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border-color);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.config-file-item:hover {
|
||||
border-color: var(--primary-30);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.file-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.file-header i {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.file-badge {
|
||||
font-size: 0.65rem;
|
||||
padding: 0.1rem 0.4rem;
|
||||
border-radius: 9999px;
|
||||
font-weight: 500;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.file-badge.required {
|
||||
background: var(--danger-bg);
|
||||
color: var(--danger-text);
|
||||
}
|
||||
|
||||
.file-badge.optional {
|
||||
background: var(--info-bg);
|
||||
color: var(--info-text);
|
||||
}
|
||||
|
||||
.file-desc {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Config Section */
|
||||
.config-section {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.config-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.config-section h4 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 0.75rem 0;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* Config Table */
|
||||
.config-table {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.config-table table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.config-table th,
|
||||
.config-table td {
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.config-table th {
|
||||
background: var(--bg-secondary);
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.config-table td {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.config-table td code {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.8rem;
|
||||
color: var(--primary-color);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.config-table tr:hover td {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
/* Config Example */
|
||||
.config-example {
|
||||
background: var(--bg-secondary);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
margin-top: 1rem;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.config-example h4 {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 0.75rem 0;
|
||||
}
|
||||
|
||||
.config-example pre {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
overflow-x: auto;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.config-example pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-primary);
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.config-section pre {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
overflow-x: auto;
|
||||
margin: 0.75rem 0 0 0;
|
||||
}
|
||||
|
||||
.config-section pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-primary);
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
/* Tutorial Note */
|
||||
.tutorial-note {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.75rem;
|
||||
background: var(--info-bg);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
margin-top: 1rem;
|
||||
border: 1px solid var(--info-border);
|
||||
}
|
||||
|
||||
.tutorial-note i {
|
||||
color: var(--info-text);
|
||||
font-size: 1rem;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
||||
.tutorial-note span {
|
||||
color: var(--info-text);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* OAuth Path List */
|
||||
.oauth-path-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.oauth-path-item {
|
||||
background: var(--bg-secondary);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.path-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.path-header i {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.path-header i.fa-gem { color: #4285f4; }
|
||||
.path-header i.fa-rocket { color: #ea4335; }
|
||||
.path-header i.fa-robot { color: #9b59b6; }
|
||||
.path-header i.fa-code { color: #ff6a00; }
|
||||
|
||||
.path-provider {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.path-value {
|
||||
display: block;
|
||||
background: var(--bg-tertiary);
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-primary);
|
||||
font-family: monospace;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.config-file-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.oauth-path-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.tutorial-panel {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.config-table th,
|
||||
.config-table td {
|
||||
padding: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark Theme */
|
||||
[data-theme="dark"] .tutorial-panel {
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .config-file-item,
|
||||
[data-theme="dark"] .config-example,
|
||||
[data-theme="dark"] .oauth-path-item {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .config-table th {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .config-example pre,
|
||||
[data-theme="dark"] .config-section pre {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
443
static/components/section-tutorial.html
Normal file
443
static/components/section-tutorial.html
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
<link rel="stylesheet" href="components/section-tutorial.css">
|
||||
<!-- Tutorial Section -->
|
||||
<section id="tutorial" class="section" aria-labelledby="tutorial-title">
|
||||
<h2 id="tutorial-title" data-i18n="tutorial.title">配置教程</h2>
|
||||
|
||||
<!-- 配置文件说明 -->
|
||||
<div class="tutorial-panel">
|
||||
<h3><i class="fas fa-file-code"></i> <span data-i18n="tutorial.config.title">配置文件说明</span></h3>
|
||||
<div class="tutorial-content">
|
||||
<p data-i18n="tutorial.config.desc">所有配置文件都存放在 <code>configs/</code> 目录下。主要配置文件包括:</p>
|
||||
|
||||
<div class="config-file-list">
|
||||
<div class="config-file-item">
|
||||
<div class="file-header">
|
||||
<i class="fas fa-file-alt"></i>
|
||||
<span class="file-name">config.json</span>
|
||||
<span class="file-badge required" data-i18n="tutorial.config.badge.required">必需</span>
|
||||
</div>
|
||||
<p class="file-desc" data-i18n="tutorial.config.file.config">主配置文件,包含 API Key、端口、模型提供商等核心设置</p>
|
||||
</div>
|
||||
<div class="config-file-item">
|
||||
<div class="file-header">
|
||||
<i class="fas fa-file-alt"></i>
|
||||
<span class="file-name">provider_pools.json</span>
|
||||
<span class="file-badge required" data-i18n="tutorial.config.badge.required">必需</span>
|
||||
</div>
|
||||
<p class="file-desc" data-i18n="tutorial.config.file.pools">提供商池配置,用于多账号轮询和故障转移</p>
|
||||
</div>
|
||||
<div class="config-file-item">
|
||||
<div class="file-header">
|
||||
<i class="fas fa-file-alt"></i>
|
||||
<span class="file-name">plugins.json</span>
|
||||
<span class="file-badge optional" data-i18n="tutorial.config.badge.optional">可选</span>
|
||||
</div>
|
||||
<p class="file-desc" data-i18n="tutorial.config.file.plugins">插件配置,用于启用或禁用系统插件</p>
|
||||
</div>
|
||||
<div class="config-file-item">
|
||||
<div class="file-header">
|
||||
<i class="fas fa-lock"></i>
|
||||
<span class="file-name">pwd</span>
|
||||
<span class="file-badge optional" data-i18n="tutorial.config.badge.optional">可选</span>
|
||||
</div>
|
||||
<p class="file-desc" data-i18n="tutorial.config.file.pwd">后台登录密码文件,默认密码为 admin123</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主配置详解 -->
|
||||
<div class="tutorial-panel">
|
||||
<h3><i class="fas fa-cogs"></i> <span data-i18n="tutorial.main.title">主配置详解 (config.json)</span></h3>
|
||||
<div class="tutorial-content">
|
||||
<div class="config-section">
|
||||
<h4 data-i18n="tutorial.main.basic.title">基础配置</h4>
|
||||
<div class="config-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-i18n="tutorial.main.table.param">参数</th>
|
||||
<th data-i18n="tutorial.main.table.type">类型</th>
|
||||
<th data-i18n="tutorial.main.table.default">默认值</th>
|
||||
<th data-i18n="tutorial.main.table.desc">说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>REQUIRED_API_KEY</code></td>
|
||||
<td>string</td>
|
||||
<td>-</td>
|
||||
<td data-i18n="tutorial.main.basic.apikey">访问本服务所需的 API Key</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>SERVER_PORT</code></td>
|
||||
<td>number</td>
|
||||
<td>3000</td>
|
||||
<td data-i18n="tutorial.main.basic.port">服务监听端口</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>HOST</code></td>
|
||||
<td>string</td>
|
||||
<td>0.0.0.0</td>
|
||||
<td data-i18n="tutorial.main.basic.host">服务监听地址</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MODEL_PROVIDER</code></td>
|
||||
<td>string</td>
|
||||
<td>-</td>
|
||||
<td data-i18n="tutorial.main.basic.provider">默认模型提供商</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-section">
|
||||
<h4 data-i18n="tutorial.main.prompt.title">系统提示配置</h4>
|
||||
<div class="config-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-i18n="tutorial.main.table.param">参数</th>
|
||||
<th data-i18n="tutorial.main.table.type">类型</th>
|
||||
<th data-i18n="tutorial.main.table.default">默认值</th>
|
||||
<th data-i18n="tutorial.main.table.desc">说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>SYSTEM_PROMPT_FILE_PATH</code></td>
|
||||
<td>string</td>
|
||||
<td>-</td>
|
||||
<td data-i18n="tutorial.main.prompt.file">系统提示文件路径</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>SYSTEM_PROMPT_MODE</code></td>
|
||||
<td>string</td>
|
||||
<td>overwrite</td>
|
||||
<td data-i18n="tutorial.main.prompt.mode">系统提示模式:overwrite(覆盖) 或 append(追加)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-section">
|
||||
<h4 data-i18n="tutorial.main.retry.title">重试配置</h4>
|
||||
<div class="config-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-i18n="tutorial.main.table.param">参数</th>
|
||||
<th data-i18n="tutorial.main.table.type">类型</th>
|
||||
<th data-i18n="tutorial.main.table.default">默认值</th>
|
||||
<th data-i18n="tutorial.main.table.desc">说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>REQUEST_MAX_RETRIES</code></td>
|
||||
<td>number</td>
|
||||
<td>3</td>
|
||||
<td data-i18n="tutorial.main.retry.max">最大重试次数</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>REQUEST_BASE_DELAY</code></td>
|
||||
<td>number</td>
|
||||
<td>1000</td>
|
||||
<td data-i18n="tutorial.main.retry.delay">重试基础延迟(毫秒)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MAX_ERROR_COUNT</code></td>
|
||||
<td>number</td>
|
||||
<td>3</td>
|
||||
<td data-i18n="tutorial.main.retry.error">提供商最大错误次数,超过后标记为不健康</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-example">
|
||||
<h4 data-i18n="tutorial.main.example.title">配置示例</h4>
|
||||
<pre><code>{
|
||||
"REQUIRED_API_KEY": "your-api-key",
|
||||
"SERVER_PORT": 3000,
|
||||
"HOST": "0.0.0.0",
|
||||
"MODEL_PROVIDER": "gemini-cli-oauth",
|
||||
"SYSTEM_PROMPT_FILE_PATH": "configs/input_system_prompt.txt",
|
||||
"SYSTEM_PROMPT_MODE": "overwrite",
|
||||
"REQUEST_MAX_RETRIES": 3,
|
||||
"REQUEST_BASE_DELAY": 1000,
|
||||
"MAX_ERROR_COUNT": 3,
|
||||
"PROVIDER_POOLS_FILE_PATH": "configs/provider_pools.json"
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提供商池配置 -->
|
||||
<div class="tutorial-panel">
|
||||
<h3><i class="fas fa-layer-group"></i> <span data-i18n="tutorial.pool.title">提供商池配置 (provider_pools.json)</span></h3>
|
||||
<div class="tutorial-content">
|
||||
<p data-i18n="tutorial.pool.desc">提供商池用于配置多个账号,实现负载均衡和故障转移。每个提供商类型可以配置多个账号节点。</p>
|
||||
|
||||
<div class="config-section">
|
||||
<h4 data-i18n="tutorial.pool.node.title">节点配置参数</h4>
|
||||
<div class="config-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-i18n="tutorial.main.table.param">参数</th>
|
||||
<th data-i18n="tutorial.main.table.type">类型</th>
|
||||
<th data-i18n="tutorial.main.table.desc">说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>uuid</code></td>
|
||||
<td>string</td>
|
||||
<td data-i18n="tutorial.pool.node.uuid">节点唯一标识,自动生成</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>name</code></td>
|
||||
<td>string</td>
|
||||
<td data-i18n="tutorial.pool.node.name">节点自定义名称</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>oauthCredsFilePath</code></td>
|
||||
<td>string</td>
|
||||
<td data-i18n="tutorial.pool.node.oauth">OAuth 凭据文件路径</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>checkHealth</code></td>
|
||||
<td>boolean</td>
|
||||
<td data-i18n="tutorial.pool.node.health">是否启用健康检查</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>checkModel</code></td>
|
||||
<td>string</td>
|
||||
<td data-i18n="tutorial.pool.node.model">健康检查使用的模型</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>notSupportedModels</code></td>
|
||||
<td>array</td>
|
||||
<td data-i18n="tutorial.pool.node.unsupported">该节点不支持的模型列表</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>disabled</code></td>
|
||||
<td>boolean</td>
|
||||
<td data-i18n="tutorial.pool.node.disabled">是否禁用该节点</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-example">
|
||||
<h4 data-i18n="tutorial.pool.example.title">配置示例</h4>
|
||||
<pre><code>{
|
||||
"gemini-cli-oauth": [
|
||||
{
|
||||
"uuid": "gemini-account-1",
|
||||
"name": "Gemini 账号 1",
|
||||
"oauthCredsFilePath": "configs/gemini/oauth_creds_1.json",
|
||||
"checkHealth": true,
|
||||
"checkModel": "gemini-2.0-flash-exp"
|
||||
},
|
||||
{
|
||||
"uuid": "gemini-account-2",
|
||||
"name": "Gemini 账号 2",
|
||||
"oauthCredsFilePath": "configs/gemini/oauth_creds_2.json",
|
||||
"checkHealth": true,
|
||||
"notSupportedModels": ["gemini-3.0-pro"]
|
||||
}
|
||||
],
|
||||
"claude-kiro-oauth": [
|
||||
{
|
||||
"uuid": "kiro-account-1",
|
||||
"name": "Kiro 账号 1",
|
||||
"oauthCredsFilePath": "configs/kiro/kiro-auth-token.json",
|
||||
"checkHealth": true,
|
||||
"checkModel": "claude-sonnet-4-5"
|
||||
}
|
||||
]
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fallback 配置 -->
|
||||
<div class="tutorial-panel">
|
||||
<h3><i class="fas fa-random"></i> <span data-i18n="tutorial.fallback.title">Fallback 降级配置</span></h3>
|
||||
<div class="tutorial-content">
|
||||
<p data-i18n="tutorial.fallback.desc">当某一提供商类型的所有账号都不可用时,可以自动切换到配置的备用提供商。</p>
|
||||
|
||||
<div class="config-section">
|
||||
<h4 data-i18n="tutorial.fallback.chain.title">跨类型 Fallback 链</h4>
|
||||
<p data-i18n="tutorial.fallback.chain.desc">在 config.json 中配置 providerFallbackChain,指定每个提供商类型的备用类型:</p>
|
||||
<pre><code>{
|
||||
"providerFallbackChain": {
|
||||
"gemini-cli-oauth": ["gemini-antigravity"],
|
||||
"gemini-antigravity": ["gemini-cli-oauth"],
|
||||
"claude-kiro-oauth": ["claude-custom"],
|
||||
"claude-custom": ["claude-kiro-oauth"]
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="config-section">
|
||||
<h4 data-i18n="tutorial.fallback.model.title">跨协议模型映射</h4>
|
||||
<p data-i18n="tutorial.fallback.model.desc">当主提供商不可用时,可以将特定模型映射到其他协议的提供商:</p>
|
||||
<pre><code>{
|
||||
"modelFallbackMapping": {
|
||||
"gemini-claude-opus-4-5-thinking": {
|
||||
"targetProviderType": "claude-kiro-oauth",
|
||||
"targetModel": "claude-opus-4-5"
|
||||
},
|
||||
"claude-opus-4-5": {
|
||||
"targetProviderType": "gemini-antigravity",
|
||||
"targetModel": "gemini-claude-opus-4-5-thinking"
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 代理配置 -->
|
||||
<div class="tutorial-panel">
|
||||
<h3><i class="fas fa-globe"></i> <span data-i18n="tutorial.proxy.title">代理配置</span></h3>
|
||||
<div class="tutorial-content">
|
||||
<p data-i18n="tutorial.proxy.desc">支持为特定提供商配置代理,用于网络受限环境。</p>
|
||||
|
||||
<div class="config-section">
|
||||
<h4 data-i18n="tutorial.proxy.config.title">代理配置参数</h4>
|
||||
<div class="config-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-i18n="tutorial.main.table.param">参数</th>
|
||||
<th data-i18n="tutorial.main.table.type">类型</th>
|
||||
<th data-i18n="tutorial.main.table.desc">说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>PROXY_URL</code></td>
|
||||
<td>string</td>
|
||||
<td data-i18n="tutorial.proxy.url">代理地址,支持 HTTP、HTTPS、SOCKS5</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>PROXY_ENABLED_PROVIDERS</code></td>
|
||||
<td>array</td>
|
||||
<td data-i18n="tutorial.proxy.providers">启用代理的提供商列表</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-example">
|
||||
<h4 data-i18n="tutorial.proxy.example.title">配置示例</h4>
|
||||
<pre><code>{
|
||||
"PROXY_URL": "http://127.0.0.1:7890",
|
||||
"PROXY_ENABLED_PROVIDERS": [
|
||||
"gemini-cli-oauth",
|
||||
"gemini-antigravity"
|
||||
]
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="tutorial-note">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<span data-i18n="tutorial.proxy.note">支持的代理类型:HTTP (http://)、HTTPS (https://)、SOCKS5 (socks5://)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OAuth 授权配置 -->
|
||||
<div class="tutorial-panel">
|
||||
<h3><i class="fas fa-key"></i> <span data-i18n="tutorial.oauth.title">OAuth 授权配置</span></h3>
|
||||
<div class="tutorial-content">
|
||||
<p data-i18n="tutorial.oauth.desc">各提供商的 OAuth 凭据文件默认存储位置:</p>
|
||||
|
||||
<div class="oauth-path-list">
|
||||
<div class="oauth-path-item">
|
||||
<div class="path-header">
|
||||
<i class="fas fa-gem"></i>
|
||||
<span class="path-provider">Gemini</span>
|
||||
</div>
|
||||
<code class="path-value">~/.gemini/oauth_creds.json</code>
|
||||
</div>
|
||||
<div class="oauth-path-item">
|
||||
<div class="path-header">
|
||||
<i class="fas fa-rocket"></i>
|
||||
<span class="path-provider">Antigravity</span>
|
||||
</div>
|
||||
<code class="path-value">~/.antigravity/oauth_creds.json</code>
|
||||
</div>
|
||||
<div class="oauth-path-item">
|
||||
<div class="path-header">
|
||||
<i class="fas fa-robot"></i>
|
||||
<span class="path-provider">Kiro</span>
|
||||
</div>
|
||||
<code class="path-value">~/.aws/sso/cache/kiro-auth-token.json</code>
|
||||
</div>
|
||||
<div class="oauth-path-item">
|
||||
<div class="path-header">
|
||||
<i class="fas fa-code"></i>
|
||||
<span class="path-provider">Qwen</span>
|
||||
</div>
|
||||
<code class="path-value">~/.qwen/oauth_creds.json</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tutorial-note">
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
<span data-i18n="tutorial.oauth.note">推荐通过 Web UI 控制台的"提供商池管理"页面点击"生成授权"按钮进行可视化授权,系统会自动保存凭据文件。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 日志配置 -->
|
||||
<div class="tutorial-panel">
|
||||
<h3><i class="fas fa-file-alt"></i> <span data-i18n="tutorial.log.title">日志配置</span></h3>
|
||||
<div class="tutorial-content">
|
||||
<div class="config-section">
|
||||
<h4 data-i18n="tutorial.log.prompt.title">提示日志配置</h4>
|
||||
<div class="config-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-i18n="tutorial.main.table.param">参数</th>
|
||||
<th data-i18n="tutorial.main.table.type">类型</th>
|
||||
<th data-i18n="tutorial.main.table.desc">说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>PROMPT_LOG_MODE</code></td>
|
||||
<td>string</td>
|
||||
<td data-i18n="tutorial.log.mode">日志模式:none(关闭)、console(控制台)、file(文件)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>PROMPT_LOG_BASE_NAME</code></td>
|
||||
<td>string</td>
|
||||
<td data-i18n="tutorial.log.basename">日志文件基础名称</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-example">
|
||||
<h4 data-i18n="tutorial.log.example.title">配置示例</h4>
|
||||
<pre><code>{
|
||||
"PROMPT_LOG_MODE": "file",
|
||||
"PROMPT_LOG_BASE_NAME": "prompt_log"
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -5,6 +5,12 @@
|
|||
<a href="#dashboard" class="nav-item active" data-section="dashboard" aria-label="Dashboard" data-i18n-aria-label="nav.dashboard">
|
||||
<i class="fas fa-tachometer-alt" aria-hidden="true"></i> <span data-i18n="nav.dashboard">仪表盘</span>
|
||||
</a>
|
||||
<a href="#guide" class="nav-item" data-section="guide" aria-label="User Guide" data-i18n-aria-label="nav.guide">
|
||||
<i class="fas fa-book" aria-hidden="true"></i> <span data-i18n="nav.guide">使用指南</span>
|
||||
</a>
|
||||
<a href="#tutorial" class="nav-item" data-section="tutorial" aria-label="Configuration Tutorial" data-i18n-aria-label="nav.tutorial">
|
||||
<i class="fas fa-graduation-cap" aria-hidden="true"></i> <span data-i18n="nav.tutorial">配置教程</span>
|
||||
</a>
|
||||
<a href="#config" class="nav-item" data-section="config" aria-label="Config Management" data-i18n-aria-label="nav.config">
|
||||
<i class="fas fa-cog" aria-hidden="true"></i> <span data-i18n="nav.config">配置管理</span>
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
<script type="module" src="app/language-switcher.js"></script>
|
||||
<script type="module" src="app/theme-switcher.js"></script>
|
||||
<script type="module" src="app/auth.js"></script>
|
||||
<script type="module" src="app/models-manager.js"></script>
|
||||
<script type="module">
|
||||
// 导入组件加载器
|
||||
import { initializeComponents } from './app/component-loader.js';
|
||||
|
|
|
|||
Loading…
Reference in a new issue