Procházet zdrojové kódy

chore: 隐藏侧边栏聊天子菜单、账户管理及隐私价格设置

- 注释侧边栏"聊天"子菜单,保留"操练场"
- 隐藏个人设置中的账户管理模块
- 隐藏其他设置中的价格设置和隐私设置Tab

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat/alipay-payment
fengsilin před 1 měsícem
rodič
revize
a95331a85f
25 změnil soubory, kde provedl 1622 přidání a 284 odebrání
  1. binární
      homepage-full
  2. +45
    -11
      model/channel_pricing.go
  3. +0
    -0
      model/test
  4. binární
      web/public/images/Anthropic.png
  5. binární
      web/public/images/gemini-ai.png
  6. binární
      web/public/images/openai.png
  7. +5
    -5
      web/src/components/layout/SiderBar.jsx
  8. +1
    -2
      web/src/components/layout/headerbar/index.jsx
  9. +3
    -3
      web/src/components/settings/PersonalSetting.jsx
  10. +6
    -6
      web/src/components/settings/personal/cards/NotificationSettings.jsx
  11. +14
    -19
      web/src/components/table/model-pricing/modal/components/ChannelPricingCard.jsx
  12. +33
    -31
      web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx
  13. +3
    -1
      web/src/components/table/model-pricing/view/card/PricingCardView.jsx
  14. +2
    -22
      web/src/hooks/common/useNavigation.js
  15. +42
    -0
      web/src/i18n/locales/en.json
  16. +43
    -1
      web/src/i18n/locales/zh-CN.json
  17. +548
    -0
      web/src/index.css
  18. +81
    -0
      web/src/pages/Home/components/CTASection.jsx
  19. +212
    -0
      web/src/pages/Home/components/HeroSection.jsx
  20. +83
    -0
      web/src/pages/Home/components/HomePageFooter.jsx
  21. +110
    -0
      web/src/pages/Home/components/PartnersSection.jsx
  22. +133
    -0
      web/src/pages/Home/components/ToolsSection.jsx
  23. +99
    -0
      web/src/pages/Home/components/ValueSection.jsx
  24. +97
    -0
      web/src/pages/Home/components/WorkflowSection.jsx
  25. +62
    -183
      web/src/pages/Home/index.jsx

binární
homepage-full Zobrazit soubor

Před Za
Šířka: 4148  |  Výška: 3148  |  Velikost: 396 KiB

+ 45
- 11
model/channel_pricing.go Zobrazit soubor

@@ -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
}

+ 0
- 0
model/test Zobrazit soubor


binární
web/public/images/Anthropic.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 7.0 KiB

binární
web/public/images/gemini-ai.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 3.5 KiB

binární
web/public/images/openai.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 7.1 KiB

+ 5
- 5
web/src/components/layout/SiderBar.jsx Zobrazit soubor

@@ -207,11 +207,11 @@ const SiderBar = ({ onNavigate = () => {} }) => {
itemKey: 'playground',
to: '/playground',
},
{
text: t('聊天'),
itemKey: 'chat',
items: chatItems,
},
// {
// text: t('聊天'),
// itemKey: 'chat',
// items: chatItems,
// },
];

// 根据配置过滤项目


+ 1
- 2
web/src/components/layout/headerbar/index.jsx Zobrazit soubor

@@ -40,7 +40,6 @@ const HeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
logo,
isNewYear,
isSelfUseMode,
docsLink,
isDemoSiteMode,
isConsoleRoute,
theme,
@@ -62,7 +61,7 @@ const HeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
getUnreadKeys,
} = useNotifications(statusState);

const { mainNavLinks } = useNavigation(t, docsLink, headerNavModules);
const { mainNavLinks } = useNavigation(t, headerNavModules);

return (
<header className='text-semi-color-text-0 sticky top-0 z-50 transition-colors duration-300 bg-white/75 dark:bg-zinc-900/75 backdrop-blur-lg'>


+ 3
- 3
web/src/components/settings/PersonalSetting.jsx Zobrazit soubor

@@ -463,9 +463,9 @@ const PersonalSetting = () => {

{/* 账户管理和其他设置 */}
<div className='grid grid-cols-1 xl:grid-cols-2 items-start gap-4 md:gap-6 mt-4 md:mt-6'>
{/* 左侧:账户管理设置 */}
{/* 左侧:账户管理设置 - 已注释 */}
<div className='flex flex-col gap-4 md:gap-6'>
<AccountManagement
{/* <AccountManagement
t={t}
userState={userState}
status={status}
@@ -482,7 +482,7 @@ const PersonalSetting = () => {
passkeyDeleteLoading={passkeyDeleteLoading}
onPasskeyRegister={handleRegisterPasskey}
onPasskeyDelete={handleRemovePasskey}
/>
/> */}

{/* 偏好设置(语言等) */}
<PreferencesSettings t={t} />


+ 6
- 6
web/src/components/settings/personal/cards/NotificationSettings.jsx Zobrazit soubor

@@ -731,8 +731,8 @@ const NotificationSettings = ({
</div>
</TabPane>

{/* 价格设置 Tab */}
<TabPane
{/* 价格设置 Tab - 已注释 */}
{/* <TabPane
tab={
<div className='flex items-center'>
<DollarSign size={16} className='mr-2' />
@@ -755,10 +755,10 @@ const NotificationSettings = ({
)}
/>
</div>
</TabPane>
</TabPane> */}

{/* 隐私设置 Tab */}
<TabPane
{/* 隐私设置 Tab - 已注释 */}
{/* <TabPane
tab={
<div className='flex items-center'>
<ShieldCheck size={16} className='mr-2' />
@@ -779,7 +779,7 @@ const NotificationSettings = ({
)}
/>
</div>
</TabPane>
</TabPane> */}

{/* 左侧边栏设置 Tab - 根据后端权限控制显示 */}
{hasSidebarSettingsPermission() && (


+ 14
- 19
web/src/components/table/model-pricing/modal/components/ChannelPricingCard.jsx Zobrazit soubor

@@ -21,23 +21,9 @@ import React, { useState, useEffect, useMemo } from 'react';
import { Card, Avatar, Typography, Table, Tag, Spin, Tooltip } from '@douyinfe/semi-ui';
import { IconServer, IconEditStroked } from '@douyinfe/semi-icons';
import { API } from '../../../../../helpers';
import { CHANNEL_OPTIONS } from '../../../../../constants';

const { Text } = Typography;

// 渠道类型映射表 - 移到组件外部避免重复计算
const channelTypeMap = (() => {
const map = {};
CHANNEL_OPTIONS.forEach((opt) => {
map[opt.value] = opt.label;
});
return map;
})();

const getChannelTypeName = (type) => {
return channelTypeMap[type] || `类型 ${type}`;
};

const ChannelPricingCard = ({
modelName,
currency,
@@ -117,7 +103,8 @@ const ChannelPricingCard = ({
// 准备表格数据
const tableData = channelPricingData.map((item, index) => ({
key: item.channel_id || index,
channelName: item.channel_name || `渠道 ${item.channel_id}`,
channelName: `渠道${['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'][index] || ` ${index + 1}`}`,
channelTags: item.tags || [], // 渠道定价的标签列表
channelType: item.channel_type,
quotaType: item.quota_type,
modelRatio: item.model_ratio,
@@ -137,13 +124,21 @@ const ChannelPricingCard = ({
title: t('渠道'),
dataIndex: 'channelName',
render: (text, record) => (
<div className='flex items-center gap-2'>
<div className='flex items-center gap-2 flex-wrap'>
<Tag color='cyan' size='small' shape='circle'>
{text}
</Tag>
<span className='text-xs text-gray-500'>
{getChannelTypeName(record.channelType)}
</span>
{record.channelTags.map((tag, idx) => (
<Tag
key={idx}
color={tag.color || 'default'}
size='small'
shape='circle'
style={{ backgroundColor: tag.color, color: '#fff' }}
>
{tag.name}
</Tag>
))}
{record.hasCustomPricing && (
<Tooltip content={t('已设置自定义定价')}>
<IconEditStroked size='small' style={{ color: 'var(--semi-color-primary)' }} />


+ 33
- 31
web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx Zobrazit soubor

@@ -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;

+ 3
- 1
web/src/components/table/model-pricing/view/card/PricingCardView.jsx Zobrazit soubor

@@ -333,7 +333,7 @@ const PricingCardView = ({
/>
</Tooltip>
</div>
<div className='grid grid-cols-3 gap-2 text-xs text-gray-600'>
<div className='grid grid-cols-2 gap-2 text-xs text-gray-600'>
<div>
{t('模型')}:{' '}
{model.quota_type === 0 ? model.model_ratio : t('无')}
@@ -344,9 +344,11 @@ const PricingCardView = ({
? parseFloat(model.completion_ratio.toFixed(3))
: t('无')}
</div>
{/* 分组定价已隐藏
<div>
{t('分组')}: {priceData?.usedGroupRatio ?? '-'}
</div>
*/}
</div>
</div>
)}


+ 2
- 22
web/src/hooks/common/useNavigation.js Zobrazit soubor

@@ -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,


+ 42
- 0
web/src/i18n/locales/en.json Zobrazit soubor

@@ -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"
}
}

+ 43
- 1
web/src/i18n/locales/zh-CN.json Zobrazit soubor

@@ -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。",
"生态伙伴": "生态伙伴",
"与主流模型供应商深度对接": "与主流模型供应商深度对接",
"保持统一协议,快速切换与扩展模型能力,随时接入最新生态。": "保持统一协议,快速切换与扩展模型能力,随时接入最新生态。",
"将大模型能力真正落地到业务流程": "将大模型能力真正落地到业务流程",
"立即接入,统一控制访问策略与调用成本,为产品带来稳定、可扩展的智能体验。": "立即接入,统一控制访问策略与调用成本,为产品带来稳定、可扩展的智能体验。",
"立即开始": "立即开始",
"查看文档": "查看文档",
"文档": "文档",
"关于": "关于",
"服务条款": "服务条款",
"使用政策": "使用政策",
"版权所有": "版权所有",
"实时调度": "实时调度",
"健康度与延迟权重动态切换,保证最优响应。": "健康度与延迟权重动态切换,保证最优响应。",
"统一监控": "统一监控",
"调用、费用、异常一站式可视化,随时掌握运行状态。": "调用、费用、异常一站式可视化,随时掌握运行状态。",
"智能限流": "智能限流",
"多维策略保障核心业务优先级,避免突发拥堵。": "多维策略保障核心业务优先级,避免突发拥堵。"
}
}

+ 548
- 0
web/src/index.css Zobrazit soubor

@@ -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);
}

+ 81
- 0
web/src/pages/Home/components/CTASection.jsx Zobrazit soubor

@@ -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;

+ 212
- 0
web/src/pages/Home/components/HeroSection.jsx Zobrazit soubor

@@ -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;

+ 83
- 0
web/src/pages/Home/components/HomePageFooter.jsx Zobrazit soubor

@@ -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;

+ 110
- 0
web/src/pages/Home/components/PartnersSection.jsx Zobrazit soubor

@@ -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;

+ 133
- 0
web/src/pages/Home/components/ToolsSection.jsx Zobrazit soubor

@@ -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;

+ 99
- 0
web/src/pages/Home/components/ValueSection.jsx Zobrazit soubor

@@ -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;

+ 97
- 0
web/src/pages/Home/components/WorkflowSection.jsx Zobrazit soubor

@@ -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;

+ 62
- 183
web/src/pages/Home/index.jsx Zobrazit soubor

@@ -18,69 +18,34 @@ For commercial licensing, please contact support@quantumnous.com
*/

import React, { useContext, useEffect, useState } from 'react';
import {
Button,
Typography,
Input,
ScrollList,
ScrollItem,
} from '@douyinfe/semi-ui';
import { API, showError, copy, showSuccess } from '../../helpers';
import { useIsMobile } from '../../hooks/common/useIsMobile';
import { API_ENDPOINTS } from '../../constants/common.constant';
import { useTranslation } from 'react-i18next';
import { marked } from 'marked';
import { StatusContext } from '../../context/Status';
import { useActualTheme } from '../../context/Theme';
import { marked } from 'marked';
import { useTranslation } from 'react-i18next';
import {
IconGithubLogo,
IconPlay,
IconFile,
IconCopy,
} from '@douyinfe/semi-icons';
import { Link } from 'react-router-dom';
import { API, showError } from '../../helpers';
import NoticeModal from '../../components/layout/NoticeModal';
import HomePricingFilters from './HomePricingFilters';
import {
Moonshot,
OpenAI,
XAI,
Zhipu,
Volcengine,
Cohere,
Claude,
Gemini,
Suno,
Minimax,
Wenxin,
Spark,
Qingyan,
DeepSeek,
Qwen,
Midjourney,
Grok,
AzureAI,
Hunyuan,
Xinference,
} from '@lobehub/icons';
import { useIsMobile } from '../../hooks/common/useIsMobile';

// 首页组件
import HeroSection from './components/HeroSection';
import ValueSection from './components/ValueSection';
import WorkflowSection from './components/WorkflowSection';
import ToolsSection from './components/ToolsSection';
import PartnersSection from './components/PartnersSection';
import CTASection from './components/CTASection';
import HomePageFooter from './components/HomePageFooter';

const { Text } = Typography;
// 模型广场
import HomePricingFilters from './HomePricingFilters';

const Home = () => {
const { t, i18n } = useTranslation();
const { i18n } = useTranslation();
const [statusState] = useContext(StatusContext);
const actualTheme = useActualTheme();
const [homePageContentLoaded, setHomePageContentLoaded] = useState(false);
const [homePageContent, setHomePageContent] = useState('');
const [noticeVisible, setNoticeVisible] = useState(false);
const isMobile = useIsMobile();
const isDemoSiteMode = statusState?.status?.demo_site_enabled || false;
const docsLink = statusState?.status?.docs_link || '';
const serverAddress =
statusState?.status?.server_address || `${window.location.origin}`;
const endpointItems = API_ENDPOINTS.map((e) => ({ value: e }));
const [endpointIndex, setEndpointIndex] = useState(0);
const isChinese = i18n.language.startsWith('zh');

const displayHomePageContent = async () => {
setHomePageContent(localStorage.getItem('home_page_content') || '');
@@ -111,13 +76,6 @@ const Home = () => {
setHomePageContentLoaded(true);
};

const handleCopyBaseURL = async () => {
const ok = await copy(serverAddress);
if (ok) {
showSuccess(t('已复制到剪切板'));
}
};

useEffect(() => {
const checkNoticeAndShow = async () => {
const lastCloseDate = localStorage.getItem('notice_close_date');
@@ -142,140 +100,61 @@ const Home = () => {
displayHomePageContent().then();
}, []);

useEffect(() => {
const timer = setInterval(() => {
setEndpointIndex((prev) => (prev + 1) % endpointItems.length);
}, 3000);
return () => clearInterval(timer);
}, [endpointItems.length]);

// 如果有自定义首页内容,显示自定义内容
if (homePageContentLoaded && homePageContent !== '') {
return (
<div className="w-full overflow-x-hidden">
{homePageContent.startsWith('https://') ? (
<iframe
src={homePageContent}
className="h-screen w-full border-none"
/>
) : (
<div
className="mt-[60px]"
dangerouslySetInnerHTML={{ __html: homePageContent }}
/>
)}
</div>
);
}

// 默认首页布局
return (
<div className='w-full overflow-x-hidden'>
<div className="home-snap-container">
<NoticeModal
visible={noticeVisible}
onClose={() => setNoticeVisible(false)}
isMobile={isMobile}
/>
{homePageContentLoaded && homePageContent === '' ? (
<div className='w-full overflow-x-hidden'>
{/* Banner 部分 */}
<div className='w-full border-b border-semi-color-border min-h-[500px] md:min-h-[600px] lg:min-h-[700px] relative overflow-x-hidden'>
{/* 背景模糊晕染球 */}
<div className='blur-ball blur-ball-indigo' />
<div className='blur-ball blur-ball-teal' />
<div className='flex flex-col items-center w-full px-4 py-20 md:py-24 lg:py-32 mt-10'>
{/* 居中内容区 */}
<div className='flex flex-col items-center justify-center text-center max-w-4xl mx-auto w-full'>
<div className='flex flex-col items-center justify-center mb-6 md:mb-8'>
<h1
className={`text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold text-semi-color-text-0 leading-tight ${isChinese ? 'tracking-wide md:tracking-wider' : ''}`}
>
<>
{t('统一的')}
<br />
<span className='shine-text'>{t('大模型接口网关')}</span>
</>
</h1>
<p className='text-base md:text-lg lg:text-xl text-semi-color-text-1 mt-4 md:mt-6 max-w-xl'>
{t('更好的价格,更好的稳定性,只需要将模型基址替换为:')}
</p>
{/* BASE URL 与端点选择 */}
<div className='flex flex-col md:flex-row items-center justify-center gap-4 w-full mt-4 md:mt-6 max-w-md'>
<Input
readonly
value={serverAddress}
className='flex-1 !rounded-full'
size={isMobile ? 'default' : 'large'}
suffix={
<div className='flex items-center gap-2'>
<ScrollList
bodyHeight={32}
style={{ border: 'unset', boxShadow: 'unset' }}
>
<ScrollItem
mode='wheel'
cycled={true}
list={endpointItems}
selectedIndex={endpointIndex}
onSelect={({ index }) => setEndpointIndex(index)}
/>
</ScrollList>
<Button
type='primary'
onClick={handleCopyBaseURL}
icon={<IconCopy />}
className='!rounded-full'
/>
</div>
}
/>
</div>
</div>

{/* 操作按钮 */}
<div className='flex flex-row gap-4 justify-center items-center'>
<Link to='/console'>
<Button
theme='solid'
type='primary'
size={isMobile ? 'default' : 'large'}
className='!rounded-3xl px-8 py-2'
icon={<IconPlay />}
>
{t('获取密钥')}
</Button>
</Link>
{isDemoSiteMode && statusState?.status?.version ? (
<Button
size={isMobile ? 'default' : 'large'}
className='flex items-center !rounded-3xl px-6 py-2'
icon={<IconGithubLogo />}
onClick={() =>
window.open(
'https://github.com/QuantumNous/new-api',
'_blank',
)
}
>
{statusState.status.version}
</Button>
) : (
docsLink && (
<Button
size={isMobile ? 'default' : 'large'}
className='flex items-center !rounded-3xl px-6 py-2'
icon={<IconFile />}
onClick={() => window.open(docsLink, '_blank')}
>
{t('文档')}
</Button>
)
)}
</div>
{/* Hero Section */}
<HeroSection />

</div>
{/* 模型广场筛选项与列表 */}
<div className='w-full max-w-6xl mt-8 px-4'>
<HomePricingFilters t={t} />
</div>
</div>
</div>
</div>
) : (
<div className='overflow-x-hidden w-full'>
{homePageContent.startsWith('https://') ? (
<iframe
src={homePageContent}
className='w-full h-screen border-none'
/>
) : (
<div
className='mt-[60px]'
dangerouslySetInnerHTML={{ __html: homePageContent }}
/>
)}
{/* 工具链 Section */}
<ToolsSection />

{/* 核心价值 Section */}
<ValueSection />

{/* 工作流 Section */}
<WorkflowSection />

{/* 生态伙伴 Section */}
<PartnersSection />

{/* 模型广场 Section */}
<section className="home-snap-section home-snap-section-pricing bg-[var(--semi-color-bg-0)]">
<div className="mx-auto w-full max-w-6xl px-5 py-16 md:px-6 lg:px-8">
<HomePricingFilters t={(key) => key} />
</div>
)}
</section>

{/* CTA Section */}
<CTASection />

{/* Footer */}
<HomePageFooter />
</div>
);
};


Načítá se…
Zrušit
Uložit