mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-11 14:56:57 +08:00
feat(web): 新增 Landing 页面与 UI 优化\n\n- 新增 LandingPage、CryptoFeatureCard 等组件\n- 登录/注册页面与样式优化\n- 静态资源 images/main.png
This commit is contained in:
107
web/package-lock.json
generated
107
web/package-lock.json
generated
@@ -8,13 +8,17 @@
|
|||||||
"name": "nofx-web",
|
"name": "nofx-web",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"framer-motion": "^12.23.24",
|
||||||
"lucide-react": "^0.552.0",
|
"lucide-react": "^0.552.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"recharts": "^2.15.2",
|
"recharts": "^2.15.2",
|
||||||
"swr": "^2.2.5",
|
"swr": "^2.2.5",
|
||||||
|
"tailwind-merge": "^3.3.1",
|
||||||
"zustand": "^5.0.2"
|
"zustand": "^5.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -834,6 +838,39 @@
|
|||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rolldown/pluginutils": {
|
"node_modules/@rolldown/pluginutils": {
|
||||||
"version": "1.0.0-beta.27",
|
"version": "1.0.0-beta.27",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
||||||
@@ -1504,6 +1541,18 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/class-variance-authority": {
|
||||||
|
"version": "0.7.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||||
|
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"clsx": "^2.1.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://polar.sh/cva"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/clsx": {
|
"node_modules/clsx": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
@@ -1905,6 +1954,33 @@
|
|||||||
"url": "https://github.com/sponsors/rawify"
|
"url": "https://github.com/sponsors/rawify"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "12.23.24",
|
||||||
|
"resolved": "https://registry.npmmirror.com/framer-motion/-/framer-motion-12.23.24.tgz",
|
||||||
|
"integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-dom": "^12.23.23",
|
||||||
|
"motion-utils": "^12.23.6",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
@@ -2212,6 +2288,21 @@
|
|||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/motion-dom": {
|
||||||
|
"version": "12.23.23",
|
||||||
|
"resolved": "https://registry.npmmirror.com/motion-dom/-/motion-dom-12.23.23.tgz",
|
||||||
|
"integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-utils": "^12.23.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-utils": {
|
||||||
|
"version": "12.23.6",
|
||||||
|
"resolved": "https://registry.npmmirror.com/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||||
|
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@@ -2970,6 +3061,16 @@
|
|||||||
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tailwind-merge": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/dcastil"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.18",
|
"version": "3.4.18",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz",
|
||||||
@@ -3096,6 +3197,12 @@
|
|||||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"license": "0BSD"
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
|||||||
@@ -8,13 +8,17 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"framer-motion": "^12.23.24",
|
||||||
"lucide-react": "^0.552.0",
|
"lucide-react": "^0.552.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"recharts": "^2.15.2",
|
"recharts": "^2.15.2",
|
||||||
"swr": "^2.2.5",
|
"swr": "^2.2.5",
|
||||||
|
"tailwind-merge": "^3.3.1",
|
||||||
"zustand": "^5.0.2"
|
"zustand": "^5.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
BIN
web/public/images/main.png
Normal file
BIN
web/public/images/main.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 675 KiB |
@@ -6,6 +6,7 @@ import { AITradersPage } from './components/AITradersPage';
|
|||||||
import { LoginPage } from './components/LoginPage';
|
import { LoginPage } from './components/LoginPage';
|
||||||
import { RegisterPage } from './components/RegisterPage';
|
import { RegisterPage } from './components/RegisterPage';
|
||||||
import { CompetitionPage } from './components/CompetitionPage';
|
import { CompetitionPage } from './components/CompetitionPage';
|
||||||
|
import { LandingPage } from './components/LandingPage';
|
||||||
import AILearning from './components/AILearning';
|
import AILearning from './components/AILearning';
|
||||||
import { LanguageProvider, useLanguage } from './contexts/LanguageContext';
|
import { LanguageProvider, useLanguage } from './contexts/LanguageContext';
|
||||||
import { AuthProvider, useAuth } from './contexts/AuthContext';
|
import { AuthProvider, useAuth } from './contexts/AuthContext';
|
||||||
@@ -179,12 +180,16 @@ function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not in admin mode and not authenticated, show login/register pages
|
// Show landing page for root route when not authenticated
|
||||||
if (!systemConfig?.admin_mode && (!user || !token)) {
|
if (!systemConfig?.admin_mode && (!user || !token)) {
|
||||||
|
if (route === '/login') {
|
||||||
|
return <LoginPage />;
|
||||||
|
}
|
||||||
if (route === '/register') {
|
if (route === '/register') {
|
||||||
return <RegisterPage />;
|
return <RegisterPage />;
|
||||||
}
|
}
|
||||||
return <LoginPage />;
|
// Default to landing page when not authenticated
|
||||||
|
return <LandingPage />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
119
web/src/components/CryptoFeatureCard.tsx
Normal file
119
web/src/components/CryptoFeatureCard.tsx
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { Check } from "lucide-react";
|
||||||
|
import { cn } from "../lib/utils";
|
||||||
|
|
||||||
|
interface CryptoFeatureCardProps {
|
||||||
|
icon: React.ReactNode;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
features: string[];
|
||||||
|
className?: string;
|
||||||
|
delay?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CryptoFeatureCard = React.forwardRef<HTMLDivElement, CryptoFeatureCardProps>(
|
||||||
|
({ icon, title, description, features, className, delay = 0 }, ref) => {
|
||||||
|
const [isHovered, setIsHovered] = React.useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
ref={ref}
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
transition={{ duration: 0.5, delay }}
|
||||||
|
onHoverStart={() => setIsHovered(true)}
|
||||||
|
onHoverEnd={() => setIsHovered(false)}
|
||||||
|
className="relative h-full"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"relative h-full overflow-hidden border-2 transition-all duration-300 rounded-xl",
|
||||||
|
"bg-gradient-to-br from-[#0C0E12] to-[#1E2329]",
|
||||||
|
"border-[#2B3139] hover:border-[#F0B90B]/50",
|
||||||
|
isHovered && "shadow-[0_0_20px_rgba(240,185,11,0.2)]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{/* Animated glow border effect */}
|
||||||
|
<motion.div
|
||||||
|
className="absolute inset-0 opacity-0 pointer-events-none"
|
||||||
|
animate={{
|
||||||
|
opacity: isHovered ? 1 : 0,
|
||||||
|
}}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-[#F0B90B]/20 to-transparent animate-[shimmer_2s_infinite]" />
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Background pattern */}
|
||||||
|
<div className="absolute inset-0 opacity-5">
|
||||||
|
<div
|
||||||
|
className="absolute inset-0"
|
||||||
|
style={{
|
||||||
|
backgroundImage: `radial-gradient(circle at 2px 2px, #F0B90B 1px, transparent 0)`,
|
||||||
|
backgroundSize: "32px 32px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative z-10 p-8 flex flex-col h-full">
|
||||||
|
{/* Icon container */}
|
||||||
|
<motion.div
|
||||||
|
className={cn(
|
||||||
|
"mb-6 inline-flex items-center justify-center w-16 h-16 rounded-xl",
|
||||||
|
"bg-gradient-to-br from-[#F0B90B]/20 to-[#F0B90B]/5",
|
||||||
|
"border border-[#F0B90B]/30"
|
||||||
|
)}
|
||||||
|
animate={{
|
||||||
|
scale: isHovered ? 1.1 : 1,
|
||||||
|
boxShadow: isHovered
|
||||||
|
? "0 0 20px rgba(240, 185, 11, 0.4)"
|
||||||
|
: "0 0 0px rgba(240, 185, 11, 0)",
|
||||||
|
}}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
>
|
||||||
|
<div className="text-[#F0B90B]">{icon}</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Title */}
|
||||||
|
<h3 className="text-2xl font-bold text-[#EAECEF] mb-3">{title}</h3>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
|
<p className="text-[#848E9C] mb-6 flex-grow leading-relaxed">{description}</p>
|
||||||
|
|
||||||
|
{/* Features list */}
|
||||||
|
<div className="space-y-3 mb-6">
|
||||||
|
{features.map((feature, index) => (
|
||||||
|
<motion.div
|
||||||
|
key={index}
|
||||||
|
initial={{ opacity: 0, x: -10 }}
|
||||||
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
transition={{ delay: delay + index * 0.1 }}
|
||||||
|
className="flex items-start gap-3"
|
||||||
|
>
|
||||||
|
<div className="mt-0.5 flex-shrink-0">
|
||||||
|
<div className="w-5 h-5 rounded-full bg-[#F0B90B]/20 flex items-center justify-center">
|
||||||
|
<Check className="w-3 h-3 text-[#F0B90B]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="text-sm text-[#EAECEF]">{feature}</span>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Corner accent */}
|
||||||
|
<div className="absolute bottom-0 right-0 w-32 h-32 opacity-10">
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-tl from-[#F0B90B] to-transparent" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
CryptoFeatureCard.displayName = "CryptoFeatureCard";
|
||||||
1148
web/src/components/LandingPage.tsx
Normal file
1148
web/src/components/LandingPage.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ import { useAuth } from '../contexts/AuthContext';
|
|||||||
import { useLanguage } from '../contexts/LanguageContext';
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
import { t } from '../i18n/translations';
|
import { t } from '../i18n/translations';
|
||||||
import { Header } from './Header';
|
import { Header } from './Header';
|
||||||
|
import { ArrowLeft } from 'lucide-react';
|
||||||
|
|
||||||
export function LoginPage() {
|
export function LoginPage() {
|
||||||
const { language } = useLanguage();
|
const { language } = useLanguage();
|
||||||
@@ -52,9 +53,22 @@ export function LoginPage() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen" style={{ background: '#0B0E11' }}>
|
<div className="min-h-screen" style={{ background: '#0B0E11' }}>
|
||||||
<Header simple />
|
<Header simple />
|
||||||
|
|
||||||
<div className="flex items-center justify-center" style={{ minHeight: 'calc(100vh - 80px)' }}>
|
<div className="flex items-center justify-center" style={{ minHeight: 'calc(100vh - 80px)' }}>
|
||||||
<div className="w-full max-w-md">
|
<div className="w-full max-w-md">
|
||||||
|
{/* Back to Home */}
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
window.history.pushState({}, '', '/');
|
||||||
|
window.dispatchEvent(new PopStateEvent('popstate'));
|
||||||
|
}}
|
||||||
|
className="flex items-center gap-2 mb-6 text-sm hover:text-[#F0B90B] transition-colors"
|
||||||
|
style={{ color: '#848E9C' }}
|
||||||
|
>
|
||||||
|
<ArrowLeft className="w-4 h-4" />
|
||||||
|
返回首页
|
||||||
|
</button>
|
||||||
|
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className="text-center mb-8">
|
<div className="text-center mb-8">
|
||||||
<div className="w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
<div className="w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||||
@@ -191,4 +205,4 @@ export function LoginPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
|||||||
import { useAuth } from '../contexts/AuthContext';
|
import { useAuth } from '../contexts/AuthContext';
|
||||||
import { useLanguage } from '../contexts/LanguageContext';
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
import { t } from '../i18n/translations';
|
import { t } from '../i18n/translations';
|
||||||
|
import { ArrowLeft } from 'lucide-react';
|
||||||
|
|
||||||
export function RegisterPage() {
|
export function RegisterPage() {
|
||||||
const { language } = useLanguage();
|
const { language } = useLanguage();
|
||||||
@@ -73,6 +74,21 @@ export function RegisterPage() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center" style={{ background: '#0B0E11' }}>
|
<div className="min-h-screen flex items-center justify-center" style={{ background: '#0B0E11' }}>
|
||||||
<div className="w-full max-w-md">
|
<div className="w-full max-w-md">
|
||||||
|
{/* Back to Home */}
|
||||||
|
{step === 'register' && (
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
window.history.pushState({}, '', '/');
|
||||||
|
window.dispatchEvent(new PopStateEvent('popstate'));
|
||||||
|
}}
|
||||||
|
className="flex items-center gap-2 mb-6 text-sm hover:text-[#F0B90B] transition-colors"
|
||||||
|
style={{ color: '#848E9C' }}
|
||||||
|
>
|
||||||
|
<ArrowLeft className="w-4 h-4" />
|
||||||
|
返回首页
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className="text-center mb-8">
|
<div className="text-center mb-8">
|
||||||
<div className="w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
<div className="w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||||
@@ -307,4 +323,4 @@ export function RegisterPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,21 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
/* Binance Brand Colors */
|
||||||
|
--brand-yellow: #F0B90B;
|
||||||
|
--brand-black: #0C0E12;
|
||||||
|
--brand-dark-gray: #1E2329;
|
||||||
|
--brand-light-gray: #EAECEF;
|
||||||
|
--brand-almost-white: #FAFAFA;
|
||||||
|
--brand-white: #FFFFFF;
|
||||||
|
|
||||||
/* Binance Theme Colors */
|
/* Binance Theme Colors */
|
||||||
--binance-yellow: #F0B90B;
|
--binance-yellow: #F0B90B;
|
||||||
--binance-yellow-dark: #C99400;
|
--binance-yellow-dark: #C99400;
|
||||||
--binance-yellow-light: #FCD535;
|
--binance-yellow-light: #FCD535;
|
||||||
--binance-yellow-glow: rgba(240, 185, 11, 0.2);
|
--binance-yellow-glow: rgba(240, 185, 11, 0.2);
|
||||||
|
|
||||||
--background: #0B0E11;
|
--background: #0C0E12;
|
||||||
--background-elevated: #181A20;
|
--background-elevated: #181A20;
|
||||||
--foreground: #EAECEF;
|
--foreground: #EAECEF;
|
||||||
--panel-bg: #1E2329;
|
--panel-bg: #1E2329;
|
||||||
@@ -138,10 +146,10 @@ body {
|
|||||||
|
|
||||||
@keyframes shimmer {
|
@keyframes shimmer {
|
||||||
0% {
|
0% {
|
||||||
background-position: -200% 0;
|
transform: translateX(-100%);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
background-position: 200% 0;
|
transform: translateX(100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
web/src/lib/utils.ts
Normal file
6
web/src/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { clsx, type ClassValue } from "clsx";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user