diff --git a/src/pages/Login/Login.css b/src/pages/Login/Login.css index e5260be..a84e45b 100644 --- a/src/pages/Login/Login.css +++ b/src/pages/Login/Login.css @@ -12,6 +12,8 @@ --error-color: #ef4444; } +/* ... existing styles ... */ + /* ===================== PAGE LAYOUT & BACKGROUND ===================== */ .login-page { min-height: 100vh; @@ -23,14 +25,16 @@ color: var(--text-primary); position: relative; overflow: hidden; + perspective: 1200px; + /* Enable 3D space */ /* Dynamic Mesh Gradient Background */ background-color: #f8fafc; - background-image: - radial-gradient(at 0% 0%, hsla(253,16%,7%,1) 0, transparent 50%), - radial-gradient(at 50% 0%, hsla(225,39%,30%,1) 0, transparent 50%), - radial-gradient(at 100% 0%, hsla(339,49%,30%,1) 0, transparent 50%); - background: + background-image: + radial-gradient(at 0% 0%, hsla(253, 16%, 7%, 1) 0, transparent 50%), + radial-gradient(at 50% 0%, hsla(225, 39%, 30%, 1) 0, transparent 50%), + radial-gradient(at 100% 0%, hsla(339, 49%, 30%, 1) 0, transparent 50%); + background: radial-gradient(at 0% 0%, rgba(99, 102, 241, 0.15) 0px, transparent 50%), radial-gradient(at 100% 0%, rgba(168, 85, 247, 0.15) 0px, transparent 50%), radial-gradient(at 100% 100%, rgba(236, 72, 153, 0.15) 0px, transparent 50%), @@ -38,6 +42,33 @@ #f8fafc; } +/* ===================== STRENGTH METER ===================== */ +.password-strength-meter { + margin-top: 0.5rem; + display: flex; + flex-direction: column; + gap: 0.375rem; +} + +.strength-bars { + display: flex; + gap: 0.25rem; +} + +.strength-bar { + height: 4px; + flex: 1; + border-radius: 2px; + transition: all 0.3s ease; +} + +.strength-text { + font-size: 0.75rem; + font-weight: 600; + text-align: right; + transition: color 0.3s ease; +} + /* Noise Texture Overlay */ .login-page::before { content: ""; @@ -72,20 +103,20 @@ width: 100%; max-width: 460px; padding: 3rem; - + /* Advanced Glassmorphism */ background: rgba(255, 255, 255, 0.7); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); border: 1px solid rgba(255, 255, 255, 0.8); border-radius: 32px; - - box-shadow: + + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px -1px rgba(0, 0, 0, 0.02), 0 20px 40px -10px rgba(0, 0, 0, 0.08), inset 0 0 0 1px rgba(255, 255, 255, 0.6); - + position: relative; z-index: 10; } @@ -106,15 +137,15 @@ display: flex; align-items: center; justify-content: center; - + background: linear-gradient(135deg, #ffffff 0%, #f1f5f9 100%); border-radius: 24px; color: #6366f1; - box-shadow: + box-shadow: 8px 8px 16px rgba(99, 102, 241, 0.1), -8px -8px 16px rgba(255, 255, 255, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.5); - + cursor: pointer; z-index: 2; } @@ -187,13 +218,13 @@ font-size: 1rem; font-family: inherit; color: var(--text-primary); - + background: rgba(255, 255, 255, 0.8); border: 1px solid #e2e8f0; border-radius: 16px; - + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - box-shadow: + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), inset 0 2px 4px rgba(0, 0, 0, 0.01); } @@ -211,7 +242,7 @@ outline: none; background: white; border-color: #a855f7; - box-shadow: + box-shadow: 0 0 0 4px rgba(168, 85, 247, 0.1), 0 2px 6px rgba(168, 85, 247, 0.05); transform: translateY(-1px); @@ -290,7 +321,7 @@ color: white; background: var(--primary-gradient); background-size: 200% auto; - box-shadow: + box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3), 0 1px 2px rgba(99, 102, 241, 0.2); } @@ -298,7 +329,7 @@ .login-button--primary:hover:not(:disabled) { background-position: right center; transform: translateY(-2px); - box-shadow: + box-shadow: 0 8px 20px rgba(99, 102, 241, 0.4), 0 4px 8px rgba(99, 102, 241, 0.2); } @@ -382,25 +413,25 @@ .login-page { padding: 1rem; } - + .login-container { padding: 2rem 1.5rem; border-radius: 24px; } - + .login-header { margin-bottom: 2rem; } - + .login-title { font-size: 1.75rem; } - + .login-logo { width: 64px; height: 64px; } - + .login-social-buttons { grid-template-columns: 1fr; } diff --git a/src/pages/Login/Login.tsx b/src/pages/Login/Login.tsx index 1aec4ec..239c9cd 100644 --- a/src/pages/Login/Login.tsx +++ b/src/pages/Login/Login.tsx @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react'; import { useNavigate, Link, useLocation } from 'react-router-dom'; import { Icon } from '@iconify/react'; -import { motion, AnimatePresence } from 'framer-motion'; +import { motion, AnimatePresence, useMotionValue, useTransform, useSpring } from 'framer-motion'; import authService from '../../services/authService'; import './Login.css'; @@ -68,19 +68,105 @@ export default function Login({ mode = 'login' }: LoginProps) { } }; + // 3D Tilt Logic + const x = useMotionValue(0); + const y = useMotionValue(0); + const mouseX = useSpring(x, { stiffness: 150, damping: 15 }); + const mouseY = useSpring(y, { stiffness: 150, damping: 15 }); + + const rotateX = useTransform(mouseY, [-0.5, 0.5], ["7deg", "-7deg"]); + const rotateY = useTransform(mouseX, [-0.5, 0.5], ["-7deg", "7deg"]); + + const handleMouseMove = (e: React.MouseEvent) => { + const rect = e.currentTarget.getBoundingClientRect(); + const width = rect.width; + const height = rect.height; + const mouseXFromCenter = e.clientX - rect.left - width / 2; + const mouseYFromCenter = e.clientY - rect.top - height / 2; + x.set(mouseXFromCenter / width); + y.set(mouseYFromCenter / height); + }; + + const handleMouseLeave = () => { + x.set(0); + y.set(0); + }; + + // Password Strength Logic + const calculateStrength = (pwd: string) => { + let score = 0; + if (!pwd) return 0; + if (pwd.length > 7) score += 1; + if (pwd.match(/[a-z]/) && pwd.match(/[A-Z]/)) score += 1; + if (pwd.match(/\d/)) score += 1; + if (pwd.match(/[^a-zA-Z\d]/)) score += 1; + return score; + }; + + const strength = calculateStrength(formData.password); + const strengthColors = ['#e2e8f0', '#ef4444', '#f59e0b', '#3b82f6', '#10b981']; + const strengthLabels = ['未输入', '弱', '中', '强', '极强']; + + const Particles = () => { + // Simple random particles + return ( +
+ {[...Array(20)].map((_, i) => ( + + ))} +
+ ); + }; + return (
+
@@ -122,6 +208,7 @@ export default function Login({ mode = 'login' }: LoginProps) { animate={{ opacity: 1, height: 'auto', marginBottom: 16 }} exit={{ opacity: 0, height: 0, marginBottom: 0 }} className="login-error" + style={{ transformStyle: "preserve-3d", translateZ: 20 }} > {error} @@ -200,8 +287,36 @@ export default function Login({ mode = 'login' }: LoginProps) { {showPassword ? : }
+ - {!isLogin && ( + {!isLogin && formData.password.length > 0 && ( + +
+ {[1, 2, 3, 4].map((i) => ( +
= i ? strengthColors[strength] : '#e2e8f0', + opacity: strength >= i ? 1 : 0.4 + }} + /> + ))} +
+ + {strengthLabels[strength]} + + + )} + + + + {!isLogin && formData.password.length === 0 && (