- 注释侧边栏"聊天"子菜单,保留"操练场" - 隐藏个人设置中的账户管理模块 - 隐藏其他设置中的价格设置和隐私设置Tab Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>feat/alipay-payment
| @@ -2,6 +2,8 @@ package model | |||
| import ( | |||
| "fmt" | |||
| "strconv" | |||
| "strings" | |||
| "sync" | |||
| "time" | |||
| @@ -209,15 +211,17 @@ func InvalidateChannelPricingCache() { | |||
| // ChannelPricingWithChannel 带渠道信息的定价响应 | |||
| type ChannelPricingWithChannel struct { | |||
| Id int `json:"id"` | |||
| ChannelId int `json:"channel_id"` | |||
| ChannelName string `json:"channel_name"` | |||
| ChannelType int `json:"channel_type"` | |||
| QuotaType int `json:"quota_type"` // 0=按量, 1=按次 | |||
| ModelRatio float64 `json:"model_ratio"` | |||
| CompletionRatio float64 `json:"completion_ratio"` | |||
| ModelPrice float64 `json:"model_price"` | |||
| HasCustomPricing bool `json:"has_custom_pricing"` // 是否有自定义定价 | |||
| Id int `json:"id"` | |||
| ChannelId int `json:"channel_id"` | |||
| ChannelName string `json:"channel_name"` | |||
| ChannelType int `json:"channel_type"` | |||
| TagIds string `json:"tag_ids" gorm:"column:tag_ids"` // 渠道定价的标签ID列表(逗号分隔) | |||
| Tags []*PricingTag `json:"tags" gorm:"-"` // 渠道定价的标签详情(不参与数据库扫描) | |||
| QuotaType int `json:"quota_type"` // 0=按量, 1=按次 | |||
| ModelRatio float64 `json:"model_ratio"` | |||
| CompletionRatio float64 `json:"completion_ratio"` | |||
| ModelPrice float64 `json:"model_price"` | |||
| HasCustomPricing bool `json:"has_custom_pricing"` // 是否有自定义定价 | |||
| } | |||
| // GetChannelPricingByModelWithChannelInfo 获取指定模型的渠道定价(带渠道信息) | |||
| @@ -254,6 +258,7 @@ func GetChannelPricingByModelWithChannelInfo(modelName string) ([]*ChannelPricin | |||
| COALESCE(channel_pricings.completion_ratio, ?) as completion_ratio, | |||
| COALESCE(channel_pricings.model_price, ?) as model_price, | |||
| channel_pricings.id as id, | |||
| channel_pricings.tag_ids as tag_ids, | |||
| (channel_pricings.id IS NOT NULL) as has_custom_pricing`, | |||
| defaultQuotaType, globalModelRatio, globalCompletionRatio, globalModelPrice). | |||
| Joins("LEFT JOIN channels ON abilities.channel_id = channels.id"). | |||
| @@ -261,8 +266,37 @@ func GetChannelPricingByModelWithChannelInfo(modelName string) ([]*ChannelPricin | |||
| Where("abilities.model = ?", modelName). | |||
| Where("abilities.enabled = ?", true). | |||
| Where("channels.status = ?", 1). // 只显示启用的渠道 | |||
| Group("abilities.channel_id"). // 去重(同一渠道可能有多个分组) | |||
| Group("abilities.channel_id, channels.name, channels.type, channel_pricings.quota_type, channel_pricings.model_ratio, channel_pricings.completion_ratio, channel_pricings.model_price, channel_pricings.id, channel_pricings.tag_ids"). | |||
| Scan(&results).Error | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| // 获取所有定价标签 | |||
| allTags, err := GetAllPricingTags() | |||
| if err != nil { | |||
| return results, nil // 如果获取标签失败,仍然返回基础结果 | |||
| } | |||
| // 建立标签ID到标签的映射 | |||
| tagMap := make(map[int]*PricingTag) | |||
| for _, tag := range allTags { | |||
| tagMap[tag.Id] = tag | |||
| } | |||
| // 为每个渠道定价填充标签 | |||
| for _, result := range results { | |||
| if result.TagIds != "" { | |||
| result.Tags = make([]*PricingTag, 0) | |||
| for _, idStr := range strings.Split(result.TagIds, ",") { | |||
| if id, err := strconv.Atoi(strings.TrimSpace(idStr)); err == nil { | |||
| if tag, ok := tagMap[id]; ok { | |||
| result.Tags = append(result.Tags, tag) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return results, err | |||
| return results, nil | |||
| } | |||
| @@ -207,11 +207,11 @@ const SiderBar = ({ onNavigate = () => {} }) => { | |||
| itemKey: 'playground', | |||
| to: '/playground', | |||
| }, | |||
| { | |||
| text: t('聊天'), | |||
| itemKey: 'chat', | |||
| items: chatItems, | |||
| }, | |||
| // { | |||
| // text: t('聊天'), | |||
| // itemKey: 'chat', | |||
| // items: chatItems, | |||
| // }, | |||
| ]; | |||
| // 根据配置过滤项目 | |||
| @@ -40,7 +40,6 @@ const HeaderBar = ({ onMobileMenuToggle, drawerOpen }) => { | |||
| logo, | |||
| isNewYear, | |||
| isSelfUseMode, | |||
| docsLink, | |||
| isDemoSiteMode, | |||
| isConsoleRoute, | |||
| theme, | |||
| @@ -62,7 +61,7 @@ const HeaderBar = ({ onMobileMenuToggle, drawerOpen }) => { | |||
| getUnreadKeys, | |||
| } = useNotifications(statusState); | |||
| const { mainNavLinks } = useNavigation(t, docsLink, headerNavModules); | |||
| const { mainNavLinks } = useNavigation(t, headerNavModules); | |||
| return ( | |||
| <header className='text-semi-color-text-0 sticky top-0 z-50 transition-colors duration-300 bg-white/75 dark:bg-zinc-900/75 backdrop-blur-lg'> | |||
| @@ -463,9 +463,9 @@ const PersonalSetting = () => { | |||
| {/* 账户管理和其他设置 */} | |||
| <div className='grid grid-cols-1 xl:grid-cols-2 items-start gap-4 md:gap-6 mt-4 md:mt-6'> | |||
| {/* 左侧:账户管理设置 */} | |||
| {/* 左侧:账户管理设置 - 已注释 */} | |||
| <div className='flex flex-col gap-4 md:gap-6'> | |||
| <AccountManagement | |||
| {/* <AccountManagement | |||
| t={t} | |||
| userState={userState} | |||
| status={status} | |||
| @@ -482,7 +482,7 @@ const PersonalSetting = () => { | |||
| passkeyDeleteLoading={passkeyDeleteLoading} | |||
| onPasskeyRegister={handleRegisterPasskey} | |||
| onPasskeyDelete={handleRemovePasskey} | |||
| /> | |||
| /> */} | |||
| {/* 偏好设置(语言等) */} | |||
| <PreferencesSettings t={t} /> | |||
| @@ -731,8 +731,8 @@ const NotificationSettings = ({ | |||
| </div> | |||
| </TabPane> | |||
| {/* 价格设置 Tab */} | |||
| <TabPane | |||
| {/* 价格设置 Tab - 已注释 */} | |||
| {/* <TabPane | |||
| tab={ | |||
| <div className='flex items-center'> | |||
| <DollarSign size={16} className='mr-2' /> | |||
| @@ -755,10 +755,10 @@ const NotificationSettings = ({ | |||
| )} | |||
| /> | |||
| </div> | |||
| </TabPane> | |||
| </TabPane> */} | |||
| {/* 隐私设置 Tab */} | |||
| <TabPane | |||
| {/* 隐私设置 Tab - 已注释 */} | |||
| {/* <TabPane | |||
| tab={ | |||
| <div className='flex items-center'> | |||
| <ShieldCheck size={16} className='mr-2' /> | |||
| @@ -779,7 +779,7 @@ const NotificationSettings = ({ | |||
| )} | |||
| /> | |||
| </div> | |||
| </TabPane> | |||
| </TabPane> */} | |||
| {/* 左侧边栏设置 Tab - 根据后端权限控制显示 */} | |||
| {hasSidebarSettingsPermission() && ( | |||
| @@ -21,23 +21,9 @@ import React, { useState, useEffect, useMemo } from 'react'; | |||
| import { Card, Avatar, Typography, Table, Tag, Spin, Tooltip } from '@douyinfe/semi-ui'; | |||
| import { IconServer, IconEditStroked } from '@douyinfe/semi-icons'; | |||
| import { API } from '../../../../../helpers'; | |||
| import { CHANNEL_OPTIONS } from '../../../../../constants'; | |||
| const { Text } = Typography; | |||
| // 渠道类型映射表 - 移到组件外部避免重复计算 | |||
| const channelTypeMap = (() => { | |||
| const map = {}; | |||
| CHANNEL_OPTIONS.forEach((opt) => { | |||
| map[opt.value] = opt.label; | |||
| }); | |||
| return map; | |||
| })(); | |||
| const getChannelTypeName = (type) => { | |||
| return channelTypeMap[type] || `类型 ${type}`; | |||
| }; | |||
| const ChannelPricingCard = ({ | |||
| modelName, | |||
| currency, | |||
| @@ -117,7 +103,8 @@ const ChannelPricingCard = ({ | |||
| // 准备表格数据 | |||
| const tableData = channelPricingData.map((item, index) => ({ | |||
| key: item.channel_id || index, | |||
| channelName: item.channel_name || `渠道 ${item.channel_id}`, | |||
| channelName: `渠道${['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'][index] || ` ${index + 1}`}`, | |||
| channelTags: item.tags || [], // 渠道定价的标签列表 | |||
| channelType: item.channel_type, | |||
| quotaType: item.quota_type, | |||
| modelRatio: item.model_ratio, | |||
| @@ -137,13 +124,21 @@ const ChannelPricingCard = ({ | |||
| title: t('渠道'), | |||
| dataIndex: 'channelName', | |||
| render: (text, record) => ( | |||
| <div className='flex items-center gap-2'> | |||
| <div className='flex items-center gap-2 flex-wrap'> | |||
| <Tag color='cyan' size='small' shape='circle'> | |||
| {text} | |||
| </Tag> | |||
| <span className='text-xs text-gray-500'> | |||
| {getChannelTypeName(record.channelType)} | |||
| </span> | |||
| {record.channelTags.map((tag, idx) => ( | |||
| <Tag | |||
| key={idx} | |||
| color={tag.color || 'default'} | |||
| size='small' | |||
| shape='circle' | |||
| style={{ backgroundColor: tag.color, color: '#fff' }} | |||
| > | |||
| {tag.name} | |||
| </Tag> | |||
| ))} | |||
| {record.hasCustomPricing && ( | |||
| <Tooltip content={t('已设置自定义定价')}> | |||
| <IconEditStroked size='small' style={{ color: 'var(--semi-color-primary)' }} /> | |||
| @@ -181,37 +181,39 @@ const ModelPricingTable = ({ | |||
| ); | |||
| }; | |||
| return ( | |||
| <Card className='!rounded-2xl shadow-sm border-0'> | |||
| <div className='flex items-center mb-4'> | |||
| <Avatar size='small' color='orange' className='mr-2 shadow-md'> | |||
| <IconCoinMoneyStroked size={16} /> | |||
| </Avatar> | |||
| <div> | |||
| <Text className='text-lg font-medium'>{t('分组价格')}</Text> | |||
| <div className='text-xs text-gray-600'> | |||
| {t('不同用户分组的价格信息')} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {autoChain.length > 0 && ( | |||
| <div className='flex flex-wrap items-center gap-1 mb-4'> | |||
| <span className='text-sm text-gray-600'>{t('auto分组调用链路')}</span> | |||
| <span className='text-sm'>→</span> | |||
| {autoChain.map((g, idx) => ( | |||
| <React.Fragment key={g}> | |||
| <Tag color='white' size='small' shape='circle'> | |||
| {g} | |||
| {t('分组')} | |||
| </Tag> | |||
| {idx < autoChain.length - 1 && <span className='text-sm'>→</span>} | |||
| </React.Fragment> | |||
| ))} | |||
| </div> | |||
| )} | |||
| {renderGroupPriceTable()} | |||
| </Card> | |||
| ); | |||
| // 分组价格已隐藏 | |||
| // return ( | |||
| // <Card className='!rounded-2xl shadow-sm border-0'> | |||
| // <div className='flex items-center mb-4'> | |||
| // <Avatar size='small' color='orange' className='mr-2 shadow-md'> | |||
| // <IconCoinMoneyStroked size={16} /> | |||
| // </Avatar> | |||
| // <div> | |||
| // <Text className='text-lg font-medium'>{t('分组价格')}</Text> | |||
| // <div className='text-xs text-gray-600'> | |||
| // {t('不同用户分组的价格信息')} | |||
| // </div> | |||
| // </div> | |||
| // </div> | |||
| // {autoChain.length > 0 && ( | |||
| // <div className='flex flex-wrap items-center gap-1 mb-4'> | |||
| // <span className='text-sm text-gray-600'>{t('auto分组调用链路')}</span> | |||
| // <span className='text-sm'>→</span> | |||
| // {autoChain.map((g, idx) => ( | |||
| // <React.Fragment key={g}> | |||
| // <Tag color='white' size='small' shape='circle'> | |||
| // {g} | |||
| // {t('分组')} | |||
| // </Tag> | |||
| // {idx < autoChain.length - 1 && <span className='text-sm'>→</span>} | |||
| // </React.Fragment> | |||
| // ))} | |||
| // </div> | |||
| // )} | |||
| // {renderGroupPriceTable()} | |||
| // </Card> | |||
| // ); | |||
| return null; | |||
| }; | |||
| export default ModelPricingTable; | |||
| @@ -333,7 +333,7 @@ const PricingCardView = ({ | |||
| /> | |||
| </Tooltip> | |||
| </div> | |||
| <div className='grid grid-cols-3 gap-2 text-xs text-gray-600'> | |||
| <div className='grid grid-cols-2 gap-2 text-xs text-gray-600'> | |||
| <div> | |||
| {t('模型')}:{' '} | |||
| {model.quota_type === 0 ? model.model_ratio : t('无')} | |||
| @@ -344,9 +344,11 @@ const PricingCardView = ({ | |||
| ? parseFloat(model.completion_ratio.toFixed(3)) | |||
| : t('无')} | |||
| </div> | |||
| {/* 分组定价已隐藏 | |||
| <div> | |||
| {t('分组')}: {priceData?.usedGroupRatio ?? '-'} | |||
| </div> | |||
| */} | |||
| </div> | |||
| </div> | |||
| )} | |||
| @@ -19,15 +19,13 @@ For commercial licensing, please contact support@quantumnous.com | |||
| import { useMemo } from 'react'; | |||
| export const useNavigation = (t, docsLink, headerNavModules) => { | |||
| export const useNavigation = (t, headerNavModules) => { | |||
| const mainNavLinks = useMemo(() => { | |||
| // 默认配置,如果没有传入配置则显示所有模块 | |||
| const defaultModules = { | |||
| home: true, | |||
| console: true, | |||
| pricing: true, | |||
| docs: true, | |||
| about: true, | |||
| }; | |||
| // 使用传入的配置或默认配置 | |||
| @@ -49,28 +47,10 @@ export const useNavigation = (t, docsLink, headerNavModules) => { | |||
| itemKey: 'pricing', | |||
| to: '/pricing', | |||
| }, | |||
| ...(docsLink | |||
| ? [ | |||
| { | |||
| text: t('文档'), | |||
| itemKey: 'docs', | |||
| isExternal: true, | |||
| externalLink: docsLink, | |||
| }, | |||
| ] | |||
| : []), | |||
| { | |||
| text: t('关于'), | |||
| itemKey: 'about', | |||
| to: '/about', | |||
| }, | |||
| ]; | |||
| // 根据配置过滤导航链接 | |||
| return allLinks.filter((link) => { | |||
| if (link.itemKey === 'docs') { | |||
| return docsLink && modules.docs; | |||
| } | |||
| if (link.itemKey === 'pricing') { | |||
| // 支持新的pricing配置格式 | |||
| return typeof modules.pricing === 'object' | |||
| @@ -79,7 +59,7 @@ export const useNavigation = (t, docsLink, headerNavModules) => { | |||
| } | |||
| return modules[link.itemKey] === true; | |||
| }); | |||
| }, [t, docsLink, headerNavModules]); | |||
| }, [t, headerNavModules]); | |||
| return { | |||
| mainNavLinks, | |||
| @@ -2877,6 +2877,48 @@ | |||
| "modelType.rerank": "Rerank", | |||
| "modelType.vision": "Vision", | |||
| "modelType.other": "Other", | |||
| "面向企业的 AI 生产力基座": "Enterprise AI Productivity Infrastructure", | |||
| "统一的大模型接口网关": "Unified LLM API Gateway", | |||
| "链接全球 AI 能力": "Connect Global AI", | |||
| "以一套域名、密钥与风控策略连接全球大模型资源,保障可观测、可拓展、可控。": "Connect to global LLM resources with a single domain, key, and policy. Observable, scalable, and controllable.", | |||
| "替换基础 URL 即可接入": "Replace base URL to connect", | |||
| "核心价值": "Core Values", | |||
| "让团队稳定使用大模型,更快落地 AI 创新": "Empower teams with stable LLM access for faster AI innovation", | |||
| "从访问控制、成本可视化到全局调度,为企业提供端到端的 AI 基础设施能力。": "End-to-end AI infrastructure from access control to cost visibility and global scheduling.", | |||
| "统一入口,极速连通": "Unified Entry, Instant Connect", | |||
| "以单一域名和密钥连接全部大模型供应商,智能容灾切换确保业务不中断。": "Connect all LLM providers with a single domain and key. Smart failover ensures uninterrupted service.", | |||
| "全栈可观测与风控": "Full-stack Observability & Control", | |||
| "实时监控调用量、错误率与费用,一键配置限流、告警与安全策略。": "Real-time monitoring of calls, errors, and costs. Configure rate limits, alerts, and security policies.", | |||
| "按需扩容与成本优化": "On-demand Scaling & Cost Optimization", | |||
| "多渠道配额、智能路由与批量任务调度,灵活控制成本与并发能力。": "Multi-channel quotas, smart routing, and batch scheduling for flexible cost and concurrency control.", | |||
| "开发者友好体验": "Developer-friendly Experience", | |||
| "兼容 OpenAI 接口协议,提供 SDK、示例与 Web Playground,轻松迭代上线。": "OpenAI-compatible API. SDKs, examples, and web playground for easy integration.", | |||
| "工作流": "Workflow", | |||
| "用 3 个步骤构建你的 AI 控制平面": "Build your AI control plane in 3 steps", | |||
| "接入配置": "Configuration", | |||
| "在控制台创建渠道、设置密钥与限额,导入模型列表。": "Create channels, set keys and quotas, import model lists in the console.", | |||
| "智能调度": "Smart Scheduling", | |||
| "根据健康度、延迟与价格自动选择最优模型通道,内置故障切换。": "Auto-select optimal model channels by health, latency, and price. Built-in failover.", | |||
| "持续洞察": "Continuous Insights", | |||
| "通过仪表盘追踪调用趋势、消耗与失败率,实时告警确保 SLO。": "Track call trends, costs, and failure rates via dashboard. Real-time alerts ensure SLO.", | |||
| "生态伙伴": "Ecosystem Partners", | |||
| "与主流模型供应商深度对接": "Deep integration with leading model providers", | |||
| "保持统一协议,快速切换与扩展模型能力,随时接入最新生态。": "Unified protocol for quick switching and scaling. Access the latest ecosystem anytime.", | |||
| "将大模型能力真正落地到业务流程": "Bring LLM capabilities to your business workflows", | |||
| "立即接入,统一控制访问策略与调用成本,为产品带来稳定、可扩展的智能体验。": "Start now. Unified access control and cost management for stable, scalable AI experiences.", | |||
| "立即开始": "Get Started", | |||
| "查看文档": "Documentation", | |||
| "文档": "Docs", | |||
| "关于": "About", | |||
| "服务条款": "Terms of Service", | |||
| "使用政策": "Usage Policy", | |||
| "版权所有": "All rights reserved", | |||
| "实时调度": "Real-time Scheduling", | |||
| "健康度与延迟权重动态切换,保证最优响应。": "Dynamic health and latency switching for optimal response.", | |||
| "统一监控": "Unified Monitoring", | |||
| "调用、费用、异常一站式可视化,随时掌握运行状态。": "One-stop visualization for calls, costs, and anomalies.", | |||
| "智能限流": "Smart Rate Limiting", | |||
| "多维策略保障核心业务优先级,避免突发拥堵。": "Multi-dimensional policies ensure core business priority and prevent congestion.", | |||
| "设计版本": "b80c3466cb6feafeb3990c7820e10e50" | |||
| } | |||
| } | |||
| @@ -2854,6 +2854,48 @@ | |||
| "modelType.embedding": "嵌入", | |||
| "modelType.rerank": "重排序", | |||
| "modelType.vision": "视觉", | |||
| "modelType.other": "其他" | |||
| "modelType.other": "其他", | |||
| "面向企业的 AI 生产力基座": "面向企业的 AI 生产力基座", | |||
| "统一的大模型接口网关": "统一的大模型接口网关", | |||
| "链接全球 AI 能力": "链接全球 AI 能力", | |||
| "以一套域名、密钥与风控策略连接全球大模型资源,保障可观测、可拓展、可控。": "以一套域名、密钥与风控策略连接全球大模型资源,保障可观测、可拓展、可控。", | |||
| "替换基础 URL 即可接入": "替换基础 URL 即可接入", | |||
| "核心价值": "核心价值", | |||
| "让团队稳定使用大模型,更快落地 AI 创新": "让团队稳定使用大模型,更快落地 AI 创新", | |||
| "从访问控制、成本可视化到全局调度,为企业提供端到端的 AI 基础设施能力。": "从访问控制、成本可视化到全局调度,为企业提供端到端的 AI 基础设施能力。", | |||
| "统一入口,极速连通": "统一入口,极速连通", | |||
| "以单一域名和密钥连接全部大模型供应商,智能容灾切换确保业务不中断。": "以单一域名和密钥连接全部大模型供应商,智能容灾切换确保业务不中断。", | |||
| "全栈可观测与风控": "全栈可观测与风控", | |||
| "实时监控调用量、错误率与费用,一键配置限流、告警与安全策略。": "实时监控调用量、错误率与费用,一键配置限流、告警与安全策略。", | |||
| "按需扩容与成本优化": "按需扩容与成本优化", | |||
| "多渠道配额、智能路由与批量任务调度,灵活控制成本与并发能力。": "多渠道配额、智能路由与批量任务调度,灵活控制成本与并发能力。", | |||
| "开发者友好体验": "开发者友好体验", | |||
| "兼容 OpenAI 接口协议,提供 SDK、示例与 Web Playground,轻松迭代上线。": "兼容 OpenAI 接口协议,提供 SDK、示例与 Web Playground,轻松迭代上线。", | |||
| "工作流": "工作流", | |||
| "用 3 个步骤构建你的 AI 控制平面": "用 3 个步骤构建你的 AI 控制平面", | |||
| "接入配置": "接入配置", | |||
| "在控制台创建渠道、设置密钥与限额,导入模型列表。": "在控制台创建渠道、设置密钥与限额,导入模型列表。", | |||
| "智能调度": "智能调度", | |||
| "根据健康度、延迟与价格自动选择最优模型通道,内置故障切换。": "根据健康度、延迟与价格自动选择最优模型通道,内置故障切换。", | |||
| "持续洞察": "持续洞察", | |||
| "通过仪表盘追踪调用趋势、消耗与失败率,实时告警确保 SLO。": "通过仪表盘追踪调用趋势、消耗与失败率,实时告警确保 SLO。", | |||
| "生态伙伴": "生态伙伴", | |||
| "与主流模型供应商深度对接": "与主流模型供应商深度对接", | |||
| "保持统一协议,快速切换与扩展模型能力,随时接入最新生态。": "保持统一协议,快速切换与扩展模型能力,随时接入最新生态。", | |||
| "将大模型能力真正落地到业务流程": "将大模型能力真正落地到业务流程", | |||
| "立即接入,统一控制访问策略与调用成本,为产品带来稳定、可扩展的智能体验。": "立即接入,统一控制访问策略与调用成本,为产品带来稳定、可扩展的智能体验。", | |||
| "立即开始": "立即开始", | |||
| "查看文档": "查看文档", | |||
| "文档": "文档", | |||
| "关于": "关于", | |||
| "服务条款": "服务条款", | |||
| "使用政策": "使用政策", | |||
| "版权所有": "版权所有", | |||
| "实时调度": "实时调度", | |||
| "健康度与延迟权重动态切换,保证最优响应。": "健康度与延迟权重动态切换,保证最优响应。", | |||
| "统一监控": "统一监控", | |||
| "调用、费用、异常一站式可视化,随时掌握运行状态。": "调用、费用、异常一站式可视化,随时掌握运行状态。", | |||
| "智能限流": "智能限流", | |||
| "多维策略保障核心业务优先级,避免突发拥堵。": "多维策略保障核心业务优先级,避免突发拥堵。" | |||
| } | |||
| } | |||
| @@ -875,3 +875,551 @@ html.dark .with-pastel-balls::before { | |||
| .semi-datepicker-range-input { | |||
| border-radius: 10px !important; | |||
| } | |||
| /* ==================== 首页 Snap 滚动样式 ==================== */ | |||
| .home-snap-container { | |||
| height: 100vh; | |||
| height: 100dvh; | |||
| overflow-y: auto; | |||
| overflow-x: hidden; | |||
| scroll-snap-type: y mandatory; | |||
| scroll-behavior: smooth; | |||
| -webkit-overflow-scrolling: touch; | |||
| } | |||
| .home-snap-container::-webkit-scrollbar { | |||
| width: 0; | |||
| display: none; | |||
| } | |||
| .home-snap-section { | |||
| min-height: 100vh; | |||
| min-height: 100dvh; | |||
| scroll-snap-align: start; | |||
| scroll-snap-stop: always; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| position: relative; | |||
| overflow: hidden; | |||
| } | |||
| /* Hero Section 特殊处理 - 不强制全屏 */ | |||
| .home-snap-section-hero { | |||
| min-height: max(100vh, fit-content); | |||
| min-height: max(100dvh, fit-content); | |||
| } | |||
| /* 模型广场 Section - 允许超出 */ | |||
| .home-snap-section-pricing { | |||
| min-height: 100vh; | |||
| min-height: 100dvh; | |||
| scroll-snap-align: start; | |||
| scroll-snap-stop: always; | |||
| } | |||
| /* ==================== 首页 Hero Section 样式 ==================== */ | |||
| .hero-gradient-bg { | |||
| background: radial-gradient( | |||
| ellipse 80% 50% at 50% -20%, | |||
| rgba(99, 102, 241, 0.15), | |||
| transparent 50% | |||
| ); | |||
| } | |||
| .hero-blur-ball-1 { | |||
| position: absolute; | |||
| width: 420px; | |||
| height: 420px; | |||
| border-radius: 50%; | |||
| filter: blur(100px); | |||
| pointer-events: none; | |||
| background: radial-gradient(circle, rgba(99, 102, 241, 0.45) 0%, transparent 70%); | |||
| top: -96px; | |||
| left: -128px; | |||
| opacity: 0.35; | |||
| } | |||
| .hero-blur-ball-2 { | |||
| position: absolute; | |||
| width: 360px; | |||
| height: 360px; | |||
| border-radius: 50%; | |||
| filter: blur(100px); | |||
| pointer-events: none; | |||
| background: radial-gradient(circle, rgba(20, 184, 166, 0.4) 0%, transparent 70%); | |||
| bottom: -80px; | |||
| right: -96px; | |||
| opacity: 0.28; | |||
| } | |||
| /* 浅色主题下降低透明度 */ | |||
| html:not(.dark) .hero-blur-ball-1 { | |||
| opacity: 0.2; | |||
| } | |||
| html:not(.dark) .hero-blur-ball-2 { | |||
| opacity: 0.15; | |||
| } | |||
| /* ==================== 功能卡片样式 ==================== */ | |||
| .feature-card-container { | |||
| position: relative; | |||
| max-width: 360px; | |||
| margin: 0 auto; | |||
| } | |||
| .feature-card-container::before { | |||
| content: ''; | |||
| position: absolute; | |||
| inset: 0; | |||
| border-radius: 32px; | |||
| opacity: 0.5; | |||
| filter: blur(48px); | |||
| background: linear-gradient(135deg, rgba(99, 102, 241, 0.35), rgba(20, 184, 166, 0.25)); | |||
| z-index: 0; | |||
| } | |||
| .feature-card { | |||
| position: relative; | |||
| z-index: 1; | |||
| display: flex; | |||
| flex-direction: column; | |||
| gap: 20px; | |||
| padding: 24px; | |||
| border-radius: 28px; | |||
| border: 1px solid var(--semi-color-border); | |||
| background-color: var(--semi-color-bg-0); | |||
| backdrop-filter: blur(16px); | |||
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08); | |||
| } | |||
| .feature-card-item { | |||
| display: flex; | |||
| flex-direction: column; | |||
| gap: 8px; | |||
| padding: 16px 20px; | |||
| border-radius: 16px; | |||
| border: 1px solid var(--semi-color-border); | |||
| background-color: var(--semi-color-bg-0); | |||
| transition: all 0.3s ease; | |||
| cursor: default; | |||
| } | |||
| .feature-card-item:hover { | |||
| transform: translateY(-4px); | |||
| box-shadow: 0 8px 24px rgba(99, 102, 241, 0.12); | |||
| } | |||
| .feature-card-icon { | |||
| width: 40px; | |||
| height: 40px; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| border-radius: 12px; | |||
| background-color: rgba(99, 102, 241, 0.14); | |||
| color: var(--semi-color-primary); | |||
| transition: transform 0.3s ease; | |||
| } | |||
| .feature-card-item:hover .feature-card-icon { | |||
| transform: scale(1.05); | |||
| } | |||
| /* ==================== 价值卡片样式 ==================== */ | |||
| .value-card { | |||
| position: relative; | |||
| display: flex; | |||
| flex-direction: column; | |||
| gap: 20px; | |||
| padding: 24px; | |||
| border-radius: 16px; | |||
| border: 1px solid var(--semi-color-border); | |||
| background-color: var(--semi-color-bg-0); | |||
| transition: all 0.3s ease; | |||
| overflow: hidden; | |||
| } | |||
| .value-card::before { | |||
| content: ''; | |||
| position: absolute; | |||
| inset: 0; | |||
| background: radial-gradient(120% 100% at 50% 0%, rgba(99, 102, 241, 0.18), transparent); | |||
| opacity: 0; | |||
| transition: opacity 0.3s ease; | |||
| pointer-events: none; | |||
| z-index: 0; | |||
| } | |||
| .value-card::after { | |||
| content: ''; | |||
| position: absolute; | |||
| top: -40px; | |||
| left: 32px; | |||
| right: 32px; | |||
| height: 112px; | |||
| border-radius: 50%; | |||
| background: linear-gradient(to bottom right, rgba(99, 102, 241, 0.25), transparent, transparent); | |||
| opacity: 0; | |||
| transition: opacity 0.3s ease; | |||
| pointer-events: none; | |||
| z-index: 0; | |||
| } | |||
| .value-card:hover { | |||
| transform: translateY(-4px); | |||
| box-shadow: 0 12px 32px rgba(0, 0, 0, 0.08); | |||
| } | |||
| .value-card:hover::before, | |||
| .value-card:hover::after { | |||
| opacity: 1; | |||
| } | |||
| .value-card-icon { | |||
| position: relative; | |||
| z-index: 1; | |||
| width: 48px; | |||
| height: 48px; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| border-radius: 16px; | |||
| background-color: rgba(99, 102, 241, 0.16); | |||
| color: var(--semi-color-primary); | |||
| transition: all 0.3s ease; | |||
| } | |||
| .value-card:hover .value-card-icon { | |||
| transform: scale(1.1) rotate(3deg); | |||
| } | |||
| .value-card-icon-blue { | |||
| background-color: rgba(14, 165, 233, 0.16); | |||
| } | |||
| .value-card-icon-teal { | |||
| background-color: rgba(20, 184, 166, 0.16); | |||
| } | |||
| .value-card-icon-purple { | |||
| background-color: rgba(129, 140, 248, 0.18); | |||
| } | |||
| /* ==================== 工作流卡片样式 ==================== */ | |||
| .workflow-card { | |||
| position: relative; | |||
| display: flex; | |||
| flex-direction: column; | |||
| gap: 12px; | |||
| padding: 24px; | |||
| border-radius: 16px; | |||
| border: 1px solid var(--semi-color-border); | |||
| background-color: var(--semi-color-bg-0); | |||
| transition: all 0.3s ease; | |||
| overflow: hidden; | |||
| } | |||
| .workflow-card::before { | |||
| content: ''; | |||
| position: absolute; | |||
| top: 0; | |||
| left: 0; | |||
| width: 3px; | |||
| height: 100%; | |||
| background: linear-gradient(to bottom, rgba(99, 102, 241, 0.45), rgba(20, 184, 166, 0.45), transparent); | |||
| opacity: 0; | |||
| transition: opacity 0.3s ease; | |||
| } | |||
| .workflow-card:hover::before { | |||
| opacity: 1; | |||
| } | |||
| .workflow-card:hover { | |||
| box-shadow: 0 8px 24px rgba(99, 102, 241, 0.08); | |||
| } | |||
| .workflow-step-number { | |||
| font-size: 12px; | |||
| font-weight: 600; | |||
| text-transform: uppercase; | |||
| letter-spacing: 0.1em; | |||
| color: var(--semi-color-text-2); | |||
| } | |||
| /* ==================== 伙伴 Logo 样式 ==================== */ | |||
| .partner-logo-container { | |||
| display: flex; | |||
| flex-wrap: wrap; | |||
| justify-content: center; | |||
| gap: 16px; | |||
| } | |||
| .partner-logo { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| width: 112px; | |||
| height: 64px; | |||
| border-radius: 16px; | |||
| border: 1px solid var(--semi-color-border); | |||
| background-color: var(--semi-color-bg-0); | |||
| transition: all 0.3s ease; | |||
| cursor: pointer; | |||
| } | |||
| .partner-logo:hover { | |||
| transform: translateY(-8px); | |||
| box-shadow: 0 12px 32px rgba(0, 0, 0, 0.1); | |||
| } | |||
| /* Logo 浮动动画 */ | |||
| @keyframes partner-float { | |||
| 0%, 100% { | |||
| transform: translateY(0) scale(1); | |||
| } | |||
| 50% { | |||
| transform: translateY(-6px) scale(1.05); | |||
| } | |||
| } | |||
| .partner-logo-animated { | |||
| animation: partner-float 3s ease-in-out infinite; | |||
| } | |||
| /* 错开动画时间 */ | |||
| .partner-logo:nth-child(1) { animation-delay: 0s; } | |||
| .partner-logo:nth-child(2) { animation-delay: 0.2s; } | |||
| .partner-logo:nth-child(3) { animation-delay: 0.4s; } | |||
| .partner-logo:nth-child(4) { animation-delay: 0.6s; } | |||
| .partner-logo:nth-child(5) { animation-delay: 0.8s; } | |||
| .partner-logo:nth-child(6) { animation-delay: 1s; } | |||
| .partner-logo:nth-child(7) { animation-delay: 1.2s; } | |||
| .partner-logo:nth-child(8) { animation-delay: 1.4s; } | |||
| .partner-logo:nth-child(9) { animation-delay: 1.6s; } | |||
| .partner-logo:nth-child(10) { animation-delay: 1.8s; } | |||
| .partner-logo:nth-child(11) { animation-delay: 2s; } | |||
| .partner-logo:nth-child(12) { animation-delay: 2.2s; } | |||
| .partner-logo:hover { | |||
| animation-play-state: paused; | |||
| } | |||
| /* ==================== 统计数据卡片样式 ==================== */ | |||
| .stat-card { | |||
| display: flex; | |||
| flex-direction: column; | |||
| gap: 4px; | |||
| padding: 12px 16px; | |||
| border-radius: 16px; | |||
| border: 1px dashed rgba(99, 102, 241, 0.25); | |||
| background-color: rgba(99, 102, 241, 0.05); | |||
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); | |||
| } | |||
| .stat-card-value { | |||
| font-size: 1.5rem; | |||
| font-weight: 600; | |||
| color: var(--semi-color-text-0); | |||
| line-height: 1.2; | |||
| } | |||
| @media (min-width: 768px) { | |||
| .stat-card-value { | |||
| font-size: 1.875rem; | |||
| } | |||
| } | |||
| .stat-card-label { | |||
| font-size: 12px; | |||
| text-transform: uppercase; | |||
| letter-spacing: 0.05em; | |||
| color: var(--semi-color-text-2); | |||
| } | |||
| /* ==================== CTA Section 样式 ==================== */ | |||
| .cta-section { | |||
| position: relative; | |||
| overflow: hidden; | |||
| } | |||
| .cta-section::before { | |||
| content: ''; | |||
| position: absolute; | |||
| inset: 0; | |||
| background: radial-gradient(circle at center top, rgba(99, 102, 241, 0.25), transparent 60%); | |||
| opacity: 0.4; | |||
| pointer-events: none; | |||
| } | |||
| .cta-section::after { | |||
| content: ''; | |||
| position: absolute; | |||
| inset: 0; | |||
| background: radial-gradient(circle at 20% -10%, rgba(20, 184, 166, 0.25), transparent 55%); | |||
| pointer-events: none; | |||
| } | |||
| /* ==================== 标签样式 ==================== */ | |||
| .hero-tag { | |||
| display: inline-flex; | |||
| align-items: center; | |||
| padding: 4px 16px; | |||
| border-radius: 9999px; | |||
| border: 1px solid rgba(99, 102, 241, 0.35); | |||
| background-color: rgba(99, 102, 241, 0.12); | |||
| color: var(--semi-color-primary); | |||
| font-size: 12px; | |||
| font-weight: 500; | |||
| letter-spacing: 0.1em; | |||
| text-transform: uppercase; | |||
| } | |||
| /* ==================== API URL 输入框样式 ==================== */ | |||
| .api-url-card { | |||
| max-width: 480px; | |||
| padding: 16px; | |||
| border-radius: 24px; | |||
| border: 1px solid var(--semi-color-border); | |||
| background-color: var(--semi-color-bg-0); | |||
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08); | |||
| backdrop-filter: blur(8px); | |||
| } | |||
| @media (min-width: 768px) { | |||
| .api-url-card { | |||
| padding: 24px; | |||
| } | |||
| } | |||
| /* ==================== 首页 Footer 样式 ==================== */ | |||
| .home-footer { | |||
| padding: 48px 24px; | |||
| background-color: var(--semi-color-bg-0); | |||
| border-top: 1px solid var(--semi-color-border); | |||
| } | |||
| @media (min-width: 768px) { | |||
| .home-footer { | |||
| padding: 64px 96px; | |||
| } | |||
| } | |||
| .home-footer-link { | |||
| color: var(--semi-color-text-1); | |||
| font-size: 14px; | |||
| transition: color 0.2s ease; | |||
| white-space: nowrap; | |||
| } | |||
| .home-footer-link:hover { | |||
| color: var(--semi-color-primary); | |||
| } | |||
| /* ==================== 响应式调整 ==================== */ | |||
| @media (max-width: 1023px) { | |||
| .hero-grid { | |||
| grid-template-columns: 1fr; | |||
| gap: 48px; | |||
| } | |||
| .feature-card-container { | |||
| max-width: 100%; | |||
| } | |||
| } | |||
| @media (max-width: 767px) { | |||
| .home-snap-section { | |||
| min-height: auto; | |||
| padding: 80px 0; | |||
| } | |||
| .home-snap-section-hero { | |||
| min-height: 100vh; | |||
| min-height: 100dvh; | |||
| } | |||
| .partner-logo { | |||
| width: 88px; | |||
| height: 56px; | |||
| } | |||
| .partner-logo-container { | |||
| gap: 12px; | |||
| } | |||
| } | |||
| /* ==================== 工具链卡片 hover-glow 效果 ==================== */ | |||
| .hover-glow-amber { | |||
| position: relative; | |||
| transition: all 0.5s ease; | |||
| } | |||
| .hover-glow-amber::before { | |||
| content: ''; | |||
| position: absolute; | |||
| inset: 0; | |||
| border-radius: inherit; | |||
| background: radial-gradient(circle at center, rgba(251, 191, 36, 0.15), transparent 70%); | |||
| opacity: 0; | |||
| transition: opacity 0.5s ease; | |||
| pointer-events: none; | |||
| } | |||
| .hover-glow-amber:hover::before { | |||
| opacity: 1; | |||
| } | |||
| .hover-glow-amber:hover { | |||
| box-shadow: 0 0 40px rgba(251, 191, 36, 0.2); | |||
| } | |||
| .hover-glow-blue { | |||
| position: relative; | |||
| transition: all 0.5s ease; | |||
| } | |||
| .hover-glow-blue::before { | |||
| content: ''; | |||
| position: absolute; | |||
| inset: 0; | |||
| border-radius: inherit; | |||
| background: radial-gradient(circle at center, rgba(59, 130, 246, 0.15), transparent 70%); | |||
| opacity: 0; | |||
| transition: opacity 0.5s ease; | |||
| pointer-events: none; | |||
| } | |||
| .hover-glow-blue:hover::before { | |||
| opacity: 1; | |||
| } | |||
| .hover-glow-blue:hover { | |||
| box-shadow: 0 0 40px rgba(59, 130, 246, 0.2); | |||
| } | |||
| .hover-glow-purple { | |||
| position: relative; | |||
| transition: all 0.5s ease; | |||
| } | |||
| .hover-glow-purple::before { | |||
| content: ''; | |||
| position: absolute; | |||
| inset: 0; | |||
| border-radius: inherit; | |||
| background: radial-gradient(circle at center, rgba(168, 85, 247, 0.15), transparent 70%); | |||
| opacity: 0; | |||
| transition: opacity 0.5s ease; | |||
| pointer-events: none; | |||
| } | |||
| .hover-glow-purple:hover::before { | |||
| opacity: 1; | |||
| } | |||
| .hover-glow-purple:hover { | |||
| box-shadow: 0 0 40px rgba(168, 85, 247, 0.2); | |||
| } | |||
| @@ -0,0 +1,81 @@ | |||
| /* | |||
| Copyright (C) 2025 QuantumNous | |||
| This program is free software: you can redistribute it and/or modify | |||
| it under the terms of the GNU Affero General Public License as | |||
| published by the Free Software Foundation, either version 3 of the | |||
| License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Affero General Public License for more details. | |||
| You should have received a copy of the GNU Affero General Public License | |||
| along with this program. If not, see <https://www.gnu.org/licenses/>. | |||
| For commercial licensing, please contact support@quantumnous.com | |||
| */ | |||
| import React, { useContext } from 'react'; | |||
| import { Button, Typography } from '@douyinfe/semi-ui'; | |||
| import { IconPlay, IconFile } from '@douyinfe/semi-icons'; | |||
| import { Link } from 'react-router-dom'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { StatusContext } from '../../../context/Status'; | |||
| const { Title, Text } = Typography; | |||
| const CTASection = () => { | |||
| const { t } = useTranslation(); | |||
| const [statusState] = useContext(StatusContext); | |||
| const isDemoSiteMode = statusState?.status?.demo_site_enabled || false; | |||
| const docsLink = statusState?.status?.docs_link || ''; | |||
| return ( | |||
| <section className="home-snap-section bg-[var(--semi-color-bg-0)]"> | |||
| <div className="mx-auto w-full max-w-5xl px-5 py-16 md:px-6 lg:px-8"> | |||
| <div className="cta-section relative overflow-hidden rounded-3xl border border-[var(--semi-color-border)] bg-[var(--semi-color-bg-0)] px-6 py-12 text-center md:px-12"> | |||
| {/* 内容 */} | |||
| <div className="relative z-10 flex flex-col items-center gap-6"> | |||
| <Title heading={2} className="text-3xl font-semibold md:text-4xl"> | |||
| {t('将大模型能力真正落地到业务流程')} | |||
| </Title> | |||
| <p className="max-w-2xl text-base leading-relaxed text-semi-color-text-1"> | |||
| {t('立即接入,统一控制访问策略与调用成本,为产品带来稳定、可扩展的智能体验。')} | |||
| </p> | |||
| {/* CTA 按钮 */} | |||
| <div className="flex flex-wrap justify-center gap-4"> | |||
| <Link to="/console"> | |||
| <Button | |||
| type="primary" | |||
| theme="solid" | |||
| size="large" | |||
| className="!rounded-3xl px-8 py-2" | |||
| icon={<IconPlay />} | |||
| > | |||
| {t('立即开始')} | |||
| </Button> | |||
| </Link> | |||
| {docsLink && ( | |||
| <Button | |||
| type="primary" | |||
| theme="light" | |||
| size="large" | |||
| className="flex items-center !rounded-3xl px-6 py-2" | |||
| icon={<IconFile />} | |||
| onClick={() => window.open(docsLink, '_blank')} | |||
| > | |||
| {t('查看文档')} | |||
| </Button> | |||
| )} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </section> | |||
| ); | |||
| }; | |||
| export default CTASection; | |||
| @@ -0,0 +1,212 @@ | |||
| /* | |||
| Copyright (C) 2025 QuantumNous | |||
| This program is free software: you can redistribute it and/or modify | |||
| it under the terms of the GNU Affero General Public License as | |||
| published by the Free Software Foundation, either version 3 of the | |||
| License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Affero General Public License for more details. | |||
| You should have received a copy of the GNU Affero General Public License | |||
| along with this program. If not, see <https://www.gnu.org/licenses/>. | |||
| For commercial licensing, please contact support@quantumnous.com | |||
| */ | |||
| import React, { useState, useEffect, useContext } from 'react'; | |||
| import { Button, Input, ScrollList, ScrollItem } from '@douyinfe/semi-ui'; | |||
| import { IconPlay, IconFile, IconCopy, IconActivity, IconHistogram, IconCloudStroked } from '@douyinfe/semi-icons'; | |||
| import { Link } from 'react-router-dom'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { copy, showSuccess } from '../../../helpers'; | |||
| import { StatusContext } from '../../../context/Status'; | |||
| import { API_ENDPOINTS } from '../../../constants/common.constant'; | |||
| const HeroSection = () => { | |||
| const { t, i18n } = useTranslation(); | |||
| const [statusState] = useContext(StatusContext); | |||
| const isChinese = i18n.language.startsWith('zh'); | |||
| const serverAddress = statusState?.status?.server_address || `${window.location.origin}`; | |||
| const isDemoSiteMode = statusState?.status?.demo_site_enabled || false; | |||
| const endpointItems = API_ENDPOINTS.map((e) => ({ value: e })); | |||
| const [endpointIndex, setEndpointIndex] = useState(0); | |||
| const handleCopyBaseURL = async () => { | |||
| const ok = await copy(serverAddress); | |||
| if (ok) { | |||
| showSuccess(t('已复制到剪切板')); | |||
| } | |||
| }; | |||
| useEffect(() => { | |||
| const timer = setInterval(() => { | |||
| setEndpointIndex((prev) => (prev + 1) % endpointItems.length); | |||
| }, 3000); | |||
| return () => clearInterval(timer); | |||
| }, [endpointItems.length]); | |||
| const features = [ | |||
| { | |||
| icon: <IconActivity size={18} />, | |||
| title: t('实时调度'), | |||
| description: t('健康度与延迟权重动态切换,保证最优响应。'), | |||
| }, | |||
| { | |||
| icon: <IconHistogram size={18} />, | |||
| title: t('统一监控'), | |||
| description: t('调用、费用、异常一站式可视化,随时掌握运行状态。'), | |||
| }, | |||
| { | |||
| icon: <IconCloudStroked size={18} />, | |||
| title: t('智能限流'), | |||
| description: t('多维策略保障核心业务优先级,避免突发拥堵。'), | |||
| }, | |||
| ]; | |||
| const stats = [ | |||
| { value: '30+', label: t('可覆盖模型') }, | |||
| { value: '99.9%', label: t('SLA 可用性') }, | |||
| { value: '7', label: t('多区域节点') }, | |||
| ]; | |||
| return ( | |||
| <section className="home-snap-section home-snap-section-hero bg-[var(--semi-color-bg-0)]"> | |||
| {/* 背景光晕 */} | |||
| <div className="hero-blur-ball-1" /> | |||
| <div className="hero-blur-ball-2" /> | |||
| <div className="relative z-10 mx-auto w-full max-w-6xl px-5 pt-28 pb-16 md:px-6 md:pt-32 md:pb-20 lg:px-8 lg:pt-36 lg:pb-24"> | |||
| <div className="grid items-center gap-12 hero-grid lg:grid-cols-[1.1fr_0.9fr]"> | |||
| {/* 左侧内容 */} | |||
| <div className="flex flex-col gap-8"> | |||
| {/* 标签 */} | |||
| <span className="hero-tag self-start"> | |||
| {t('面向企业的 AI 生产力基座')} | |||
| </span> | |||
| {/* 大标题 */} | |||
| <div | |||
| className={`text-4xl font-bold text-semi-color-text-0 leading-tight md:text-5xl lg:text-6xl ${ | |||
| isChinese ? 'tracking-wide md:tracking-wider' : '' | |||
| }`} | |||
| > | |||
| {t('统一的大模型接口网关')} | |||
| <br /> | |||
| <span className="shine-text">{t('链接全球 AI 能力')}</span> | |||
| </div> | |||
| {/* 描述 */} | |||
| <p className="max-w-xl text-base leading-relaxed text-semi-color-text-1 md:text-lg"> | |||
| {t('以一套域名、密钥与风控策略连接全球大模型资源,保障可观测、可拓展、可控。')} | |||
| </p> | |||
| {/* API URL 输入框 */} | |||
| <div className="api-url-card"> | |||
| <span className="mb-3 block text-xs uppercase tracking-wide text-semi-color-text-2"> | |||
| {t('替换基础 URL 即可接入')} | |||
| </span> | |||
| <Input | |||
| readonly | |||
| value={serverAddress} | |||
| className="!rounded-2xl" | |||
| size="large" | |||
| suffix={ | |||
| <div className="flex items-center gap-2"> | |||
| <ScrollList | |||
| bodyHeight={32} | |||
| style={{ border: 'unset', boxShadow: 'unset' }} | |||
| > | |||
| <ScrollItem | |||
| mode="wheel" | |||
| cycled={true} | |||
| list={endpointItems} | |||
| selectedIndex={endpointIndex} | |||
| onSelect={({ index }) => setEndpointIndex(index)} | |||
| /> | |||
| </ScrollList> | |||
| <Button | |||
| type="primary" | |||
| onClick={handleCopyBaseURL} | |||
| icon={<IconCopy />} | |||
| className="!rounded-full" | |||
| size="small" | |||
| /> | |||
| </div> | |||
| } | |||
| /> | |||
| </div> | |||
| {/* CTA 按钮 */} | |||
| <div className="flex flex-wrap items-center gap-4"> | |||
| <Link to="/console"> | |||
| <Button | |||
| theme="solid" | |||
| type="primary" | |||
| size="large" | |||
| className="!rounded-3xl px-8 py-2" | |||
| icon={<IconPlay />} | |||
| > | |||
| {t('获取密钥')} | |||
| </Button> | |||
| </Link> | |||
| {isDemoSiteMode && statusState?.status?.version ? ( | |||
| <Button | |||
| size="large" | |||
| className="flex items-center !rounded-3xl px-6 py-2" | |||
| icon={<IconFile />} | |||
| onClick={() => | |||
| window.open( | |||
| 'https://github.com/QuantumNous/new-api', | |||
| '_blank' | |||
| ) | |||
| } | |||
| > | |||
| {statusState.status.version} | |||
| </Button> | |||
| ) : null} | |||
| </div> | |||
| {/* 统计数据 */} | |||
| <div className="flex flex-wrap gap-8 pt-2"> | |||
| {stats.map((stat, index) => ( | |||
| <div key={index} className="stat-card"> | |||
| <span className="stat-card-value">{stat.value}</span> | |||
| <span className="stat-card-label">{stat.label}</span> | |||
| </div> | |||
| ))} | |||
| </div> | |||
| </div> | |||
| {/* 右侧功能卡片 */} | |||
| <div className="relative hidden lg:block"> | |||
| <div className="feature-card-container"> | |||
| <div className="feature-card"> | |||
| {features.map((feature, index) => ( | |||
| <div key={index} className="feature-card-item"> | |||
| <div className="flex items-center gap-3"> | |||
| <div className="feature-card-icon">{feature.icon}</div> | |||
| <span className="text-sm font-semibold text-semi-color-text-0"> | |||
| {feature.title} | |||
| </span> | |||
| </div> | |||
| <span className="text-xs leading-relaxed text-semi-color-text-2"> | |||
| {feature.description} | |||
| </span> | |||
| </div> | |||
| ))} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </section> | |||
| ); | |||
| }; | |||
| export default HeroSection; | |||
| @@ -0,0 +1,83 @@ | |||
| /* | |||
| Copyright (C) 2025 QuantumNous | |||
| This program is free software: you can redistribute it and/or modify | |||
| it under the terms of the GNU Affero General Public License as | |||
| published by the Free Software Foundation, either version 3 of the | |||
| License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Affero General Public License for more details. | |||
| You should have received a copy of the GNU Affero General Public License | |||
| along with this program. If not, see <https://www.gnu.org/licenses/>. | |||
| For commercial licensing, please contact support@quantumnous.com | |||
| */ | |||
| import React, { useContext } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { StatusContext } from '../../../context/Status'; | |||
| const HomePageFooter = () => { | |||
| const { t } = useTranslation(); | |||
| const [statusState] = useContext(StatusContext); | |||
| const systemName = statusState?.status?.system_name || 'New API'; | |||
| const footerInfo = statusState?.status?.footer_info || ''; | |||
| const currentYear = new Date().getFullYear(); | |||
| const footerLinks = [ | |||
| { label: t('服务条款'), href: '/terms', external: false }, | |||
| { label: t('使用政策'), href: '/usage-policy', external: false }, | |||
| ]; | |||
| return ( | |||
| <footer className="home-footer"> | |||
| <div className="mx-auto w-full max-w-[1110px]"> | |||
| {/* 链接区域 */} | |||
| <div className="mb-6 flex flex-col items-center justify-center gap-3 md:flex-row md:gap-4"> | |||
| {footerLinks.filter(link => link.href).map((link, index) => ( | |||
| <React.Fragment key={link.label}> | |||
| <a | |||
| href={link.href} | |||
| target={link.external ? '_blank' : undefined} | |||
| rel={link.external ? 'noopener noreferrer' : undefined} | |||
| className="home-footer-link" | |||
| > | |||
| {link.label} | |||
| </a> | |||
| {index < footerLinks.filter(l => l.href).length - 1 && ( | |||
| <div className="hidden h-4 w-px bg-semi-color-border md:block" /> | |||
| )} | |||
| </React.Fragment> | |||
| ))} | |||
| </div> | |||
| {/* 分割线 */} | |||
| <div className="border-t border-semi-color-border pt-6"> | |||
| <div className="flex flex-col items-center justify-between gap-4 md:flex-row"> | |||
| {/* 版权信息 */} | |||
| <div className="flex flex-col items-center gap-3 md:flex-row"> | |||
| <span className="text-sm text-semi-color-text-1"> | |||
| © {currentYear} {systemName}. {t('版权所有')} | |||
| </span> | |||
| </div> | |||
| {/* 附加信息 */} | |||
| {footerInfo && ( | |||
| <div | |||
| className="text-center text-sm text-semi-color-text-2 md:text-right" | |||
| dangerouslySetInnerHTML={{ __html: footerInfo }} | |||
| /> | |||
| )} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </footer> | |||
| ); | |||
| }; | |||
| export default HomePageFooter; | |||
| @@ -0,0 +1,110 @@ | |||
| /* | |||
| Copyright (C) 2025 QuantumNous | |||
| This program is free software: you can redistribute it and/or modify | |||
| it under the terms of the GNU Affero General Public License as | |||
| published by the Free Software Foundation, either version 3 of the | |||
| License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Affero General Public License for more details. | |||
| You should have received a copy of the GNU Affero General Public License | |||
| along with this program. If not, see <https://www.gnu.org/licenses/>. | |||
| For commercial licensing, please contact support@quantumnous.com | |||
| */ | |||
| import React from 'react'; | |||
| import { Typography } from '@douyinfe/semi-ui'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { | |||
| Moonshot, | |||
| OpenAI, | |||
| XAI, | |||
| Zhipu, | |||
| Volcengine, | |||
| Cohere, | |||
| Claude, | |||
| Gemini, | |||
| Suno, | |||
| Minimax, | |||
| Wenxin, | |||
| Spark, | |||
| Qingyan, | |||
| DeepSeek, | |||
| Qwen, | |||
| Midjourney, | |||
| Grok, | |||
| AzureAI, | |||
| Hunyuan, | |||
| Xinference, | |||
| } from '@lobehub/icons'; | |||
| const { Title, Text } = Typography; | |||
| const PartnersSection = () => { | |||
| const { t } = useTranslation(); | |||
| const partners = [ | |||
| { icon: Moonshot, name: 'Moonshot AI', size: 40 }, | |||
| { icon: OpenAI, name: 'OpenAI', size: 40 }, | |||
| { icon: XAI, name: 'Grok', size: 40 }, | |||
| { icon: Zhipu, name: '智谱', size: 40 }, | |||
| { icon: Volcengine, name: '火山引擎', size: 40 }, | |||
| { icon: Cohere, name: 'Cohere', size: 40 }, | |||
| { icon: Claude, name: 'Claude', size: 40 }, | |||
| { icon: Gemini, name: 'Gemini', size: 40 }, | |||
| { icon: Suno, name: 'Suno', size: 40 }, | |||
| { icon: Minimax, name: 'Minimax', size: 40 }, | |||
| { icon: Wenxin, name: '文心', size: 40 }, | |||
| { icon: Spark, name: '讯飞星火', size: 40 }, | |||
| { icon: Qingyan, name: '腾讯混元', size: 40 }, | |||
| { icon: DeepSeek, name: 'DeepSeek', size: 40 }, | |||
| { icon: Qwen, name: '通义千问', size: 40 }, | |||
| { icon: Midjourney, name: 'Midjourney', size: 40 }, | |||
| { icon: Grok, name: 'Grok', size: 40 }, | |||
| { icon: AzureAI, name: 'Azure AI', size: 40 }, | |||
| { icon: Hunyuan, name: '腾讯混元', size: 40 }, | |||
| { icon: Xinference, name: 'Xinference', size: 40 }, | |||
| ]; | |||
| return ( | |||
| <section className="home-snap-section bg-[var(--semi-color-bg-0)]"> | |||
| <div className="mx-auto w-full max-w-6xl px-5 py-16 md:px-6 lg:px-8"> | |||
| {/* 标题区域 */} | |||
| <div className="text-center"> | |||
| <Text className="text-sm uppercase tracking-widest text-semi-color-text-2"> | |||
| {t('生态伙伴')} | |||
| </Text> | |||
| <Title heading={2} className="mt-3 text-3xl font-semibold md:text-4xl"> | |||
| {t('与主流模型供应商深度对接')} | |||
| </Title> | |||
| <p className="mt-4 text-base leading-relaxed text-semi-color-text-1"> | |||
| {t('保持统一协议,快速切换与扩展模型能力,随时接入最新生态。')} | |||
| </p> | |||
| </div> | |||
| {/* Logo 网格 */} | |||
| <div className="partner-logo-container mt-10"> | |||
| {partners.map((Partner, index) => { | |||
| const IconComponent = Partner.icon; | |||
| return ( | |||
| <div | |||
| key={index} | |||
| className={`partner-logo partner-logo-animated`} | |||
| title={Partner.name} | |||
| > | |||
| <IconComponent size={Partner.size} /> | |||
| </div> | |||
| ); | |||
| })} | |||
| </div> | |||
| </div> | |||
| </section> | |||
| ); | |||
| }; | |||
| export default PartnersSection; | |||
| @@ -0,0 +1,133 @@ | |||
| /* | |||
| Copyright (C) 2025 QuantumNous | |||
| This program is free software: you can redistribute it and/or modify | |||
| it under the terms of the GNU Affero General Public License as | |||
| published by the Free Software Foundation, either version 3 of the | |||
| License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Affero General Public License for more details. | |||
| You should have received a copy of the GNU Affero General Public License | |||
| along with this program. If not, see <https://www.gnu.org/licenses/>. | |||
| For commercial licensing, please contact support@quantumnous.com | |||
| */ | |||
| import React from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { IconChevronRight } from '@douyinfe/semi-icons'; | |||
| const ToolCard = ({ icon, brand, brandColor, title, description, link, glowClass }) => { | |||
| const brandColorClasses = { | |||
| amber: 'text-amber-400 hover-glow-amber', | |||
| blue: 'text-blue-400 hover-glow-blue', | |||
| purple: 'text-purple-400 hover-glow-purple', | |||
| }; | |||
| return ( | |||
| <div className="group cursor-pointer"> | |||
| <div | |||
| className={`rounded-2xl sm:rounded-[2rem] border bg-white border-black/5 p-8 sm:p-10 lg:p-12 shadow-sm transition-all duration-500 hover:shadow-2xl ${brandColorClasses[glowClass]}`} | |||
| > | |||
| {/* Logo */} | |||
| <div className="mb-6 sm:mb-8"> | |||
| <img | |||
| src={icon} | |||
| alt={title} | |||
| className="h-10 w-10 sm:h-12 sm:w-12 object-contain" | |||
| /> | |||
| </div> | |||
| {/* Brand Tag */} | |||
| <span | |||
| className={`mb-4 sm:mb-6 block text-[10px] sm:text-xs font-black uppercase tracking-[0.15em] ${brandColorClasses[brandColor].split(' ')[0]}`} | |||
| > | |||
| {brand} | |||
| </span> | |||
| {/* Title */} | |||
| <h3 className="mb-3 sm:mb-4 text-xl sm:text-2xl font-bold tracking-tight"> | |||
| {title} | |||
| </h3> | |||
| {/* Description */} | |||
| <p className="mb-6 sm:mb-8 text-sm sm:text-base font-light leading-relaxed opacity-40"> | |||
| {description} | |||
| </p> | |||
| {/* Link */} | |||
| <a | |||
| href={link} | |||
| target="_top" | |||
| className="flex items-center gap-2 text-[10px] font-bold opacity-0 transition-opacity group-hover:opacity-100" | |||
| > | |||
| LEARN CONFIG | |||
| <IconChevronRight size={14} /> | |||
| </a> | |||
| </div> | |||
| </div> | |||
| ); | |||
| }; | |||
| const ToolsSection = () => { | |||
| const { t } = useTranslation(); | |||
| const tools = [ | |||
| { | |||
| icon: '/images/Anthropic.png', | |||
| brand: 'Anthropic', | |||
| brandColor: 'amber', | |||
| title: 'Claude Code', | |||
| description: '代码执行能力强劲,高效理解需求,快速生成精准代码。', | |||
| link: '/pricing', | |||
| glowClass: 'amber', | |||
| }, | |||
| { | |||
| icon: '/images/openai.png', | |||
| brand: 'OpenAI', | |||
| brandColor: 'blue', | |||
| title: 'CodeX', | |||
| description: '深度思考模式,慢工出细活,复杂逻辑处理更严谨。', | |||
| link: '/pricing', | |||
| glowClass: 'blue', | |||
| }, | |||
| { | |||
| icon: '/images/gemini-ai.png', | |||
| brand: 'Google AI', | |||
| brandColor: 'purple', | |||
| title: 'Gemini CLI', | |||
| description: '前端能力顶尖,UI/UX 设计与实现一步到位,视觉效果出众。', | |||
| link: '/pricing', | |||
| glowClass: 'purple', | |||
| }, | |||
| ]; | |||
| return ( | |||
| <section className="home-snap-section bg-[var(--semi-color-bg-0)]"> | |||
| <div className="mx-auto max-w-[1400px] px-4 sm:px-6 lg:px-8"> | |||
| {/* Header */} | |||
| <div className="mb-16 flex flex-col items-start justify-between gap-4 sm:mb-20 sm:flex-row sm:items-end sm:gap-6 lg:mb-24"> | |||
| <h2 className="whitespace-pre-line text-[clamp(1.75rem,5vw,3.5rem)] font-black tracking-[-0.03em]"> | |||
| {t('原生支持\n极致工具链')} | |||
| </h2> | |||
| <p className="max-w-xs text-xs font-light opacity-40 sm:text-sm"> | |||
| {t('深度优化 API 路由,确保在 CLI 环境下依然拥有流畅的流式交互体验。')} | |||
| </p> | |||
| </div> | |||
| {/* Cards Grid */} | |||
| <div className="grid grid-cols-1 gap-4 sm:gap-6 md:grid-cols-3"> | |||
| {tools.map((tool, index) => ( | |||
| <ToolCard key={index} {...tool} /> | |||
| ))} | |||
| </div> | |||
| </div> | |||
| </section> | |||
| ); | |||
| }; | |||
| export default ToolsSection; | |||
| @@ -0,0 +1,99 @@ | |||
| /* | |||
| Copyright (C) 2025 QuantumNous | |||
| This program is free software: you can redistribute it and/or modify | |||
| it under the terms of the GNU Affero General Public License as | |||
| published by the Free Software Foundation, either version 3 of the | |||
| License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Affero General Public License for more details. | |||
| You should have received a copy of the GNU Affero General Public License | |||
| along with this program. If not, see <https://www.gnu.org/licenses/>. | |||
| For commercial licensing, please contact support@quantumnous.com | |||
| */ | |||
| import React from 'react'; | |||
| import { Typography } from '@douyinfe/semi-ui'; | |||
| import { | |||
| IconActivity, | |||
| IconHistogram, | |||
| IconCloudStroked, | |||
| IconLayers, | |||
| } from '@douyinfe/semi-icons'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| const { Title, Text } = Typography; | |||
| const ValueSection = () => { | |||
| const { t } = useTranslation(); | |||
| const values = [ | |||
| { | |||
| icon: <IconActivity style={{ fontSize: 20 }} />, | |||
| title: t('统一入口,极速连通'), | |||
| description: t('以单一域名和密钥连接全部大模型供应商,智能容灾切换确保业务不中断。'), | |||
| iconClass: '', | |||
| }, | |||
| { | |||
| icon: <IconHistogram style={{ fontSize: 20 }} />, | |||
| title: t('全栈可观测与风控'), | |||
| description: t('实时监控调用量、错误率与费用,一键配置限流、告警与安全策略。'), | |||
| iconClass: 'value-card-icon-blue', | |||
| }, | |||
| { | |||
| icon: <IconCloudStroked style={{ fontSize: 20 }} />, | |||
| title: t('按需扩容与成本优化'), | |||
| description: t('多渠道配额、智能路由与批量任务调度,灵活控制成本与并发能力。'), | |||
| iconClass: 'value-card-icon-teal', | |||
| }, | |||
| { | |||
| icon: <IconLayers style={{ fontSize: 20 }} />, | |||
| title: t('开发者友好体验'), | |||
| description: t('兼容 OpenAI 接口协议,提供 SDK、示例与 Web Playground,轻松迭代上线。'), | |||
| iconClass: 'value-card-icon-purple', | |||
| }, | |||
| ]; | |||
| return ( | |||
| <section className="home-snap-section bg-[var(--semi-color-bg-0)]"> | |||
| <div className="mx-auto w-full max-w-6xl px-5 py-16 md:px-6 lg:px-8"> | |||
| {/* 标题区域 */} | |||
| <div> | |||
| <Text className="text-sm uppercase tracking-widest text-semi-color-text-2"> | |||
| {t('核心价值')} | |||
| </Text> | |||
| <Title heading={2} className="mt-3 text-3xl font-semibold md:text-4xl"> | |||
| {t('让团队稳定使用大模型,更快落地 AI 创新')} | |||
| </Title> | |||
| <p className="mt-4 max-w-2xl text-base leading-relaxed text-semi-color-text-1"> | |||
| {t('从访问控制、成本可视化到全局调度,为企业提供端到端的 AI 基础设施能力。')} | |||
| </p> | |||
| </div> | |||
| {/* 卡片网格 */} | |||
| <div className="mt-10 grid gap-6 md:grid-cols-2"> | |||
| {values.map((value, index) => ( | |||
| <div key={index} className="value-card"> | |||
| <div className={`value-card-icon ${value.iconClass}`}> | |||
| {value.icon} | |||
| </div> | |||
| <Title heading={3} className="text-xl font-semibold"> | |||
| {value.title} | |||
| </Title> | |||
| <p className="text-sm leading-relaxed text-semi-color-text-1"> | |||
| {value.description} | |||
| </p> | |||
| </div> | |||
| ))} | |||
| </div> | |||
| </div> | |||
| </section> | |||
| ); | |||
| }; | |||
| export default ValueSection; | |||
| @@ -0,0 +1,97 @@ | |||
| /* | |||
| Copyright (C) 2025 QuantumNous | |||
| This program is free software: you can redistribute it and/or modify | |||
| it under the terms of the GNU Affero General Public License as | |||
| published by the Free Software Foundation, either version 3 of the | |||
| License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Affero General Public License for more details. | |||
| You should have received a copy of the GNU Affero General Public License | |||
| along with this program. If not, see <https://www.gnu.org/licenses/>. | |||
| For commercial licensing, please contact support@quantumnous.com | |||
| */ | |||
| import React from 'react'; | |||
| import { Typography } from '@douyinfe/semi-ui'; | |||
| import { | |||
| IconLayers, | |||
| IconActivity, | |||
| IconHistogram, | |||
| } from '@douyinfe/semi-icons'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| const { Title, Text } = Typography; | |||
| const WorkflowSection = () => { | |||
| const { t } = useTranslation(); | |||
| const steps = [ | |||
| { | |||
| number: '01', | |||
| icon: <IconLayers style={{ fontSize: 20 }} />, | |||
| title: t('接入配置'), | |||
| description: t('在控制台创建渠道、设置密钥与限额,导入模型列表。'), | |||
| }, | |||
| { | |||
| number: '02', | |||
| icon: <IconActivity style={{ fontSize: 20 }} />, | |||
| title: t('智能调度'), | |||
| description: t('根据健康度、延迟与价格自动选择最优模型通道,内置故障切换。'), | |||
| }, | |||
| { | |||
| number: '03', | |||
| icon: <IconHistogram style={{ fontSize: 20 }} />, | |||
| title: t('持续洞察'), | |||
| description: t('通过仪表盘追踪调用趋势、消耗与失败率,实时告警确保 SLO。'), | |||
| }, | |||
| ]; | |||
| return ( | |||
| <section className="home-snap-section bg-[var(--semi-color-bg-0)]"> | |||
| <div className="mx-auto w-full max-w-6xl px-5 py-16 md:px-6 lg:px-8"> | |||
| {/* 标题区域 */} | |||
| <div> | |||
| <Text className="text-sm uppercase tracking-widest text-semi-color-text-2"> | |||
| {t('工作流')} | |||
| </Text> | |||
| <Title heading={2} className="mt-3 text-3xl font-semibold md:text-4xl"> | |||
| {t('用 3 个步骤构建你的 AI 控制平面')} | |||
| </Title> | |||
| </div> | |||
| {/* 步骤卡片 */} | |||
| <div className="mt-10 grid gap-6 md:grid-cols-3"> | |||
| {steps.map((step, index) => ( | |||
| <div key={index} className="workflow-card"> | |||
| {/* 图标 */} | |||
| <div className="flex h-12 w-12 items-center justify-center rounded-full bg-[rgba(99,102,241,0.12)] text-semi-color-primary transition-transform duration-300"> | |||
| {step.icon} | |||
| </div> | |||
| {/* 步骤编号 */} | |||
| <span className="workflow-step-number">{step.number}</span> | |||
| {/* 标题 */} | |||
| <Title heading={3} className="text-lg font-semibold"> | |||
| {step.title} | |||
| </Title> | |||
| {/* 描述 */} | |||
| <p className="text-sm leading-relaxed text-semi-color-text-1"> | |||
| {step.description} | |||
| </p> | |||
| </div> | |||
| ))} | |||
| </div> | |||
| </div> | |||
| </section> | |||
| ); | |||
| }; | |||
| export default WorkflowSection; | |||
| @@ -18,69 +18,34 @@ For commercial licensing, please contact support@quantumnous.com | |||
| */ | |||
| import React, { useContext, useEffect, useState } from 'react'; | |||
| import { | |||
| Button, | |||
| Typography, | |||
| Input, | |||
| ScrollList, | |||
| ScrollItem, | |||
| } from '@douyinfe/semi-ui'; | |||
| import { API, showError, copy, showSuccess } from '../../helpers'; | |||
| import { useIsMobile } from '../../hooks/common/useIsMobile'; | |||
| import { API_ENDPOINTS } from '../../constants/common.constant'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { marked } from 'marked'; | |||
| import { StatusContext } from '../../context/Status'; | |||
| import { useActualTheme } from '../../context/Theme'; | |||
| import { marked } from 'marked'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { | |||
| IconGithubLogo, | |||
| IconPlay, | |||
| IconFile, | |||
| IconCopy, | |||
| } from '@douyinfe/semi-icons'; | |||
| import { Link } from 'react-router-dom'; | |||
| import { API, showError } from '../../helpers'; | |||
| import NoticeModal from '../../components/layout/NoticeModal'; | |||
| import HomePricingFilters from './HomePricingFilters'; | |||
| import { | |||
| Moonshot, | |||
| OpenAI, | |||
| XAI, | |||
| Zhipu, | |||
| Volcengine, | |||
| Cohere, | |||
| Claude, | |||
| Gemini, | |||
| Suno, | |||
| Minimax, | |||
| Wenxin, | |||
| Spark, | |||
| Qingyan, | |||
| DeepSeek, | |||
| Qwen, | |||
| Midjourney, | |||
| Grok, | |||
| AzureAI, | |||
| Hunyuan, | |||
| Xinference, | |||
| } from '@lobehub/icons'; | |||
| import { useIsMobile } from '../../hooks/common/useIsMobile'; | |||
| // 首页组件 | |||
| import HeroSection from './components/HeroSection'; | |||
| import ValueSection from './components/ValueSection'; | |||
| import WorkflowSection from './components/WorkflowSection'; | |||
| import ToolsSection from './components/ToolsSection'; | |||
| import PartnersSection from './components/PartnersSection'; | |||
| import CTASection from './components/CTASection'; | |||
| import HomePageFooter from './components/HomePageFooter'; | |||
| const { Text } = Typography; | |||
| // 模型广场 | |||
| import HomePricingFilters from './HomePricingFilters'; | |||
| const Home = () => { | |||
| const { t, i18n } = useTranslation(); | |||
| const { i18n } = useTranslation(); | |||
| const [statusState] = useContext(StatusContext); | |||
| const actualTheme = useActualTheme(); | |||
| const [homePageContentLoaded, setHomePageContentLoaded] = useState(false); | |||
| const [homePageContent, setHomePageContent] = useState(''); | |||
| const [noticeVisible, setNoticeVisible] = useState(false); | |||
| const isMobile = useIsMobile(); | |||
| const isDemoSiteMode = statusState?.status?.demo_site_enabled || false; | |||
| const docsLink = statusState?.status?.docs_link || ''; | |||
| const serverAddress = | |||
| statusState?.status?.server_address || `${window.location.origin}`; | |||
| const endpointItems = API_ENDPOINTS.map((e) => ({ value: e })); | |||
| const [endpointIndex, setEndpointIndex] = useState(0); | |||
| const isChinese = i18n.language.startsWith('zh'); | |||
| const displayHomePageContent = async () => { | |||
| setHomePageContent(localStorage.getItem('home_page_content') || ''); | |||
| @@ -111,13 +76,6 @@ const Home = () => { | |||
| setHomePageContentLoaded(true); | |||
| }; | |||
| const handleCopyBaseURL = async () => { | |||
| const ok = await copy(serverAddress); | |||
| if (ok) { | |||
| showSuccess(t('已复制到剪切板')); | |||
| } | |||
| }; | |||
| useEffect(() => { | |||
| const checkNoticeAndShow = async () => { | |||
| const lastCloseDate = localStorage.getItem('notice_close_date'); | |||
| @@ -142,140 +100,61 @@ const Home = () => { | |||
| displayHomePageContent().then(); | |||
| }, []); | |||
| useEffect(() => { | |||
| const timer = setInterval(() => { | |||
| setEndpointIndex((prev) => (prev + 1) % endpointItems.length); | |||
| }, 3000); | |||
| return () => clearInterval(timer); | |||
| }, [endpointItems.length]); | |||
| // 如果有自定义首页内容,显示自定义内容 | |||
| if (homePageContentLoaded && homePageContent !== '') { | |||
| return ( | |||
| <div className="w-full overflow-x-hidden"> | |||
| {homePageContent.startsWith('https://') ? ( | |||
| <iframe | |||
| src={homePageContent} | |||
| className="h-screen w-full border-none" | |||
| /> | |||
| ) : ( | |||
| <div | |||
| className="mt-[60px]" | |||
| dangerouslySetInnerHTML={{ __html: homePageContent }} | |||
| /> | |||
| )} | |||
| </div> | |||
| ); | |||
| } | |||
| // 默认首页布局 | |||
| return ( | |||
| <div className='w-full overflow-x-hidden'> | |||
| <div className="home-snap-container"> | |||
| <NoticeModal | |||
| visible={noticeVisible} | |||
| onClose={() => setNoticeVisible(false)} | |||
| isMobile={isMobile} | |||
| /> | |||
| {homePageContentLoaded && homePageContent === '' ? ( | |||
| <div className='w-full overflow-x-hidden'> | |||
| {/* Banner 部分 */} | |||
| <div className='w-full border-b border-semi-color-border min-h-[500px] md:min-h-[600px] lg:min-h-[700px] relative overflow-x-hidden'> | |||
| {/* 背景模糊晕染球 */} | |||
| <div className='blur-ball blur-ball-indigo' /> | |||
| <div className='blur-ball blur-ball-teal' /> | |||
| <div className='flex flex-col items-center w-full px-4 py-20 md:py-24 lg:py-32 mt-10'> | |||
| {/* 居中内容区 */} | |||
| <div className='flex flex-col items-center justify-center text-center max-w-4xl mx-auto w-full'> | |||
| <div className='flex flex-col items-center justify-center mb-6 md:mb-8'> | |||
| <h1 | |||
| className={`text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold text-semi-color-text-0 leading-tight ${isChinese ? 'tracking-wide md:tracking-wider' : ''}`} | |||
| > | |||
| <> | |||
| {t('统一的')} | |||
| <br /> | |||
| <span className='shine-text'>{t('大模型接口网关')}</span> | |||
| </> | |||
| </h1> | |||
| <p className='text-base md:text-lg lg:text-xl text-semi-color-text-1 mt-4 md:mt-6 max-w-xl'> | |||
| {t('更好的价格,更好的稳定性,只需要将模型基址替换为:')} | |||
| </p> | |||
| {/* BASE URL 与端点选择 */} | |||
| <div className='flex flex-col md:flex-row items-center justify-center gap-4 w-full mt-4 md:mt-6 max-w-md'> | |||
| <Input | |||
| readonly | |||
| value={serverAddress} | |||
| className='flex-1 !rounded-full' | |||
| size={isMobile ? 'default' : 'large'} | |||
| suffix={ | |||
| <div className='flex items-center gap-2'> | |||
| <ScrollList | |||
| bodyHeight={32} | |||
| style={{ border: 'unset', boxShadow: 'unset' }} | |||
| > | |||
| <ScrollItem | |||
| mode='wheel' | |||
| cycled={true} | |||
| list={endpointItems} | |||
| selectedIndex={endpointIndex} | |||
| onSelect={({ index }) => setEndpointIndex(index)} | |||
| /> | |||
| </ScrollList> | |||
| <Button | |||
| type='primary' | |||
| onClick={handleCopyBaseURL} | |||
| icon={<IconCopy />} | |||
| className='!rounded-full' | |||
| /> | |||
| </div> | |||
| } | |||
| /> | |||
| </div> | |||
| </div> | |||
| {/* 操作按钮 */} | |||
| <div className='flex flex-row gap-4 justify-center items-center'> | |||
| <Link to='/console'> | |||
| <Button | |||
| theme='solid' | |||
| type='primary' | |||
| size={isMobile ? 'default' : 'large'} | |||
| className='!rounded-3xl px-8 py-2' | |||
| icon={<IconPlay />} | |||
| > | |||
| {t('获取密钥')} | |||
| </Button> | |||
| </Link> | |||
| {isDemoSiteMode && statusState?.status?.version ? ( | |||
| <Button | |||
| size={isMobile ? 'default' : 'large'} | |||
| className='flex items-center !rounded-3xl px-6 py-2' | |||
| icon={<IconGithubLogo />} | |||
| onClick={() => | |||
| window.open( | |||
| 'https://github.com/QuantumNous/new-api', | |||
| '_blank', | |||
| ) | |||
| } | |||
| > | |||
| {statusState.status.version} | |||
| </Button> | |||
| ) : ( | |||
| docsLink && ( | |||
| <Button | |||
| size={isMobile ? 'default' : 'large'} | |||
| className='flex items-center !rounded-3xl px-6 py-2' | |||
| icon={<IconFile />} | |||
| onClick={() => window.open(docsLink, '_blank')} | |||
| > | |||
| {t('文档')} | |||
| </Button> | |||
| ) | |||
| )} | |||
| </div> | |||
| {/* Hero Section */} | |||
| <HeroSection /> | |||
| </div> | |||
| {/* 模型广场筛选项与列表 */} | |||
| <div className='w-full max-w-6xl mt-8 px-4'> | |||
| <HomePricingFilters t={t} /> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| ) : ( | |||
| <div className='overflow-x-hidden w-full'> | |||
| {homePageContent.startsWith('https://') ? ( | |||
| <iframe | |||
| src={homePageContent} | |||
| className='w-full h-screen border-none' | |||
| /> | |||
| ) : ( | |||
| <div | |||
| className='mt-[60px]' | |||
| dangerouslySetInnerHTML={{ __html: homePageContent }} | |||
| /> | |||
| )} | |||
| {/* 工具链 Section */} | |||
| <ToolsSection /> | |||
| {/* 核心价值 Section */} | |||
| <ValueSection /> | |||
| {/* 工作流 Section */} | |||
| <WorkflowSection /> | |||
| {/* 生态伙伴 Section */} | |||
| <PartnersSection /> | |||
| {/* 模型广场 Section */} | |||
| <section className="home-snap-section home-snap-section-pricing bg-[var(--semi-color-bg-0)]"> | |||
| <div className="mx-auto w-full max-w-6xl px-5 py-16 md:px-6 lg:px-8"> | |||
| <HomePricingFilters t={(key) => key} /> | |||
| </div> | |||
| )} | |||
| </section> | |||
| {/* CTA Section */} | |||
| <CTASection /> | |||
| {/* Footer */} | |||
| <HomePageFooter /> | |||
| </div> | |||
| ); | |||
| }; | |||