From e4a3b1bd296b96269bd72096aa5387d5294fceb3 Mon Sep 17 00:00:00 2001 From: lichao Date: Tue, 31 Mar 2026 23:40:56 +0800 Subject: [PATCH] feat: customize frontend with OasisRelay branding and UI enhancements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rebrand hero section with OasisRelay title, gradient text, and uptime counter - Add transparent-to-frosted-glass nav bar effect on home page scroll - Add grid square background pattern on home page - Simplify nav items to: 首页, 控制台, 定价, 文档 - Change 分组监控 to Activity icon button opening external status page - Switch font family to PingFang SC with cross-platform fallbacks - Fix username visibility in light mode nav bar - Restyle login/register forms and home page sections Co-Authored-By: Claude Opus 4.6 (1M context) --- web/bun.lock | 9 +- web/src/components/auth/LoginForm.jsx | 606 ++++----- web/src/components/auth/RegisterForm.jsx | 559 ++++---- .../layout/headerbar/ActionButtons.jsx | 18 +- .../layout/headerbar/Navigation.jsx | 9 +- .../components/layout/headerbar/UserArea.jsx | 50 +- web/src/components/layout/headerbar/index.jsx | 97 +- web/src/hooks/common/useNavigation.js | 8 +- web/src/index.css | 1165 ++++++++++++++++- web/src/pages/Home/index.jsx | 704 ++++++---- 10 files changed, 2151 insertions(+), 1074 deletions(-) diff --git a/web/bun.lock b/web/bun.lock index e3b293cb..9a841922 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "react-template", @@ -10,7 +11,7 @@ "@visactor/react-vchart": "~1.8.8", "@visactor/vchart": "~1.8.8", "@visactor/vchart-semi-theme": "~1.8.8", - "axios": "1.12.0", + "axios": "1.13.5", "clsx": "^2.1.1", "dayjs": "^1.11.11", "history": "^5.3.0", @@ -776,7 +777,7 @@ "autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="], - "axios": ["axios@1.12.0", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg=="], + "axios": ["axios@1.13.5", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q=="], "babel-plugin-macros": ["babel-plugin-macros@3.1.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="], @@ -1104,13 +1105,13 @@ "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], - "follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="], + "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], "for-in": ["for-in@1.0.2", "", {}, "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ=="], "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], - "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], + "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], diff --git a/web/src/components/auth/LoginForm.jsx b/web/src/components/auth/LoginForm.jsx index 7e8c0ce0..0c24251a 100644 --- a/web/src/components/auth/LoginForm.jsx +++ b/web/src/components/auth/LoginForm.jsx @@ -43,15 +43,11 @@ import { import Turnstile from 'react-turnstile'; import { Button, - Card, Checkbox, - Divider, Form, Icon, Modal, } from '@douyinfe/semi-ui'; -import Title from '@douyinfe/semi-ui/lib/es/typography/title'; -import Text from '@douyinfe/semi-ui/lib/es/typography/text'; import TelegramLoginButton from 'react-telegram-login'; import { @@ -502,369 +498,245 @@ const LoginForm = () => { const renderOAuthOptions = () => { return ( -
-
-
- Logo - - {systemName} - -
- - -
- - {t('登 录')} - -
-
-
- {status.wechat_login && ( - - )} - - {status.github_oauth && ( - - )} - - {status.discord_oauth && ( - - )} - - {status.oidc_enabled && ( - - )} - - {status.linuxdo_oauth && ( - - )} - - {status.custom_oauth_providers && - status.custom_oauth_providers.map((provider) => ( - - ))} - - {status.telegram_oauth && ( -
- -
- )} - - {status.passkey_login && passkeySupported && ( - - )} - - - {t('或')} - - - -
- - {(hasUserAgreement || hasPrivacyPolicy) && ( -
- setAgreedToTerms(e.target.checked)} - > - - {t('我已阅读并同意')} - {hasUserAgreement && ( - <> - - {t('用户协议')} - - - )} - {hasUserAgreement && hasPrivacyPolicy && t('和')} - {hasPrivacyPolicy && ( - <> - - {t('隐私政策')} - - - )} - - -
- )} - - {!status.self_use_mode_enabled && ( -
- - {t('没有账户?')}{' '} - - {t('注册')} - - -
- )} -
-
+
+
+ Logo + {systemName}
+ +

{t('登 录')}

+ +
+ {status.wechat_login && ( + + )} + + {status.github_oauth && ( + + )} + + {status.discord_oauth && ( + + )} + + {status.oidc_enabled && ( + + )} + + {status.linuxdo_oauth && ( + + )} + + {status.custom_oauth_providers && + status.custom_oauth_providers.map((provider) => ( + + ))} + + {status.telegram_oauth && ( +
+ +
+ )} + + {status.passkey_login && passkeySupported && ( + + )} + +
{t('或')}
+ + +
+ + {(hasUserAgreement || hasPrivacyPolicy) && ( +
+ setAgreedToTerms(e.target.checked)}> + + {t('我已阅读并同意')} + {hasUserAgreement && ( + {t('用户协议')} + )} + {hasUserAgreement && hasPrivacyPolicy && t('和')} + {hasPrivacyPolicy && ( + {t('隐私政策')} + )} + + +
+ )} + + {!status.self_use_mode_enabled && ( +

{t('创建新账号')}

+ )}
); }; const renderEmailLoginForm = () => { return ( -
-
-
- Logo - {systemName} -
- - -
- - {t('登 录')} - -
-
- {status.passkey_login && passkeySupported && ( - - )} -
- handleChange('username', value)} - prefix={} - /> - - handleChange('password', value)} - prefix={} - /> - - {(hasUserAgreement || hasPrivacyPolicy) && ( -
- setAgreedToTerms(e.target.checked)} - > - - {t('我已阅读并同意')} - {hasUserAgreement && ( - <> - - {t('用户协议')} - - - )} - {hasUserAgreement && hasPrivacyPolicy && t('和')} - {hasPrivacyPolicy && ( - <> - - {t('隐私政策')} - - - )} - - -
- )} - -
- - - -
- - - {hasOAuthLoginOptions && ( - <> - - {t('或')} - - -
- -
- - )} - - {!status.self_use_mode_enabled && ( -
- - {t('没有账户?')}{' '} - - {t('注册')} - - -
- )} -
-
+
+
+ Logo + {systemName}
+ +

{t('登录到您的账号')}

+

{t('创建新账号')}

+ + {status.passkey_login && passkeySupported && ( + + )} + +
+ + handleChange('username', e.target.value)} + className='lp-input' + /> +
+ +
+ 🔒 + handleChange('password', e.target.value)} + className='lp-input' + /> +
+ + {(hasUserAgreement || hasPrivacyPolicy) && ( +
+ setAgreedToTerms(e.target.checked)}> + + {t('我已阅读并同意')} + {hasUserAgreement && ( + {t('用户协议')} + )} + {hasUserAgreement && hasPrivacyPolicy && t('和')} + {hasPrivacyPolicy && ( + {t('隐私政策')} + )} + + +
+ )} + + + + + + {hasOAuthLoginOptions && ( + <> +
{t('或')}
+ + + )}
); }; @@ -947,19 +819,9 @@ const LoginForm = () => { }; return ( -
- {/* 背景模糊晕染球 */} -
-
-
- {showEmailLogin || - !hasOAuthLoginOptions +
+
+ {showEmailLogin || !hasOAuthLoginOptions ? renderEmailLoginForm() : renderOAuthOptions()} {renderWeChatLoginModal()} diff --git a/web/src/components/auth/RegisterForm.jsx b/web/src/components/auth/RegisterForm.jsx index 0a755b19..c4d34266 100644 --- a/web/src/components/auth/RegisterForm.jsx +++ b/web/src/components/auth/RegisterForm.jsx @@ -35,21 +35,14 @@ import { import Turnstile from 'react-turnstile'; import { Button, - Card, Checkbox, - Divider, Form, Icon, Modal, } from '@douyinfe/semi-ui'; -import Title from '@douyinfe/semi-ui/lib/es/typography/title'; -import Text from '@douyinfe/semi-ui/lib/es/typography/text'; import { IconGithubLogo, IconMail, - IconUser, - IconLock, - IconKey, } from '@douyinfe/semi-icons'; import { onGitHubOAuthClicked, @@ -393,162 +386,118 @@ const RegisterForm = () => { const renderOAuthOptions = () => { return ( -
-
-
- Logo - - {systemName} - -
+
+
+ Logo + {systemName} +
- -
- - {t('注 册')} - +

{t('创建新账号')}

+

{t('登录已有账号')}

+ +
+ {status.wechat_login && ( + + )} + + {status.github_oauth && ( + + )} + + {status.discord_oauth && ( + + )} + + {status.oidc_enabled && ( + + )} + + {status.linuxdo_oauth && ( + + )} + + {status.custom_oauth_providers && + status.custom_oauth_providers.map((provider) => ( + + ))} + + {status.telegram_oauth && ( +
+
-
-
- {status.wechat_login && ( - - )} + )} - {status.github_oauth && ( - - )} +
{t('或')}
- {status.discord_oauth && ( - - )} - - {status.oidc_enabled && ( - - )} - - {status.linuxdo_oauth && ( - - )} - - {status.custom_oauth_providers && - status.custom_oauth_providers.map((provider) => ( - - ))} - - {status.telegram_oauth && ( -
- -
- )} - - - {t('或')} - - - -
- -
- - {t('已有账户?')}{' '} - - {t('登录')} - - -
-
- +
); @@ -556,175 +505,126 @@ const RegisterForm = () => { const renderEmailRegisterForm = () => { return ( -
-
-
- Logo - - {systemName} - -
+
+
+ Logo + {systemName} +
- -
- - {t('注 册')} - +

{t('创建新账号')}

+

{t('登录已有账号')}

+ +
+ 👤 + handleChange('username', e.target.value)} + className='lp-input' + /> +
+ +
+ 🔒 + handleChange('password', e.target.value)} + className='lp-input' + /> +
+ +
+ 🔒 + handleChange('password2', e.target.value)} + className='lp-input' + /> +
+ + {showEmailVerification && ( + <> +
+ + handleChange('email', e.target.value)} + className='lp-input' + />
-
-
- handleChange('username', value)} - prefix={} +
+
+ 🔑 + handleChange('verification_code', e.target.value)} + className='lp-input' /> - - handleChange('password', value)} - prefix={} - /> - - handleChange('password2', value)} - prefix={} - /> - - {showEmailVerification && ( - <> - handleChange('email', value)} - prefix={} - suffix={ - - } - /> - - handleChange('verification_code', value) - } - prefix={} - /> - - )} - - {(hasUserAgreement || hasPrivacyPolicy) && ( -
- setAgreedToTerms(e.target.checked)} - > - - {t('我已阅读并同意')} - {hasUserAgreement && ( - <> - - {t('用户协议')} - - - )} - {hasUserAgreement && hasPrivacyPolicy && t('和')} - {hasPrivacyPolicy && ( - <> - - {t('隐私政策')} - - - )} - - -
- )} - -
- -
- - - {hasOAuthRegisterOptions && ( - <> - - {t('或')} - - -
- -
- - )} - -
- - {t('已有账户?')}{' '} - - {t('登录')} - -
+
- + + )} + + {(hasUserAgreement || hasPrivacyPolicy) && ( +
+ setAgreedToTerms(e.target.checked)}> + + {t('我已阅读并同意')} + {hasUserAgreement && ( + {t('用户协议')} + )} + {hasUserAgreement && hasPrivacyPolicy && t('和')} + {hasPrivacyPolicy && ( + {t('隐私政策')} + )} + + +
+ )} + + + + {hasOAuthRegisterOptions && ( + <> +
{t('或')}
+ + + )} + +
+
注册后您将获得:
+
使用量统计面板
+
灵活的订阅方案
+
200+ 模型一键接入
+
专属 API Token
); @@ -770,17 +670,8 @@ const RegisterForm = () => { }; return ( -
- {/* 背景模糊晕染球 */} -
-
-
+
+
{showEmailRegister || !hasOAuthRegisterOptions ? renderEmailRegisterForm() diff --git a/web/src/components/layout/headerbar/ActionButtons.jsx b/web/src/components/layout/headerbar/ActionButtons.jsx index 545b5227..9eb7c147 100644 --- a/web/src/components/layout/headerbar/ActionButtons.jsx +++ b/web/src/components/layout/headerbar/ActionButtons.jsx @@ -18,6 +18,8 @@ For commercial licensing, please contact support@quantumnous.com */ import React from 'react'; +import { Button } from '@douyinfe/semi-ui'; +import { Activity } from 'lucide-react'; import NewYearButton from './NewYearButton'; import NotificationButton from './NotificationButton'; import ThemeToggle from './ThemeToggle'; @@ -41,23 +43,37 @@ const ActionButtons = ({ t, }) => { return ( -
+
+ {/* 通知栏 */} + {/* 分组监控 */} + - +
{showRegisterButton && (
- - + + {t('注册')}
)} + + {t('登录')} +
); } diff --git a/web/src/components/layout/headerbar/index.jsx b/web/src/components/layout/headerbar/index.jsx index 81b51d7f..9015ccd9 100644 --- a/web/src/components/layout/headerbar/index.jsx +++ b/web/src/components/layout/headerbar/index.jsx @@ -17,10 +17,11 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { useHeaderBar } from '../../../hooks/common/useHeaderBar'; import { useNotifications } from '../../../hooks/common/useNotifications'; import { useNavigation } from '../../../hooks/common/useNavigation'; +import { useActualTheme } from '../../../context/Theme'; import NoticeModal from '../NoticeModal'; import MobileMenuButton from './MobileMenuButton'; import HeaderLogo from './HeaderLogo'; @@ -51,9 +52,12 @@ const HeaderBar = ({ onMobileMenuToggle, drawerOpen }) => { handleThemeToggle, handleMobileMenuToggle, navigate, + location, t, } = useHeaderBar({ onMobileMenuToggle, drawerOpen }); + const actualTheme = useActualTheme(); + const { noticeVisible, unreadCount, @@ -64,8 +68,41 @@ const HeaderBar = ({ onMobileMenuToggle, drawerOpen }) => { const { mainNavLinks } = useNavigation(t, docsLink, headerNavModules); + // 首页路由 = '/' 或 '/home' + const isHomePage = location.pathname === '/' || location.pathname === '/home'; + + // 滚动监听:仅首页启用透明导航 + // 注意:页面滚动发生在 Semi UI 的内层 Layout 容器(overflow:auto),不是 window + // 须用 document capture 阶段才能拿到滚动事件 + const [scrolled, setScrolled] = useState(false); + useEffect(() => { + if (!isHomePage) return; + const onScroll = (e) => { + const top = e.target?.scrollTop ?? window.scrollY; + setScrolled(top > 10); + }; + document.addEventListener('scroll', onScroll, { passive: true, capture: true }); + // 初始化:检查当前滚动位置 + const layouts = document.querySelectorAll('.semi-layout'); + let initTop = window.scrollY; + layouts.forEach(el => { if (el.scrollTop > initTop) initTop = el.scrollTop; }); + setScrolled(initTop > 10); + return () => document.removeEventListener('scroll', onScroll, { capture: true }); + }, [isHomePage]); + + // 深色导航栏条件:首页 或 actualTheme=dark + const useDarkNav = isHomePage || actualTheme === 'dark'; + + const headerClass = [ + 'lp-header-bar', + useDarkNav ? 'lp-header-dark' : 'lp-header-light', + // 非首页始终用 scrolled 样式(实色背景),首页根据滚动位置切换 + (!isHomePage || scrolled) ? 'lp-header-scrolled' : 'lp-header-top', + 'text-semi-color-text-0 sticky top-0 z-50 transition-all duration-300', + ].join(' '); + return ( -
+
{ unreadKeys={getUnreadKeys()} /> -
-
-
+
+
+
{ />
- +
+ +
- +
+ +
diff --git a/web/src/hooks/common/useNavigation.js b/web/src/hooks/common/useNavigation.js index f7e61a20..cd52e1b8 100644 --- a/web/src/hooks/common/useNavigation.js +++ b/web/src/hooks/common/useNavigation.js @@ -45,7 +45,7 @@ export const useNavigation = (t, docsLink, headerNavModules) => { to: '/console', }, { - text: t('模型广场'), + text: t('定价'), itemKey: 'pricing', to: '/pricing', }, @@ -59,11 +59,6 @@ export const useNavigation = (t, docsLink, headerNavModules) => { }, ] : []), - { - text: t('关于'), - itemKey: 'about', - to: '/about', - }, ]; // 根据配置过滤导航链接 @@ -72,7 +67,6 @@ export const useNavigation = (t, docsLink, headerNavModules) => { return docsLink && modules.docs; } if (link.itemKey === 'pricing') { - // 支持新的pricing配置格式 return typeof modules.pricing === 'object' ? modules.pricing.enabled : modules.pricing; diff --git a/web/src/index.css b/web/src/index.css index 116ff741..1da3919b 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -18,19 +18,78 @@ --sidebar-width: 180px; --sidebar-width-collapsed: 60px; --sidebar-current-width: var(--sidebar-width); + + /* 苹方字体栈:macOS/iOS 用苹方,Windows 用微软雅黑,Linux 用 Noto Sans */ + --font-sans: 'PingFang SC', 'PingFang HK', 'PingFang TC', + -apple-system, BlinkMacSystemFont, + 'Microsoft YaHei', 'Microsoft YaHei UI', + 'Noto Sans SC', 'Noto Sans CJK SC', + 'Helvetica Neue', Arial, sans-serif; + + /* 小方格背景(深色默认) */ + --grid-line: rgba(255,255,255,0.055); + --grid-size: 32px; +} + +/* 浅色主题下格子线改为深色 */ +html.semi-always-light, +body[theme-mode="light"], +html:not(.dark) body { + --grid-line: rgba(0,0,0,0.06); +} + +/* 深色主题 */ +html.dark, +body[theme-mode="dark"] { + --grid-line: rgba(255,255,255,0.055); } body.sidebar-collapsed { --sidebar-current-width: var(--sidebar-width-collapsed); } +/* html/body 只提供背景底色,格子只在首页 .lp-root 内显示 */ +html { + background-color: var(--semi-color-bg-0, #0e1520); +} + body { - font-family: Lato, 'Helvetica Neue', Arial, Helvetica, 'Microsoft YaHei', - sans-serif; + font-family: var(--font-sans); + font-weight: 400; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; color: var(--semi-color-text-0); background-color: var(--semi-color-bg-0); } +/* 苹方字重适配:细 / 常规 / 中等 / 粗 */ +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-sans); + font-weight: 600; + letter-spacing: -.01em; +} + +strong, b, th { + font-weight: 600; +} + +/* Semi UI 组件字体覆盖 */ +.semi-typography, +.semi-button, +.semi-input, +.semi-select, +.semi-table, +.semi-form, +.semi-navigation, +.semi-tabs, +.semi-dropdown, +.semi-modal, +.semi-toast, +.semi-notification, +.semi-tooltip { + font-family: var(--font-sans) !important; +} + .app-layout { height: 100vh; height: 100dvh; @@ -955,7 +1014,198 @@ html.dark .with-pastel-balls::before { gap: 0.25rem; } -/* ==================== 自定义圆角样式 ==================== */ +/* ==================== 自定义顶部导航栏 ==================== */ +.lp-header-bar { + height: 56px; + display: flex; + align-items: center; + transition: background .3s, border-color .3s, backdrop-filter .3s; +} + +/* 深色导航栏(首页 + dark模式) */ +.lp-header-dark { + background: rgba(10,15,26,.60); + border-bottom: 1px solid rgba(255,255,255,.07); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); +} +/* 深色 + 顶部 → 完全透明无模糊 */ +.lp-header-dark.lp-header-top { + background: transparent !important; + border-bottom: 1px solid transparent !important; + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; +} + +/* 浅色导航栏(非首页 + light模式) */ +.lp-header-light { + background: rgba(255,255,255,.60); + border-bottom: 1px solid rgba(0,0,0,.07); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); +} +/* 浅色 + 顶部 → 完全透明无模糊 */ +.lp-header-light.lp-header-top { + background: transparent !important; + border-bottom: 1px solid transparent !important; + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; +} + +/* ── 导航链接:深色模式 ── */ +.lp-header-dark .lp-nav-link { + color: #ffffff !important; + text-decoration: none !important; + padding: 5px 14px !important; + border-radius: 8px !important; + font-size: .9rem !important; + font-weight: 500 !important; + transition: color .2s, background .2s !important; + flex-shrink: 0; + display: flex; + align-items: center; +} +.lp-header-dark .lp-nav-link:hover { + color: #ffffff !important; + background: rgba(255,255,255,.08) !important; +} + +/* ── 导航链接:浅色模式 ── */ +.lp-header-light .lp-nav-link { + color: #1a1a2e !important; + text-decoration: none !important; + padding: 5px 14px !important; + border-radius: 8px !important; + font-size: .9rem !important; + font-weight: 500 !important; + transition: color .2s, background .2s !important; + flex-shrink: 0; + display: flex; + align-items: center; +} +.lp-header-light .lp-nav-link:hover { + color: #000 !important; + background: rgba(0,0,0,.06) !important; +} + +/* ── Logo / 用户名文字颜色 ── */ +.lp-header-dark a.group .semi-typography, +.lp-header-dark .semi-typography { color: #dde3ee !important; } + +/* 浅色导航(非首页):深色字 */ +.lp-header-light a.group .semi-typography, +.lp-header-light .semi-typography { color: #1a1a2e !important; } +/* 但首页用深色导航,浅色模式下首页导航仍是深色背景,文字要亮色 */ +.lp-header-dark.lp-header-top a.group .semi-typography, +.lp-header-dark.lp-header-top .semi-typography, +.lp-header-dark.lp-header-scrolled a.group .semi-typography, +.lp-header-dark.lp-header-scrolled .semi-typography { color: #dde3ee !important; } + +/* ── 右侧图标按钮:深色模式 ── */ +.lp-header-dark .semi-button-tertiary.semi-button-borderless { + color: rgba(221,227,238,.7) !important; + background: rgba(255,255,255,.04) !important; + border: 1px solid rgba(255,255,255,.1) !important; +} +.lp-header-dark .semi-button-tertiary.semi-button-borderless:hover { + background: rgba(255,255,255,.08) !important; + border-color: rgba(255,255,255,.2) !important; + color: #dde3ee !important; +} + +/* ── 右侧图标按钮:浅色模式 ── */ +.lp-header-light .semi-button-tertiary.semi-button-borderless { + color: rgba(30,30,50,.6) !important; + background: rgba(0,0,0,.03) !important; + border: 1px solid rgba(0,0,0,.1) !important; +} +.lp-header-light .semi-button-tertiary.semi-button-borderless:hover { + background: rgba(0,0,0,.07) !important; + border-color: rgba(0,0,0,.18) !important; + color: #1a1a2e !important; +} + +/* ── 登录按钮:深色模式 ── */ +.lp-header-dark .lp-nav-login-btn { + display: inline-flex; + align-items: center; + padding: 6px 18px; + border-radius: 8px; + background: transparent; + color: rgba(221,227,238,.85) !important; + font-size: .88rem; + font-weight: 500; + text-decoration: none; + border: 1px solid rgba(255,255,255,.18) !important; + transition: all .2s; +} +.lp-header-dark .lp-nav-login-btn:hover { + background: rgba(255,255,255,.08) !important; + border-color: rgba(255,255,255,.32) !important; + color: #fff !important; +} + +/* ── 登录按钮:浅色模式 ── */ +.lp-header-light .lp-nav-login-btn { + display: inline-flex; + align-items: center; + padding: 6px 18px; + border-radius: 8px; + background: transparent; + color: #1a1a2e !important; + font-size: .88rem; + font-weight: 500; + text-decoration: none; + border: 1px solid rgba(0,0,0,.18) !important; + transition: all .2s; +} +.lp-header-light .lp-nav-login-btn:hover { + background: rgba(0,0,0,.05) !important; + border-color: rgba(0,0,0,.3) !important; + color: #000 !important; +} + +/* ── 橙色注册按钮(两种模式通用) ── */ +.lp-header-bar .lp-nav-orange-btn { + display: inline-flex; + align-items: center; + padding: 6px 18px; + border-radius: 8px; + background: #e8834a !important; + color: #fff !important; + font-size: .88rem; + font-weight: 600; + text-decoration: none; + border: none !important; + cursor: pointer; + transition: opacity .2s, transform .15s; +} +.lp-header-bar .lp-nav-orange-btn:hover { + opacity: .88 !important; + transform: translateY(-1px); +} + +/* ── 用户头像区域:浅色模式字色 ── */ +.lp-header-light .semi-avatar { border: 1px solid rgba(0,0,0,.12); } +.lp-header-light .semi-typography { color: #1a1a2e !important; } + +/* ── 用户头像区域:深色模式 ── */ +.lp-header-dark .semi-avatar { border: 1px solid rgba(255,255,255,.15); } + +/* 兜底:老class不带 dark/light 子类时也能工作 */ +.lp-header-bar .lp-nav-link { + text-decoration: none !important; + padding: 5px 14px !important; + border-radius: 8px !important; + font-size: .9rem !important; + font-weight: 500 !important; + transition: color .2s, background .2s !important; + flex-shrink: 0; + display: flex; + align-items: center; +} + + .semi-radio, .semi-tagInput, .semi-input-textarea-wrapper, @@ -994,3 +1244,912 @@ html.dark .with-pastel-balls::before { opacity: 1; } } + +/* ==================== 首页 Landing Page 专用样式 ==================== */ +.lp-root { + --lp-bg: transparent; + --lp-bg2: transparent; + --lp-card: #161f2e; + --lp-card-b: rgba(255,255,255,.07); + --lp-text: #dde3ee; + --lp-dim: rgba(221,227,238,.5); + --lp-dim2: rgba(221,227,238,.32); + /* 首页始终深色背景,格子颜色固定用白色线条 */ + --grid-line: rgba(255,255,255,0.055); + --lp-orange: #e8834a; + --lp-teal: #2dd4bf; + --lp-purple: #8b5cf6; + --lp-blue: #60a5fa; + color: var(--lp-text); + background: #0a0f1a; + font-family: 'Inter','Noto Sans SC',system-ui,sans-serif; + overflow-x: hidden; + width: 100%; + position: relative; +} + +/* 格子仅在首页 .lp-root 内显示,fixed 固定随视口 */ +.lp-root::before { + content: ''; + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; + background-image: + linear-gradient(var(--grid-line) 1px, transparent 1px), + linear-gradient(90deg, var(--grid-line) 1px, transparent 1px); + background-size: var(--grid-size) var(--grid-size); +} + +/* 首页内所有直接子元素在格子之上 */ +.lp-root > * { + position: relative; + z-index: 1; +} + +/* Hero 区 */ +.lp-hero { + position: relative; + width: 100%; + min-height: 86vh; + display: flex; + flex-direction: column; + align-items: center; + overflow: visible; + background: transparent; /* 格子从 html 透上来 */ + padding-bottom: 72px; +} + +/* Tyndall 光柱动效 */ +.lp-tyndall { + position: absolute; + inset: 0; + overflow: hidden; + pointer-events: none; + z-index: 1; +} +.lp-ty { + position: absolute; + border-radius: 50%; + will-change: transform, opacity; +} +@keyframes lp-ty-sweep { + 0% { opacity: 0; transform: translate(0,0) scale(.85); } + 18% { opacity: 1; } + 78% { opacity: .85; } + 100% { opacity: 0; transform: translate(-38vw,16vh) scale(1.08); } +} +.lp-ty1 { + width: 58vw; height: 90vh; + background: radial-gradient(ellipse 55% 65% at 55% 35%, rgba(0,200,150,.52) 0%, transparent 70%); + filter: blur(72px); left: 55%; top: -12%; + animation: lp-ty-sweep 10s cubic-bezier(.45,0,.55,1) infinite; +} +.lp-ty2 { + width: 52vw; height: 85vh; + background: radial-gradient(ellipse 55% 65% at 52% 38%, rgba(118,44,248,.50) 0%, transparent 70%); + filter: blur(80px); left: 62%; top: -5%; + animation: lp-ty-sweep 12s cubic-bezier(.45,0,.55,1) infinite; + animation-delay: -4s; +} +.lp-ty3 { + width: 48vw; height: 80vh; + background: radial-gradient(ellipse 55% 65% at 50% 32%, rgba(196,20,88,.48) 0%, transparent 70%); + filter: blur(75px); left: 58%; top: -8%; + animation: lp-ty-sweep 14s cubic-bezier(.45,0,.55,1) infinite; + animation-delay: -8s; +} +.lp-ty4 { + width: 54vw; height: 82vh; + background: radial-gradient(ellipse 55% 65% at 53% 36%, rgba(8,150,205,.46) 0%, transparent 70%); + filter: blur(78px); left: 66%; top: -6%; + animation: lp-ty-sweep 11s cubic-bezier(.45,0,.55,1) infinite; + animation-delay: -2s; +} +.lp-ty5 { + width: 44vw; height: 75vh; + background: radial-gradient(ellipse 55% 65% at 50% 40%, rgba(50,205,120,.40) 0%, transparent 70%); + filter: blur(68px); left: 70%; top: -4%; + animation: lp-ty-sweep 13s cubic-bezier(.45,0,.55,1) infinite; + animation-delay: -6s; +} + +/* Hero 文字区 */ +.lp-hero-top { + position: relative; + z-index: 2; + text-align: center; + padding: 100px 20px 0; + width: min(680px, 90vw); +} +.lp-hero-title { + font-size: clamp(2.6rem,6.5vw,4.8rem); + font-weight: 900; + letter-spacing: -.5px; + line-height: 1.15; + margin-bottom: 16px; + /* 橙→金→玫瑰红渐变 */ + background: linear-gradient(135deg, #ff9d4d 0%, #f5c842 45%, #ff6b9d 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} +.lp-hero-title-sub { + display: block; + font-size: clamp(1.1rem,2.8vw,1.9rem); + font-weight: 700; + letter-spacing: .08em; + margin-top: 8px; + /* 青→蓝渐变 */ + background: linear-gradient(135deg, #38d9c0 0%, #5b9cf6 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} +.lp-hero-sub { + font-size: clamp(.92rem,1.8vw,1.05rem); + color: rgba(221,227,238,.72); + font-weight: 500; + line-height: 1.75; + max-width: 580px; + margin: 0 auto; +} + +/* Hero 预览框 */ +.lp-fluid-wrap { + position: relative; + z-index: 2; + width: min(780px,82vw); + height: clamp(220px, 44vh, 480px); + margin: 36px auto 0; + border-radius: 22px; + overflow: hidden; + flex-shrink: 0; + box-shadow: -18px 32px 100px rgba(0,0,0,.72), 0 20px 50px rgba(0,0,0,.45), inset 0 0 0 1px rgba(255,255,255,.08); +} +.lp-fluid-card { + width: 100%; height: 100%; + background: rgba(6,11,20,.85); + position: relative; overflow: hidden; +} + +/* 运行时间计时器 — 标题上方 */ +.lp-uptime { + display: inline-flex; + align-items: center; + gap: 8px; + background: rgba(255,255,255,.05); + border: 1px solid rgba(255,255,255,.1); + border-radius: 100px; + padding: 7px 18px 7px 12px; + margin-bottom: 22px; +} +.lp-uptime-dot { + width: 7px; height: 7px; + border-radius: 50%; + background: #2dd4bf; + box-shadow: 0 0 6px #2dd4bf; + flex-shrink: 0; + animation: lp-dot-pulse 2s ease-in-out infinite; +} +@keyframes lp-dot-pulse { + 0%,100% { opacity: 1; box-shadow: 0 0 6px #2dd4bf; } + 50% { opacity: .4; box-shadow: 0 0 2px #2dd4bf; } +} +.lp-uptime-label { + font-size: .86rem; + font-weight: 500; + color: rgba(221,227,238,.55); + white-space: nowrap; + margin-right: 4px; +} +.lp-uptime-block { + display: inline-flex; + align-items: baseline; + gap: 1px; + background: rgba(0,0,0,.3); + border: 1px solid rgba(255,255,255,.08); + border-radius: 6px; + padding: 3px 9px; + font-size: .92rem; + font-weight: 700; + color: #e2e8f0; + font-variant-numeric: tabular-nums; + letter-spacing: .01em; +} +.lp-uptime-block em { + font-style: normal; + font-size: .7rem; + font-weight: 400; + color: rgba(221,227,238,.4); + margin-left: 2px; +} + +/* Hero pills */ +.lp-hero-pills { + position: relative; z-index: 2; + display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; + margin-top: 24px; padding-bottom: 8px; +} +.lp-hero-pill { + display: inline-flex; align-items: center; gap: 6px; + padding: 7px 18px; border-radius: 100px; + border: 1px solid rgba(255,255,255,.18); + background: rgba(255,255,255,.06); + font-size: .83rem; font-weight: 600; color: rgba(221,227,238,.8); + transition: border-color .2s, background .2s, color .2s; +} +.lp-hero-pill:hover { border-color: rgba(255,255,255,.28); background: rgba(255,255,255,.07); color: var(--lp-text); } + +/* 向下滚动箭头 */ +.lp-hero-scroll { + position: relative; left: auto; bottom: auto; transform: none; + z-index: 3; display: flex; flex-direction: column; align-items: center; gap: 2px; cursor: pointer; + margin-top: 20px; +} +.lp-hero-scroll span { + display: block; width: 18px; height: 18px; + border-right: 2px solid rgba(255,255,255,.5); + border-bottom: 2px solid rgba(255,255,255,.5); + transform: rotate(45deg); + animation: lp-chev-bounce 1.8s ease-in-out infinite; +} +.lp-hero-scroll span:nth-child(2) { animation-delay: .18s; opacity: .55; } +.lp-hero-scroll span:nth-child(3) { animation-delay: .36s; opacity: .25; } +@keyframes lp-chev-bounce { + 0%,100% { opacity: .8; transform: rotate(45deg) translate(0,0); } + 50% { opacity: .2; transform: rotate(45deg) translate(4px,4px); } +} + +/* Section 基础 */ +.lp-section { padding: 88px 5%; } +.lp-label { + display: block; text-align: center; + font-size: .7rem; font-weight: 700; letter-spacing: 2.5px; + text-transform: uppercase; color: var(--lp-teal); + margin-bottom: 18px; +} +.lp-s-title { + font-size: clamp(1.6rem,3.5vw,2.4rem); + font-weight: 800; letter-spacing: -.5px; line-height: 1.25; + text-align: center; margin-bottom: 14px; + color: var(--lp-text); +} +.lp-s-sub { + text-align: center; + color: rgba(221,227,238,.65); + font-size: .97rem; + font-weight: 500; + line-height: 1.75; + max-width: 520px; margin: 0 auto 56px; +} +.lp-divider { + height: 1px; + background: linear-gradient(90deg, transparent, rgba(255,255,255,.07), transparent); +} + +/* Integrations 区 — 实色 */ +.lp-integrations { background: #080d16; padding: 64px 5%; } +.lp-int-header { + display: flex; align-items: center; justify-content: space-between; + margin-bottom: 28px; flex-wrap: wrap; gap: 16px; + max-width: 1180px; margin-left: auto; margin-right: auto; +} +.lp-int-header .lp-label { margin: 0; text-align: left; } +.lp-int-note { + display: flex; align-items: center; gap: 12px; + font-size: .85rem; color: var(--lp-dim); +} +.lp-int-note a { + display: inline-flex; align-items: center; gap: 6px; + padding: 7px 16px; border-radius: 8px; + border: 1px solid rgba(255,255,255,.1); + background: rgba(255,255,255,.04); + color: var(--lp-dim); text-decoration: none; font-size: .83rem; font-weight: 500; + transition: all .2s; +} +.lp-int-note a:hover { border-color: rgba(255,255,255,.22); color: var(--lp-text); background: rgba(255,255,255,.07); } +.lp-int-cards { + display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; + max-width: 1180px; margin-left: auto; margin-right: auto; +} +@media(max-width:700px){ .lp-int-cards { grid-template-columns: 1fr; } } +.lp-int-card { + background: var(--lp-card); border: 1px solid var(--lp-card-b); + border-radius: 14px; padding: 22px 24px; + display: flex; align-items: center; gap: 16px; + text-decoration: none; color: var(--lp-text); + transition: border-color .25s, background .25s, transform .2s, box-shadow .25s; + cursor: pointer; position: relative; overflow: hidden; +} +.lp-int-card::before { + content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px; + background: linear-gradient(90deg, transparent, var(--lp-ic, rgba(255,255,255,.15)), transparent); + opacity: 0; transition: opacity .3s; +} +.lp-int-card:hover { + border-color: var(--lp-ic, rgba(255,255,255,.2)); + background: rgba(255,255,255,.05); + transform: translateY(-3px); + box-shadow: 0 12px 40px rgba(0,0,0,.35); +} +.lp-int-card:hover::before { opacity: 1; } +.lp-int-avatar { + width: 42px; height: 42px; border-radius: 10px; + display: flex; align-items: center; justify-content: center; + font-size: 1.1rem; font-weight: 800; flex-shrink: 0; + background: var(--lp-ib, rgba(255,255,255,.08)); +} +.lp-int-info { flex: 1; } +.lp-int-name { font-size: .95rem; font-weight: 700; margin-bottom: 3px; } +.lp-int-org { font-size: .78rem; color: var(--lp-dim2); } +.lp-int-arrow { color: var(--lp-dim2); font-size: .9rem; flex-shrink: 0; } +.lp-ic-claude { --lp-ic: rgba(205,133,63,.45); --lp-ib: rgba(205,133,63,.12); } +.lp-ic-codex { --lp-ic: rgba(16,163,127,.45); --lp-ib: rgba(16,163,127,.12); } +.lp-ic-gemini { --lp-ic: rgba(66,133,244,.45); --lp-ib: rgba(66,133,244,.12); } + +/* Features 区 */ +/* Features 区 — 透明,格子透出 */ +.lp-features { background: transparent; } +.lp-feat-grid { + display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; + max-width: 1180px; margin-left: auto; margin-right: auto; +} +@media(max-width:900px){ .lp-feat-grid { grid-template-columns: repeat(2,1fr); } } +@media(max-width:580px){ .lp-feat-grid { grid-template-columns: 1fr; } } +.lp-feat-card { + background: var(--lp-card); border: 1px solid var(--lp-card-b); + border-radius: 14px; padding: 26px 24px; + transition: border-color .25s, transform .2s; +} +.lp-feat-card:hover { border-color: rgba(255,255,255,.14); transform: translateY(-3px); } +.lp-feat-icon { + width: 36px; height: 36px; border-radius: 9px; + display: flex; align-items: center; justify-content: center; + font-size: 1rem; margin-bottom: 16px; +} +.lp-fi-teal { background: rgba(45,212,191,.12); } +.lp-fi-blue { background: rgba(96,165,250,.12); } +.lp-fi-green { background: rgba(52,211,153,.12); } +.lp-fi-purple { background: rgba(139,92,246,.12); } +.lp-fi-orange { background: rgba(251,146,60,.12); } +.lp-fi-pink { background: rgba(244,114,182,.12); } +.lp-feat-card h3 { font-size: .95rem; font-weight: 700; margin-bottom: 8px; color: var(--lp-text); } +.lp-feat-card p { font-size: .85rem; color: var(--lp-dim); line-height: 1.7; } + +/* Comparison 区 */ +/* Comparison 区 — 实色 */ +.lp-comparison { background: #080d16; } +.lp-cmp-wrap { + overflow-x: auto; + border: 1px solid var(--lp-card-b); border-radius: 14px; overflow: hidden; + max-width: 1180px; margin-left: auto; margin-right: auto; +} +.lp-cmp-table { + width: 100%; border-collapse: collapse; font-size: .88rem; color: var(--lp-text); +} +.lp-cmp-table th, +.lp-cmp-table td { padding: 15px 22px; text-align: left; border-bottom: 1px solid rgba(255,255,255,.05); } +.lp-cmp-table thead th { + background: rgba(255,255,255,.04); + font-size: .78rem; font-weight: 700; letter-spacing: .8px; + text-transform: uppercase; color: var(--lp-dim2); +} +.lp-cmp-table tbody tr:last-child td { border-bottom: none; } +.lp-cmp-table tbody tr:nth-child(even) td { background: rgba(255,255,255,.018); } +.lp-cmp-table tbody tr:hover td { background: rgba(255,255,255,.035); } +.lp-cmp-table td:first-child { color: var(--lp-dim2); font-weight: 500; width: 140px; } +.lp-th-claude { color: #e8a87c !important; } +.lp-th-codex { color: #34d399 !important; } +.lp-th-gemini { color: #60a5fa !important; } +.lp-badge { + display: inline-flex; align-items: center; gap: 6px; + font-size: .78rem; font-weight: 600; + padding: 3px 10px; border-radius: 100px; border: 1px solid; +} +.lp-cb-claude { color: #e8a87c; border-color: rgba(205,133,63,.35); background: rgba(205,133,63,.08); } +.lp-cb-codex { color: #34d399; border-color: rgba(52,211,153,.35); background: rgba(52,211,153,.08); } +.lp-cb-gemini { color: #60a5fa; border-color: rgba(96,165,250,.35); background: rgba(96,165,250,.08); } +.lp-code-inline { + font-size: .83rem; padding: 2px 8px; border-radius: 5px; + font-family: 'Fira Code','Courier New',monospace; +} + +/* Steps 区 */ +/* Steps 区 — 透明,格子透出 */ +.lp-steps { background: transparent; } +.lp-steps-grid { + display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; + max-width: 1180px; margin-left: auto; margin-right: auto; +} +@media(max-width:760px){ .lp-steps-grid { grid-template-columns: 1fr; } } +.lp-step-card { + background: var(--lp-card); border: 1px solid var(--lp-card-b); + border-radius: 14px; padding: 28px 24px; + transition: border-color .25s, transform .2s; position: relative; +} +.lp-step-card:hover { border-color: rgba(45,212,191,.25); transform: translateY(-3px); } +.lp-step-num { + font-size: .7rem; font-weight: 700; letter-spacing: 2px; + text-transform: uppercase; color: var(--lp-teal); margin-bottom: 16px; +} +.lp-step-icon { + width: 44px; height: 44px; border-radius: 12px; + background: rgba(45,212,191,.1); border: 1px solid rgba(45,212,191,.2); + display: flex; align-items: center; justify-content: center; + font-size: 1.2rem; margin-bottom: 18px; +} +.lp-step-card h3 { font-size: .95rem; font-weight: 700; margin-bottom: 8px; color: var(--lp-text); } +.lp-step-card p { font-size: .85rem; color: var(--lp-dim); line-height: 1.7; margin-bottom: 18px; } +.lp-step-cta { + display: inline-flex; align-items: center; gap: 6px; + color: var(--lp-teal); font-size: .83rem; font-weight: 600; + text-decoration: none; transition: gap .2s; +} +.lp-step-cta:hover { gap: 10px; } + +/* Steps CTA 块 */ +.lp-steps-cta { + margin-top: 36px; + background: linear-gradient(135deg, rgba(45,212,191,.08), rgba(139,92,246,.08)); + border: 1px solid rgba(45,212,191,.18); + border-radius: 16px; padding: 36px 32px; text-align: center; + max-width: 1180px; margin-left: auto; margin-right: auto; +} +.lp-steps-cta h3 { font-size: 1.15rem; font-weight: 700; margin-bottom: 8px; color: var(--lp-text); } +.lp-steps-cta p { font-size: .88rem; color: var(--lp-dim); margin-bottom: 22px; line-height: 1.65; } + +/* 代码块 */ +.lp-code-block { + margin: 40px auto 0; + background: rgba(255,255,255,.03); + border: 1px solid rgba(255,255,255,.07); + border-radius: 14px; padding: 24px 28px; + max-width: 620px; +} +.lp-code-dots { display: flex; gap: 7px; margin-bottom: 16px; align-items: center; } +.lp-dot-r { width: 11px; height: 11px; border-radius: 50%; background: #ff5f57; flex-shrink: 0; } +.lp-dot-y { width: 11px; height: 11px; border-radius: 50%; background: #febc2e; flex-shrink: 0; } +.lp-dot-g { width: 11px; height: 11px; border-radius: 50%; background: #28c840; flex-shrink: 0; } +.lp-code-block code { + display: block; line-height: 1.85; + color: #c9d1d9; overflow-x: auto; white-space: pre; + font-family: 'Fira Code','Courier New',monospace; font-size: .83rem; +} +.lp-ck { color: #7c6aff; } +.lp-cs { color: #3ecfcf; } +.lp-cv { color: #f97316; } + +/* Big CTA 区 */ +/* Big CTA 区 — 实色 */ +.lp-big-cta { background: #080d16; padding: 88px 5%; text-align: center; } +.lp-big-cta .lp-s-title { margin-bottom: 12px; } +.lp-big-cta p { color: var(--lp-dim); font-size: .95rem; margin-bottom: 32px; } +.lp-cta-btns { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; } + +/* 按钮 */ +.lp-btn-teal { + display: inline-flex; align-items: center; gap: 8px; + padding: 11px 26px; border-radius: 10px; + background: var(--lp-teal); color: #0a1a18; + font-size: .9rem; font-weight: 700; + text-decoration: none; border: none; cursor: pointer; + transition: opacity .2s, transform .15s; +} +.lp-btn-teal:hover { opacity: .88; transform: translateY(-2px); } +.lp-btn-outline { + display: inline-flex; align-items: center; gap: 8px; + padding: 11px 24px; border-radius: 10px; + background: transparent; color: var(--lp-dim); + font-size: .88rem; font-weight: 500; text-decoration: none; + border: 1px solid rgba(255,255,255,.12); + transition: all .2s; +} +.lp-btn-outline:hover { border-color: rgba(255,255,255,.28); color: var(--lp-text); } + +/* FAQ 区 */ +/* FAQ 区 — 透明,格子透出 */ +.lp-faq { background: transparent; padding: 88px 5%; } +.lp-faq-list { + max-width: 760px; margin: 0 auto; + display: flex; flex-direction: column; gap: 2px; +} +.lp-faq-item { + border: 1px solid var(--lp-card-b); border-radius: 12px; + background: var(--lp-card); overflow: hidden; + transition: border-color .2s; +} +.lp-faq-item:hover { border-color: rgba(255,255,255,.13); } +.lp-faq-item.lp-open { border-color: rgba(45,212,191,.2); } +.lp-faq-q { + width: 100%; display: flex; align-items: center; justify-content: space-between; + padding: 18px 22px; background: none; border: none; + cursor: pointer; color: var(--lp-text); font-size: .9rem; font-weight: 500; + text-align: left; gap: 12px; +} +.lp-faq-arrow { color: var(--lp-dim2); flex-shrink: 0; transition: transform .25s; font-size: .8rem; } +.lp-faq-item.lp-open .lp-faq-arrow { transform: rotate(180deg); } +.lp-faq-a { + max-height: 0; overflow: hidden; + transition: max-height .3s ease, padding .3s; + padding: 0 22px; +} +.lp-faq-item.lp-open .lp-faq-a { max-height: 200px; padding: 0 22px 18px; } +.lp-faq-a p { font-size: .87rem; color: var(--lp-dim); line-height: 1.75; } + +/* Footer — 实色压底 */ +.lp-footer { + background: #060a12; + border-top: 1px solid rgba(255,255,255,.06); + padding: 56px 5% 28px; +} +.lp-footer-grid { + display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 40px; margin-bottom: 48px; +} +@media(max-width:800px){ .lp-footer-grid { grid-template-columns: 1fr 1fr; } } +@media(max-width:480px){ .lp-footer-grid { grid-template-columns: 1fr; } } +.lp-footer-logo { + display: flex; align-items: center; gap: 9px; + font-size: 1rem; font-weight: 700; color: var(--lp-text); + text-decoration: none; margin-bottom: 12px; +} +.lp-footer-brand p { + font-size: .85rem; color: var(--lp-dim2); line-height: 1.7; + max-width: 240px; margin-bottom: 16px; +} +.lp-footer-chips { display: flex; gap: 6px; flex-wrap: wrap; } +.lp-footer-chip { + font-size: .74rem; padding: 3px 9px; border-radius: 6px; + border: 1px solid rgba(255,255,255,.08); color: var(--lp-dim2); +} +.lp-footer-col h4 { + font-size: .75rem; font-weight: 700; letter-spacing: 1px; + text-transform: uppercase; color: var(--lp-dim2); margin-bottom: 14px; +} +.lp-footer-col ul { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 9px; } +.lp-footer-col ul li a { + color: var(--lp-dim2); text-decoration: none; font-size: .86rem; transition: color .2s; +} +.lp-footer-col ul li a:hover { color: var(--lp-text); } +.lp-footer-bottom { + display: flex; align-items: center; justify-content: space-between; + padding-top: 22px; border-top: 1px solid rgba(255,255,255,.05); + font-size: .8rem; color: rgba(255,255,255,.25); flex-wrap: wrap; gap: 8px; +} +.lp-footer-bottom a { color: rgba(255,255,255,.25); text-decoration: none; } +.lp-footer-bottom a:hover { color: var(--lp-dim2); } + +/* 登录/注册 深色背景 */ +.lp-auth-page { + background: + radial-gradient(ellipse 60% 50% at 70% 30%, rgba(0,200,150,.18) 0%, transparent 60%), + radial-gradient(ellipse 50% 40% at 30% 70%, rgba(118,44,248,.15) 0%, transparent 60%), + #0e1520; + color: #dde3ee; + font-family: 'Inter', 'Noto Sans SC', system-ui, sans-serif; +} + +/* 登录/注册主区域 */ +.lp-auth-main { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: 80px 20px 40px; + min-height: inherit; +} + +/* 卡片 */ +.lp-auth-card { + position: relative; + z-index: 1; + width: 100%; + max-width: 480px; + text-align: center; +} + +/* Logo行 */ +.lp-auth-logo-row { + display: flex; + align-items: center; + justify-content: center; + gap: 9px; + margin-bottom: 24px; +} +.lp-auth-brand { + font-size: 1.05rem; + font-weight: 700; + color: #dde3ee; +} + +/* 标题 */ +.lp-auth-title { + font-size: 1.75rem !important; + font-weight: 800 !important; + color: #ffffff !important; + letter-spacing: -.5px; + margin-bottom: 10px; +} + +/* 副标题 */ +.lp-auth-sub { + font-size: .9rem; + color: rgba(221,227,238,.5); + margin-bottom: 28px; +} +.lp-auth-link { + color: #2dd4bf !important; + text-decoration: none !important; + font-weight: 500; + transition: opacity .2s; + cursor: pointer; +} +.lp-auth-link:hover { opacity: .75; } + +/* 原生 input 样式 */ +.lp-input { + width: 100%; + padding: 14px 16px 14px 44px; + background: rgba(255,255,255,.05); + border: 1px solid rgba(255,255,255,.1); + border-radius: 10px; + color: #dde3ee; + font-size: .93rem; + font-family: inherit; + outline: none; + transition: border-color .2s, background .2s; +} +.lp-input::placeholder { color: rgba(221,227,238,.32); } +.lp-input:focus { + border-color: rgba(45,212,191,.45); + background: rgba(255,255,255,.07); +} + +/* 表单输入行 */ +.lp-form-group { + margin-bottom: 16px; + position: relative; + text-align: left; +} +.lp-form-icon { + position: absolute; + left: 14px; + top: 50%; + transform: translateY(-50%); + color: rgba(221,227,238,.32); + font-size: 1rem; + pointer-events: none; + z-index: 2; +} +.lp-form-group .semi-input-wrapper, +.lp-form-group .semi-input { + width: 100% !important; + padding: 14px 16px 14px 44px !important; + background: rgba(255,255,255,.05) !important; + border: 1px solid rgba(255,255,255,.1) !important; + border-radius: 10px !important; + color: #dde3ee !important; + font-size: .93rem !important; + outline: none; + transition: border-color .2s, background .2s; +} +.lp-form-group .semi-input-wrapper:focus-within { + border-color: rgba(45,212,191,.45) !important; + background: rgba(255,255,255,.07) !important; +} +.lp-form-group .semi-input::placeholder { color: rgba(221,227,238,.32) !important; } +/* 去掉 semi-ui 的 label */ +.lp-form-group .semi-form-field-label { display: none !important; } +.lp-form-group .semi-form-field { margin-bottom: 0 !important; } + +/* 验证码行 */ +.lp-code-row { + display: flex; + gap: 10px; + margin-bottom: 16px; + align-items: stretch; +} +.lp-btn-send-code { + flex-shrink: 0; + padding: 0 16px; + border-radius: 10px; + border: 1px solid rgba(45,212,191,.4); + background: rgba(45,212,191,.1); + color: #2dd4bf; + font-size: .83rem; + font-weight: 600; + font-family: inherit; + cursor: pointer; + white-space: nowrap; + transition: all .2s; +} +.lp-btn-send-code:hover { background: rgba(45,212,191,.2); } +.lp-btn-send-code:disabled { opacity: .5; cursor: not-allowed; } + +/* 协议行 */ +.lp-auth-terms, .lp-terms-row { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 20px; + text-align: left; +} +.lp-auth-terms-text { + font-size: .85rem; + color: rgba(221,227,238,.5); +} + +/* 提交按钮 */ +.lp-btn-submit { + width: 100%; + padding: 14px; + border: none; + border-radius: 10px; + background: linear-gradient(135deg, #2dd4bf, #8b5cf6); + color: #fff; + font-size: 1rem; + font-weight: 700; + font-family: inherit; + cursor: pointer; + letter-spacing: .02em; + transition: opacity .2s, transform .15s; + margin-bottom: 8px; +} +.lp-btn-submit:hover { opacity: .88; transform: translateY(-1px); } +.lp-btn-submit:active { transform: translateY(0); } +.lp-btn-submit:disabled { opacity: .5; cursor: not-allowed; transform: none; } + +/* 忘记密码按钮 */ +.lp-btn-forgot { + width: 100%; + padding: 10px; + border: none; + background: transparent; + color: #2dd4bf; + font-size: .88rem; + font-family: inherit; + cursor: pointer; + transition: opacity .2s; + margin-bottom: 4px; +} +.lp-btn-forgot:hover { opacity: .75; } + +/* 其他选项按钮 */ +.lp-btn-other-options { + width: 100%; + padding: 12px; + border: 1px solid rgba(255,255,255,.12); + border-radius: 10px; + background: transparent; + color: rgba(221,227,238,.6); + font-size: .88rem; + font-family: inherit; + cursor: pointer; + transition: all .2s; +} +.lp-btn-other-options:hover { + border-color: rgba(255,255,255,.25); + color: #dde3ee; + background: rgba(255,255,255,.04); +} + +/* 分隔线 */ +.lp-auth-divider { + display: flex; + align-items: center; + gap: 12px; + margin: 18px 0; + color: rgba(221,227,238,.3); + font-size: .83rem; +} +.lp-auth-divider::before, +.lp-auth-divider::after { + content: ''; + flex: 1; + height: 1px; + background: rgba(255,255,255,.1); +} + +/* OAuth 按钮列表 */ +.lp-auth-oauth-list { display: flex; flex-direction: column; gap: 10px; } +.lp-auth-oauth-btn, +.lp-auth-oauth-list .semi-button { + width: 100% !important; + height: 48px !important; + border-radius: 10px !important; + border: 1px solid rgba(255,255,255,.12) !important; + background: rgba(255,255,255,.04) !important; + color: #dde3ee !important; + font-size: .9rem !important; + transition: all .2s !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; +} +.lp-auth-oauth-btn:hover, +.lp-auth-oauth-list .semi-button:hover { + border-color: rgba(255,255,255,.22) !important; + background: rgba(255,255,255,.07) !important; +} +/* OAuth 主登录按钮(solid primary)覆盖 */ +.lp-auth-oauth-list .semi-button-primary.semi-button-solid { + background: linear-gradient(135deg, #2dd4bf, #8b5cf6) !important; + border: none !important; + color: #fff !important; + font-weight: 700 !important; +} + +/* Semi UI 表单控件在 lp-auth-page 里的深色覆盖 */ +.lp-auth-page .semi-input-wrapper, +.lp-auth-page .semi-input { + background: rgba(255,255,255,.05) !important; + border: 1px solid rgba(255,255,255,.1) !important; + color: #dde3ee !important; +} +.lp-auth-page .semi-input-wrapper:focus-within { + border-color: rgba(45,212,191,.45) !important; + background: rgba(255,255,255,.07) !important; +} +.lp-auth-page .semi-input::placeholder { color: rgba(221,227,238,.32) !important; } +.lp-auth-page .semi-checkbox-inner { border-color: rgba(255,255,255,.2) !important; } +.lp-auth-page .semi-checkbox-addon { color: rgba(221,227,238,.5) !important; } + +/* Benefits 区块 */ +.lp-benefits { + margin-top: 36px; + border-top: 1px solid rgba(255,255,255,.07); + padding-top: 24px; + text-align: left; +} +.lp-benefits-title { + font-size: .78rem; + color: rgba(221,227,238,.32); + margin-bottom: 14px; + letter-spacing: .5px; + text-transform: uppercase; +} +.lp-benefit-item { + display: flex; + align-items: center; + gap: 10px; + font-size: .88rem; + color: rgba(221,227,238,.5); + margin-bottom: 10px; +} +.lp-benefit-item::before { + content: '✓'; + color: #2dd4bf; + font-weight: 700; + font-size: .9rem; + flex-shrink: 0; +} + +/* 旧的 benefits 类名兼容 */ +.lp-auth-benefits { + margin-top: 36px; + border-top: 1px solid rgba(255,255,255,.07); + padding-top: 24px; + text-align: left; +} +.lp-auth-benefits-title { + font-size: .78rem; + color: rgba(221,227,238,.32); + margin-bottom: 14px; + letter-spacing: .5px; + text-transform: uppercase; +} +.lp-auth-benefit-item { + display: flex; + align-items: center; + gap: 10px; + font-size: .88rem; + color: rgba(221,227,238,.5); + margin-bottom: 10px; +} +.lp-auth-benefit-item::before { + content: '✓'; + color: #2dd4bf; + font-weight: 700; + font-size: .9rem; + flex-shrink: 0; +} diff --git a/web/src/pages/Home/index.jsx b/web/src/pages/Home/index.jsx index c153c1b3..2776b012 100644 --- a/web/src/pages/Home/index.jsx +++ b/web/src/pages/Home/index.jsx @@ -17,103 +17,111 @@ along with this program. If not, see . 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 React, { useContext, useEffect, useState, useRef } from 'react'; +import { API } from '../../helpers'; 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 NoticeModal from '../../components/layout/NoticeModal'; -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'; -const { Text } = Typography; +/* ────────────────────────────────────────────── + 运行时间计时器(起始时间:2025-01-01 00:00:00) +────────────────────────────────────────────── */ +const START_TIME = new Date('2025-09-12T00:00:00+08:00').getTime(); +const UptimeCounter = () => { + const calc = () => { + const diff = Date.now() - START_TIME; + const s = Math.floor(diff / 1000); + return { + days: Math.floor(s / 86400), + hours: Math.floor((s % 86400) / 3600), + minutes: Math.floor((s % 3600) / 60), + seconds: s % 60, + }; + }; + const [t, setT] = useState(calc); + useEffect(() => { + const id = setInterval(() => setT(calc()), 1000); + return () => clearInterval(id); + }, []); + const pad = n => String(n).padStart(2, '0'); + return ( +
+ + 本站已稳定运行 + {t.days} + {pad(t.hours)} + {pad(t.minutes)} + {pad(t.seconds)} +
+ ); +}; +const FaqItem = ({ question, children, defaultOpen }) => { + const [open, setOpen] = useState(!!defaultOpen); + return ( +
+ +
+

{children}

+
+
+ ); +}; + +/* ────────────────────────────────────────────── + 首页主组件 +────────────────────────────────────────────── */ 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'); + /* 服务端配置的自定义首页内容(Markdown 或外链 URL) */ const displayHomePageContent = async () => { - setHomePageContent(localStorage.getItem('home_page_content') || ''); - const res = await API.get('/api/home_page_content'); - const { success, message, data } = res.data; - if (success) { - let content = data; - if (!data.startsWith('https://')) { - content = marked.parse(data); - } - setHomePageContent(content); - localStorage.setItem('home_page_content', content); - - // 如果内容是 URL,则发送主题模式 - if (data.startsWith('https://')) { - const iframe = document.querySelector('iframe'); - if (iframe) { - iframe.onload = () => { - iframe.contentWindow.postMessage({ themeMode: actualTheme }, '*'); - iframe.contentWindow.postMessage({ lang: i18n.language }, '*'); - }; + const cached = localStorage.getItem('home_page_content') || ''; + setHomePageContent(cached); + try { + const res = await API.get('/api/home_page_content'); + const { success, data } = res.data; + if (success && data && data.trim() !== '') { + let content = data; + if (!data.startsWith('https://')) { + content = marked.parse(data); } - } - } else { - showError(message); - setHomePageContent('加载首页内容失败...'); - } - setHomePageContentLoaded(true); - }; + setHomePageContent(content); + localStorage.setItem('home_page_content', content); - const handleCopyBaseURL = async () => { - const ok = await copy(serverAddress); - if (ok) { - showSuccess(t('已复制到剪切板')); + if (data.startsWith('https://')) { + const iframe = document.querySelector('iframe'); + if (iframe) { + iframe.onload = () => { + iframe.contentWindow.postMessage({ themeMode: actualTheme }, '*'); + iframe.contentWindow.postMessage({ lang: i18n.language }, '*'); + }; + } + } + setHomePageContentLoaded(true); + } else { + // 没有自定义内容,显示默认首页 + localStorage.removeItem('home_page_content'); + setHomePageContent(''); + setHomePageContentLoaded(true); + } + } catch (e) { + // 网络错误/无后端,直接显示默认首页,不报错 + setHomePageContent(''); + setHomePageContentLoaded(true); } }; @@ -133,7 +141,6 @@ const Home = () => { } } }; - checkNoticeAndShow(); }, []); @@ -141,214 +148,361 @@ 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://') ? ( +