diff --git a/homepage-full b/homepage-full new file mode 100644 index 0000000..0f709c6 Binary files /dev/null and b/homepage-full differ diff --git a/model/channel_pricing.go b/model/channel_pricing.go index a7004f8..ddc764f 100644 --- a/model/channel_pricing.go +++ b/model/channel_pricing.go @@ -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 } diff --git a/model/test b/model/test new file mode 100644 index 0000000..e69de29 diff --git a/web/public/images/Anthropic.png b/web/public/images/Anthropic.png new file mode 100644 index 0000000..9ad5005 Binary files /dev/null and b/web/public/images/Anthropic.png differ diff --git a/web/public/images/gemini-ai.png b/web/public/images/gemini-ai.png new file mode 100644 index 0000000..bd1330f Binary files /dev/null and b/web/public/images/gemini-ai.png differ diff --git a/web/public/images/openai.png b/web/public/images/openai.png new file mode 100644 index 0000000..b7e934e Binary files /dev/null and b/web/public/images/openai.png differ diff --git a/web/src/components/layout/SiderBar.jsx b/web/src/components/layout/SiderBar.jsx index 34971c3..f38acd1 100644 --- a/web/src/components/layout/SiderBar.jsx +++ b/web/src/components/layout/SiderBar.jsx @@ -207,11 +207,11 @@ const SiderBar = ({ onNavigate = () => {} }) => { itemKey: 'playground', to: '/playground', }, - { - text: t('聊天'), - itemKey: 'chat', - items: chatItems, - }, + // { + // text: t('聊天'), + // itemKey: 'chat', + // items: chatItems, + // }, ]; // 根据配置过滤项目 diff --git a/web/src/components/layout/headerbar/index.jsx b/web/src/components/layout/headerbar/index.jsx index 81b51d7..33ac59f 100644 --- a/web/src/components/layout/headerbar/index.jsx +++ b/web/src/components/layout/headerbar/index.jsx @@ -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 (
diff --git a/web/src/components/settings/PersonalSetting.jsx b/web/src/components/settings/PersonalSetting.jsx index 8ee6415..c82c15d 100644 --- a/web/src/components/settings/PersonalSetting.jsx +++ b/web/src/components/settings/PersonalSetting.jsx @@ -463,9 +463,9 @@ const PersonalSetting = () => { {/* 账户管理和其他设置 */}
- {/* 左侧:账户管理设置 */} + {/* 左侧:账户管理设置 - 已注释 */}
- { passkeyDeleteLoading={passkeyDeleteLoading} onPasskeyRegister={handleRegisterPasskey} onPasskeyDelete={handleRemovePasskey} - /> + /> */} {/* 偏好设置(语言等) */} diff --git a/web/src/components/settings/personal/cards/NotificationSettings.jsx b/web/src/components/settings/personal/cards/NotificationSettings.jsx index e57e39d..b3b3dbf 100644 --- a/web/src/components/settings/personal/cards/NotificationSettings.jsx +++ b/web/src/components/settings/personal/cards/NotificationSettings.jsx @@ -731,8 +731,8 @@ const NotificationSettings = ({
- {/* 价格设置 Tab */} - @@ -755,10 +755,10 @@ const NotificationSettings = ({ )} />
- + */} - {/* 隐私设置 Tab */} - @@ -779,7 +779,7 @@ const NotificationSettings = ({ )} /> - + */} {/* 左侧边栏设置 Tab - 根据后端权限控制显示 */} {hasSidebarSettingsPermission() && ( diff --git a/web/src/components/table/model-pricing/modal/components/ChannelPricingCard.jsx b/web/src/components/table/model-pricing/modal/components/ChannelPricingCard.jsx index 3d50060..e643517 100644 --- a/web/src/components/table/model-pricing/modal/components/ChannelPricingCard.jsx +++ b/web/src/components/table/model-pricing/modal/components/ChannelPricingCard.jsx @@ -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) => ( -
+
{text} - - {getChannelTypeName(record.channelType)} - + {record.channelTags.map((tag, idx) => ( + + {tag.name} + + ))} {record.hasCustomPricing && ( diff --git a/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx b/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx index 266bbf9..b2442a0 100644 --- a/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx +++ b/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx @@ -181,37 +181,39 @@ const ModelPricingTable = ({ ); }; - return ( - -
- - - -
- {t('分组价格')} -
- {t('不同用户分组的价格信息')} -
-
-
- {autoChain.length > 0 && ( -
- {t('auto分组调用链路')} - - {autoChain.map((g, idx) => ( - - - {g} - {t('分组')} - - {idx < autoChain.length - 1 && } - - ))} -
- )} - {renderGroupPriceTable()} -
- ); + // 分组价格已隐藏 + // return ( + // + //
+ // + // + // + //
+ // {t('分组价格')} + //
+ // {t('不同用户分组的价格信息')} + //
+ //
+ //
+ // {autoChain.length > 0 && ( + //
+ // {t('auto分组调用链路')} + // + // {autoChain.map((g, idx) => ( + // + // + // {g} + // {t('分组')} + // + // {idx < autoChain.length - 1 && } + // + // ))} + //
+ // )} + // {renderGroupPriceTable()} + //
+ // ); + return null; }; export default ModelPricingTable; diff --git a/web/src/components/table/model-pricing/view/card/PricingCardView.jsx b/web/src/components/table/model-pricing/view/card/PricingCardView.jsx index 849ea97..98a4b58 100644 --- a/web/src/components/table/model-pricing/view/card/PricingCardView.jsx +++ b/web/src/components/table/model-pricing/view/card/PricingCardView.jsx @@ -333,7 +333,7 @@ const PricingCardView = ({ />
-
+
{t('模型')}:{' '} {model.quota_type === 0 ? model.model_ratio : t('无')} @@ -344,9 +344,11 @@ const PricingCardView = ({ ? parseFloat(model.completion_ratio.toFixed(3)) : t('无')}
+ {/* 分组定价已隐藏
{t('分组')}: {priceData?.usedGroupRatio ?? '-'}
+ */}
)} diff --git a/web/src/hooks/common/useNavigation.js b/web/src/hooks/common/useNavigation.js index f7e61a2..65d8d9f 100644 --- a/web/src/hooks/common/useNavigation.js +++ b/web/src/hooks/common/useNavigation.js @@ -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, diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index bf89055..d2b4325 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -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" } } diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index a52fb69..803674c 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -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。", + "生态伙伴": "生态伙伴", + "与主流模型供应商深度对接": "与主流模型供应商深度对接", + "保持统一协议,快速切换与扩展模型能力,随时接入最新生态。": "保持统一协议,快速切换与扩展模型能力,随时接入最新生态。", + "将大模型能力真正落地到业务流程": "将大模型能力真正落地到业务流程", + "立即接入,统一控制访问策略与调用成本,为产品带来稳定、可扩展的智能体验。": "立即接入,统一控制访问策略与调用成本,为产品带来稳定、可扩展的智能体验。", + "立即开始": "立即开始", + "查看文档": "查看文档", + "文档": "文档", + "关于": "关于", + "服务条款": "服务条款", + "使用政策": "使用政策", + "版权所有": "版权所有", + "实时调度": "实时调度", + "健康度与延迟权重动态切换,保证最优响应。": "健康度与延迟权重动态切换,保证最优响应。", + "统一监控": "统一监控", + "调用、费用、异常一站式可视化,随时掌握运行状态。": "调用、费用、异常一站式可视化,随时掌握运行状态。", + "智能限流": "智能限流", + "多维策略保障核心业务优先级,避免突发拥堵。": "多维策略保障核心业务优先级,避免突发拥堵。" } } diff --git a/web/src/index.css b/web/src/index.css index 265f0b8..1f645b3 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -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); +} diff --git a/web/src/pages/Home/components/CTASection.jsx b/web/src/pages/Home/components/CTASection.jsx new file mode 100644 index 0000000..10a0720 --- /dev/null +++ b/web/src/pages/Home/components/CTASection.jsx @@ -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 . + +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 ( +
+
+
+ {/* 内容 */} +
+ + {t('将大模型能力真正落地到业务流程')} + +

+ {t('立即接入,统一控制访问策略与调用成本,为产品带来稳定、可扩展的智能体验。')} +

+ + {/* CTA 按钮 */} +
+ + + + {docsLink && ( + + )} +
+
+
+
+
+ ); +}; + +export default CTASection; diff --git a/web/src/pages/Home/components/HeroSection.jsx b/web/src/pages/Home/components/HeroSection.jsx new file mode 100644 index 0000000..194c156 --- /dev/null +++ b/web/src/pages/Home/components/HeroSection.jsx @@ -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 . + +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: , + title: t('实时调度'), + description: t('健康度与延迟权重动态切换,保证最优响应。'), + }, + { + icon: , + title: t('统一监控'), + description: t('调用、费用、异常一站式可视化,随时掌握运行状态。'), + }, + { + icon: , + title: t('智能限流'), + description: t('多维策略保障核心业务优先级,避免突发拥堵。'), + }, + ]; + + const stats = [ + { value: '30+', label: t('可覆盖模型') }, + { value: '99.9%', label: t('SLA 可用性') }, + { value: '7', label: t('多区域节点') }, + ]; + + return ( +
+ {/* 背景光晕 */} +
+
+ +
+
+ {/* 左侧内容 */} +
+ {/* 标签 */} + + {t('面向企业的 AI 生产力基座')} + + + {/* 大标题 */} +
+ {t('统一的大模型接口网关')} +
+ {t('链接全球 AI 能力')} +
+ + {/* 描述 */} +

+ {t('以一套域名、密钥与风控策略连接全球大模型资源,保障可观测、可拓展、可控。')} +

+ + {/* API URL 输入框 */} +
+ + {t('替换基础 URL 即可接入')} + + + + setEndpointIndex(index)} + /> + +
+ } + /> +
+ + {/* CTA 按钮 */} +
+ + + + {isDemoSiteMode && statusState?.status?.version ? ( + + ) : null} +
+ + {/* 统计数据 */} +
+ {stats.map((stat, index) => ( +
+ {stat.value} + {stat.label} +
+ ))} +
+
+ + {/* 右侧功能卡片 */} +
+
+
+ {features.map((feature, index) => ( +
+
+
{feature.icon}
+ + {feature.title} + +
+ + {feature.description} + +
+ ))} +
+
+
+
+
+
+ ); +}; + +export default HeroSection; diff --git a/web/src/pages/Home/components/HomePageFooter.jsx b/web/src/pages/Home/components/HomePageFooter.jsx new file mode 100644 index 0000000..ba5a5d7 --- /dev/null +++ b/web/src/pages/Home/components/HomePageFooter.jsx @@ -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 . + +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 ( +
+
+ {/* 链接区域 */} +
+ {footerLinks.filter(link => link.href).map((link, index) => ( + + + {link.label} + + {index < footerLinks.filter(l => l.href).length - 1 && ( +
+ )} + + ))} +
+ + {/* 分割线 */} +
+
+ {/* 版权信息 */} +
+ + © {currentYear} {systemName}. {t('版权所有')} + +
+ + {/* 附加信息 */} + {footerInfo && ( +
+ )} +
+
+
+
+ ); +}; + +export default HomePageFooter; diff --git a/web/src/pages/Home/components/PartnersSection.jsx b/web/src/pages/Home/components/PartnersSection.jsx new file mode 100644 index 0000000..c4fdd28 --- /dev/null +++ b/web/src/pages/Home/components/PartnersSection.jsx @@ -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 . + +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 ( +
+
+ {/* 标题区域 */} +
+ + {t('生态伙伴')} + + + {t('与主流模型供应商深度对接')} + +

+ {t('保持统一协议,快速切换与扩展模型能力,随时接入最新生态。')} +

+
+ + {/* Logo 网格 */} +
+ {partners.map((Partner, index) => { + const IconComponent = Partner.icon; + return ( +
+ +
+ ); + })} +
+
+
+ ); +}; + +export default PartnersSection; diff --git a/web/src/pages/Home/components/ToolsSection.jsx b/web/src/pages/Home/components/ToolsSection.jsx new file mode 100644 index 0000000..25059c6 --- /dev/null +++ b/web/src/pages/Home/components/ToolsSection.jsx @@ -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 . + +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 ( +
+
+ {/* Logo */} +
+ {title} +
+ + {/* Brand Tag */} + + {brand} + + + {/* Title */} +

+ {title} +

+ + {/* Description */} +

+ {description} +

+ + {/* Link */} + + LEARN CONFIG + + +
+
+ ); +}; + +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 ( +
+
+ {/* Header */} +
+

+ {t('原生支持\n极致工具链')} +

+

+ {t('深度优化 API 路由,确保在 CLI 环境下依然拥有流畅的流式交互体验。')} +

+
+ + {/* Cards Grid */} +
+ {tools.map((tool, index) => ( + + ))} +
+
+
+ ); +}; + +export default ToolsSection; diff --git a/web/src/pages/Home/components/ValueSection.jsx b/web/src/pages/Home/components/ValueSection.jsx new file mode 100644 index 0000000..b8d568c --- /dev/null +++ b/web/src/pages/Home/components/ValueSection.jsx @@ -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 . + +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: , + title: t('统一入口,极速连通'), + description: t('以单一域名和密钥连接全部大模型供应商,智能容灾切换确保业务不中断。'), + iconClass: '', + }, + { + icon: , + title: t('全栈可观测与风控'), + description: t('实时监控调用量、错误率与费用,一键配置限流、告警与安全策略。'), + iconClass: 'value-card-icon-blue', + }, + { + icon: , + title: t('按需扩容与成本优化'), + description: t('多渠道配额、智能路由与批量任务调度,灵活控制成本与并发能力。'), + iconClass: 'value-card-icon-teal', + }, + { + icon: , + title: t('开发者友好体验'), + description: t('兼容 OpenAI 接口协议,提供 SDK、示例与 Web Playground,轻松迭代上线。'), + iconClass: 'value-card-icon-purple', + }, + ]; + + return ( +
+
+ {/* 标题区域 */} +
+ + {t('核心价值')} + + + {t('让团队稳定使用大模型,更快落地 AI 创新')} + +

+ {t('从访问控制、成本可视化到全局调度,为企业提供端到端的 AI 基础设施能力。')} +

+
+ + {/* 卡片网格 */} +
+ {values.map((value, index) => ( +
+
+ {value.icon} +
+ + {value.title} + +

+ {value.description} +

+
+ ))} +
+
+
+ ); +}; + +export default ValueSection; diff --git a/web/src/pages/Home/components/WorkflowSection.jsx b/web/src/pages/Home/components/WorkflowSection.jsx new file mode 100644 index 0000000..811a808 --- /dev/null +++ b/web/src/pages/Home/components/WorkflowSection.jsx @@ -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 . + +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: , + title: t('接入配置'), + description: t('在控制台创建渠道、设置密钥与限额,导入模型列表。'), + }, + { + number: '02', + icon: , + title: t('智能调度'), + description: t('根据健康度、延迟与价格自动选择最优模型通道,内置故障切换。'), + }, + { + number: '03', + icon: , + title: t('持续洞察'), + description: t('通过仪表盘追踪调用趋势、消耗与失败率,实时告警确保 SLO。'), + }, + ]; + + return ( +
+
+ {/* 标题区域 */} +
+ + {t('工作流')} + + + {t('用 3 个步骤构建你的 AI 控制平面')} + +
+ + {/* 步骤卡片 */} +
+ {steps.map((step, index) => ( +
+ {/* 图标 */} +
+ {step.icon} +
+ + {/* 步骤编号 */} + {step.number} + + {/* 标题 */} + + {step.title} + + + {/* 描述 */} +

+ {step.description} +

+
+ ))} +
+
+
+ ); +}; + +export default WorkflowSection; diff --git a/web/src/pages/Home/index.jsx b/web/src/pages/Home/index.jsx index e1a238c..391ca77 100644 --- a/web/src/pages/Home/index.jsx +++ b/web/src/pages/Home/index.jsx @@ -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 ( +
+ {homePageContent.startsWith('https://') ? ( +