- 注释侧边栏"聊天"子菜单,保留"操练场" - 隐藏个人设置中的账户管理模块 - 隐藏其他设置中的价格设置和隐私设置Tab Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>feat/alipay-payment
| @@ -2,6 +2,8 @@ package model | |||||
| import ( | import ( | ||||
| "fmt" | "fmt" | ||||
| "strconv" | |||||
| "strings" | |||||
| "sync" | "sync" | ||||
| "time" | "time" | ||||
| @@ -209,15 +211,17 @@ func InvalidateChannelPricingCache() { | |||||
| // ChannelPricingWithChannel 带渠道信息的定价响应 | // ChannelPricingWithChannel 带渠道信息的定价响应 | ||||
| type ChannelPricingWithChannel struct { | 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 获取指定模型的渠道定价(带渠道信息) | // GetChannelPricingByModelWithChannelInfo 获取指定模型的渠道定价(带渠道信息) | ||||
| @@ -254,6 +258,7 @@ func GetChannelPricingByModelWithChannelInfo(modelName string) ([]*ChannelPricin | |||||
| COALESCE(channel_pricings.completion_ratio, ?) as completion_ratio, | COALESCE(channel_pricings.completion_ratio, ?) as completion_ratio, | ||||
| COALESCE(channel_pricings.model_price, ?) as model_price, | COALESCE(channel_pricings.model_price, ?) as model_price, | ||||
| channel_pricings.id as id, | channel_pricings.id as id, | ||||
| channel_pricings.tag_ids as tag_ids, | |||||
| (channel_pricings.id IS NOT NULL) as has_custom_pricing`, | (channel_pricings.id IS NOT NULL) as has_custom_pricing`, | ||||
| defaultQuotaType, globalModelRatio, globalCompletionRatio, globalModelPrice). | defaultQuotaType, globalModelRatio, globalCompletionRatio, globalModelPrice). | ||||
| Joins("LEFT JOIN channels ON abilities.channel_id = channels.id"). | 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.model = ?", modelName). | ||||
| Where("abilities.enabled = ?", true). | Where("abilities.enabled = ?", true). | ||||
| Where("channels.status = ?", 1). // 只显示启用的渠道 | 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 | 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', | itemKey: 'playground', | ||||
| to: '/playground', | to: '/playground', | ||||
| }, | }, | ||||
| { | |||||
| text: t('聊天'), | |||||
| itemKey: 'chat', | |||||
| items: chatItems, | |||||
| }, | |||||
| // { | |||||
| // text: t('聊天'), | |||||
| // itemKey: 'chat', | |||||
| // items: chatItems, | |||||
| // }, | |||||
| ]; | ]; | ||||
| // 根据配置过滤项目 | // 根据配置过滤项目 | ||||
| @@ -40,7 +40,6 @@ const HeaderBar = ({ onMobileMenuToggle, drawerOpen }) => { | |||||
| logo, | logo, | ||||
| isNewYear, | isNewYear, | ||||
| isSelfUseMode, | isSelfUseMode, | ||||
| docsLink, | |||||
| isDemoSiteMode, | isDemoSiteMode, | ||||
| isConsoleRoute, | isConsoleRoute, | ||||
| theme, | theme, | ||||
| @@ -62,7 +61,7 @@ const HeaderBar = ({ onMobileMenuToggle, drawerOpen }) => { | |||||
| getUnreadKeys, | getUnreadKeys, | ||||
| } = useNotifications(statusState); | } = useNotifications(statusState); | ||||
| const { mainNavLinks } = useNavigation(t, docsLink, headerNavModules); | |||||
| const { mainNavLinks } = useNavigation(t, headerNavModules); | |||||
| return ( | 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'> | <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='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'> | <div className='flex flex-col gap-4 md:gap-6'> | ||||
| <AccountManagement | |||||
| {/* <AccountManagement | |||||
| t={t} | t={t} | ||||
| userState={userState} | userState={userState} | ||||
| status={status} | status={status} | ||||
| @@ -482,7 +482,7 @@ const PersonalSetting = () => { | |||||
| passkeyDeleteLoading={passkeyDeleteLoading} | passkeyDeleteLoading={passkeyDeleteLoading} | ||||
| onPasskeyRegister={handleRegisterPasskey} | onPasskeyRegister={handleRegisterPasskey} | ||||
| onPasskeyDelete={handleRemovePasskey} | onPasskeyDelete={handleRemovePasskey} | ||||
| /> | |||||
| /> */} | |||||
| {/* 偏好设置(语言等) */} | {/* 偏好设置(语言等) */} | ||||
| <PreferencesSettings t={t} /> | <PreferencesSettings t={t} /> | ||||
| @@ -731,8 +731,8 @@ const NotificationSettings = ({ | |||||
| </div> | </div> | ||||
| </TabPane> | </TabPane> | ||||
| {/* 价格设置 Tab */} | |||||
| <TabPane | |||||
| {/* 价格设置 Tab - 已注释 */} | |||||
| {/* <TabPane | |||||
| tab={ | tab={ | ||||
| <div className='flex items-center'> | <div className='flex items-center'> | ||||
| <DollarSign size={16} className='mr-2' /> | <DollarSign size={16} className='mr-2' /> | ||||
| @@ -755,10 +755,10 @@ const NotificationSettings = ({ | |||||
| )} | )} | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| </TabPane> | |||||
| </TabPane> */} | |||||
| {/* 隐私设置 Tab */} | |||||
| <TabPane | |||||
| {/* 隐私设置 Tab - 已注释 */} | |||||
| {/* <TabPane | |||||
| tab={ | tab={ | ||||
| <div className='flex items-center'> | <div className='flex items-center'> | ||||
| <ShieldCheck size={16} className='mr-2' /> | <ShieldCheck size={16} className='mr-2' /> | ||||
| @@ -779,7 +779,7 @@ const NotificationSettings = ({ | |||||
| )} | )} | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| </TabPane> | |||||
| </TabPane> */} | |||||
| {/* 左侧边栏设置 Tab - 根据后端权限控制显示 */} | {/* 左侧边栏设置 Tab - 根据后端权限控制显示 */} | ||||
| {hasSidebarSettingsPermission() && ( | {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 { Card, Avatar, Typography, Table, Tag, Spin, Tooltip } from '@douyinfe/semi-ui'; | ||||
| import { IconServer, IconEditStroked } from '@douyinfe/semi-icons'; | import { IconServer, IconEditStroked } from '@douyinfe/semi-icons'; | ||||
| import { API } from '../../../../../helpers'; | import { API } from '../../../../../helpers'; | ||||
| import { CHANNEL_OPTIONS } from '../../../../../constants'; | |||||
| const { Text } = Typography; | 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 = ({ | const ChannelPricingCard = ({ | ||||
| modelName, | modelName, | ||||
| currency, | currency, | ||||
| @@ -117,7 +103,8 @@ const ChannelPricingCard = ({ | |||||
| // 准备表格数据 | // 准备表格数据 | ||||
| const tableData = channelPricingData.map((item, index) => ({ | const tableData = channelPricingData.map((item, index) => ({ | ||||
| key: item.channel_id || index, | key: item.channel_id || index, | ||||
| channelName: item.channel_name || `渠道 ${item.channel_id}`, | |||||
| channelName: `渠道${['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'][index] || ` ${index + 1}`}`, | |||||
| channelTags: item.tags || [], // 渠道定价的标签列表 | |||||
| channelType: item.channel_type, | channelType: item.channel_type, | ||||
| quotaType: item.quota_type, | quotaType: item.quota_type, | ||||
| modelRatio: item.model_ratio, | modelRatio: item.model_ratio, | ||||
| @@ -137,13 +124,21 @@ const ChannelPricingCard = ({ | |||||
| title: t('渠道'), | title: t('渠道'), | ||||
| dataIndex: 'channelName', | dataIndex: 'channelName', | ||||
| render: (text, record) => ( | 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'> | <Tag color='cyan' size='small' shape='circle'> | ||||
| {text} | {text} | ||||
| </Tag> | </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 && ( | {record.hasCustomPricing && ( | ||||
| <Tooltip content={t('已设置自定义定价')}> | <Tooltip content={t('已设置自定义定价')}> | ||||
| <IconEditStroked size='small' style={{ color: 'var(--semi-color-primary)' }} /> | <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; | export default ModelPricingTable; | ||||
| @@ -333,7 +333,7 @@ const PricingCardView = ({ | |||||
| /> | /> | ||||
| </Tooltip> | </Tooltip> | ||||
| </div> | </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> | <div> | ||||
| {t('模型')}:{' '} | {t('模型')}:{' '} | ||||
| {model.quota_type === 0 ? model.model_ratio : t('无')} | {model.quota_type === 0 ? model.model_ratio : t('无')} | ||||
| @@ -344,9 +344,11 @@ const PricingCardView = ({ | |||||
| ? parseFloat(model.completion_ratio.toFixed(3)) | ? parseFloat(model.completion_ratio.toFixed(3)) | ||||
| : t('无')} | : t('无')} | ||||
| </div> | </div> | ||||
| {/* 分组定价已隐藏 | |||||
| <div> | <div> | ||||
| {t('分组')}: {priceData?.usedGroupRatio ?? '-'} | {t('分组')}: {priceData?.usedGroupRatio ?? '-'} | ||||
| </div> | </div> | ||||
| */} | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| )} | )} | ||||
| @@ -19,15 +19,13 @@ For commercial licensing, please contact support@quantumnous.com | |||||
| import { useMemo } from 'react'; | import { useMemo } from 'react'; | ||||
| export const useNavigation = (t, docsLink, headerNavModules) => { | |||||
| export const useNavigation = (t, headerNavModules) => { | |||||
| const mainNavLinks = useMemo(() => { | const mainNavLinks = useMemo(() => { | ||||
| // 默认配置,如果没有传入配置则显示所有模块 | // 默认配置,如果没有传入配置则显示所有模块 | ||||
| const defaultModules = { | const defaultModules = { | ||||
| home: true, | home: true, | ||||
| console: true, | console: true, | ||||
| pricing: true, | pricing: true, | ||||
| docs: true, | |||||
| about: true, | |||||
| }; | }; | ||||
| // 使用传入的配置或默认配置 | // 使用传入的配置或默认配置 | ||||
| @@ -49,28 +47,10 @@ export const useNavigation = (t, docsLink, headerNavModules) => { | |||||
| itemKey: 'pricing', | itemKey: 'pricing', | ||||
| to: '/pricing', | to: '/pricing', | ||||
| }, | }, | ||||
| ...(docsLink | |||||
| ? [ | |||||
| { | |||||
| text: t('文档'), | |||||
| itemKey: 'docs', | |||||
| isExternal: true, | |||||
| externalLink: docsLink, | |||||
| }, | |||||
| ] | |||||
| : []), | |||||
| { | |||||
| text: t('关于'), | |||||
| itemKey: 'about', | |||||
| to: '/about', | |||||
| }, | |||||
| ]; | ]; | ||||
| // 根据配置过滤导航链接 | // 根据配置过滤导航链接 | ||||
| return allLinks.filter((link) => { | return allLinks.filter((link) => { | ||||
| if (link.itemKey === 'docs') { | |||||
| return docsLink && modules.docs; | |||||
| } | |||||
| if (link.itemKey === 'pricing') { | if (link.itemKey === 'pricing') { | ||||
| // 支持新的pricing配置格式 | // 支持新的pricing配置格式 | ||||
| return typeof modules.pricing === 'object' | return typeof modules.pricing === 'object' | ||||
| @@ -79,7 +59,7 @@ export const useNavigation = (t, docsLink, headerNavModules) => { | |||||
| } | } | ||||
| return modules[link.itemKey] === true; | return modules[link.itemKey] === true; | ||||
| }); | }); | ||||
| }, [t, docsLink, headerNavModules]); | |||||
| }, [t, headerNavModules]); | |||||
| return { | return { | ||||
| mainNavLinks, | mainNavLinks, | ||||
| @@ -2877,6 +2877,48 @@ | |||||
| "modelType.rerank": "Rerank", | "modelType.rerank": "Rerank", | ||||
| "modelType.vision": "Vision", | "modelType.vision": "Vision", | ||||
| "modelType.other": "Other", | "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" | "设计版本": "b80c3466cb6feafeb3990c7820e10e50" | ||||
| } | } | ||||
| } | } | ||||
| @@ -2854,6 +2854,48 @@ | |||||
| "modelType.embedding": "嵌入", | "modelType.embedding": "嵌入", | ||||
| "modelType.rerank": "重排序", | "modelType.rerank": "重排序", | ||||
| "modelType.vision": "视觉", | "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 { | .semi-datepicker-range-input { | ||||
| border-radius: 10px !important; | 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 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 { StatusContext } from '../../context/Status'; | ||||
| import { useActualTheme } from '../../context/Theme'; | 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 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 Home = () => { | ||||
| const { t, i18n } = useTranslation(); | |||||
| const { i18n } = useTranslation(); | |||||
| const [statusState] = useContext(StatusContext); | const [statusState] = useContext(StatusContext); | ||||
| const actualTheme = useActualTheme(); | const actualTheme = useActualTheme(); | ||||
| const [homePageContentLoaded, setHomePageContentLoaded] = useState(false); | const [homePageContentLoaded, setHomePageContentLoaded] = useState(false); | ||||
| const [homePageContent, setHomePageContent] = useState(''); | const [homePageContent, setHomePageContent] = useState(''); | ||||
| const [noticeVisible, setNoticeVisible] = useState(false); | const [noticeVisible, setNoticeVisible] = useState(false); | ||||
| const isMobile = useIsMobile(); | 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 () => { | const displayHomePageContent = async () => { | ||||
| setHomePageContent(localStorage.getItem('home_page_content') || ''); | setHomePageContent(localStorage.getItem('home_page_content') || ''); | ||||
| @@ -111,13 +76,6 @@ const Home = () => { | |||||
| setHomePageContentLoaded(true); | setHomePageContentLoaded(true); | ||||
| }; | }; | ||||
| const handleCopyBaseURL = async () => { | |||||
| const ok = await copy(serverAddress); | |||||
| if (ok) { | |||||
| showSuccess(t('已复制到剪切板')); | |||||
| } | |||||
| }; | |||||
| useEffect(() => { | useEffect(() => { | ||||
| const checkNoticeAndShow = async () => { | const checkNoticeAndShow = async () => { | ||||
| const lastCloseDate = localStorage.getItem('notice_close_date'); | const lastCloseDate = localStorage.getItem('notice_close_date'); | ||||
| @@ -142,140 +100,61 @@ const Home = () => { | |||||
| displayHomePageContent().then(); | 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 ( | return ( | ||||
| <div className='w-full overflow-x-hidden'> | |||||
| <div className="home-snap-container"> | |||||
| <NoticeModal | <NoticeModal | ||||
| visible={noticeVisible} | visible={noticeVisible} | ||||
| onClose={() => setNoticeVisible(false)} | onClose={() => setNoticeVisible(false)} | ||||
| isMobile={isMobile} | 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> | </div> | ||||
| )} | |||||
| </section> | |||||
| {/* CTA Section */} | |||||
| <CTASection /> | |||||
| {/* Footer */} | |||||
| <HomePageFooter /> | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| }; | }; | ||||