feat: 登录页5种视觉大片级重设计 — 星河/极光/日月/星辰/流星

5个登录组件全面升级为动态视觉效果:

1. SplitView — 星河流转: 深靛蓝渐变(#0f0c29→#302b63) + 3条CSS流星动画
   + 7个漂浮星辰光点 + 脉动光晕,底线输入框聚焦靛蓝

2. CenteredCard — 极光幻境: 深蓝黑底(#0a0a2e) + 3条流动极光色带
   (翠绿/靛蓝/紫) filter:blur(80px) 缓慢流动,毛玻璃居中卡片

3. SidePanel — 日月交替(重点重做): 暮光紫→夜空蓝渐变
   + 200px天体球(太阳金→月亮银蓝 8s循环) + 300px旋转轨道环
   + 6个轨道光点 + 3条流星 + 10个闪烁星点

4. FullImage — 星辰连线: 深海军蓝(#0a1628) + 33个随机星点
   + 6条星座连线 + 右上角蓝紫发光球 + 左下角暖色光球

5. MinimalDark — 流星雨: 深紫黑(#0c0a1a) + 6条流星动画
   + 10个静态星点 + 渐变菱形logo + 极简布局
This commit is contained in:
logan committed 2026-05-27 12:48:27 +08:00
1 parent 46054c8b2f
commit 1aa729b61b
5 files changed
+776 -432

No files matched your search

+108 -73
View File
@@ -5,95 +5,98 @@ export default function CenteredCard() {
return ( return (
<div <div
className="flex items-center justify-center min-h-screen relative" style={{
style={{ padding: 24 }} position: 'relative',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minHeight: '100vh',
padding: 24,
overflow: 'hidden',
background: '#0a0a2e',
}}
> >
{/* Full-screen dark gradient background with noise texture */} {/* Aurora band 1 — Emerald green */}
<div <div
style={{ style={{
position: 'fixed', position: 'fixed',
inset: 0, top: '20%',
zIndex: 0, left: '-10%',
background: ` width: 600,
radial-gradient(ellipse at 50% 40%, #0a0a1a 0%, #1a0a2e 70%, #0f0520 100%) height: 200,
`, background: '#00d4aa',
}}
/>
{/* Subtle noise / dot pattern overlay */}
<div
style={{
position: 'fixed',
inset: 0,
zIndex: 1,
background: `
radial-gradient(circle at 20% 80%, rgba(99, 102, 241, 0.04) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(139, 92, 246, 0.05) 0%, transparent 50%),
radial-gradient(circle at 50% 50%, rgba(99,102,241,0.02) 0%, transparent 80%)
`,
}}
/>
{/* Extra floating glow orbs */}
<div
style={{
position: 'fixed',
top: '10%',
left: '60%',
width: 400,
height: 400,
borderRadius: '50%', borderRadius: '50%',
background: 'radial-gradient(circle, rgba(99,102,241,0.06), transparent 70%)', filter: 'blur(80px)',
opacity: 0.2,
zIndex: 1, zIndex: 1,
filter: 'blur(40px)', animation: 'aurora-flow-1 12s ease-in-out infinite',
}}
/>
<div
style={{
position: 'fixed',
bottom: '5%',
right: '60%',
width: 300,
height: 300,
borderRadius: '50%',
background: 'radial-gradient(circle, rgba(139,92,246,0.05), transparent 70%)',
zIndex: 1,
filter: 'blur(40px)',
}} }}
/> />
{/* Center glassmorphism card */} {/* Aurora band 2 — Indigo */}
<div
style={{
position: 'fixed',
top: '40%',
left: '30%',
width: 500,
height: 180,
background: '#4f46e5',
borderRadius: '50%',
filter: 'blur(80px)',
opacity: 0.25,
zIndex: 1,
animation: 'aurora-flow-2 15s ease-in-out infinite',
}}
/>
{/* Aurora band 3 — Purple */}
<div
style={{
position: 'fixed',
top: '60%',
left: '50%',
width: 550,
height: 160,
background: '#a855f7',
borderRadius: '50%',
filter: 'blur(80px)',
opacity: 0.15,
zIndex: 1,
animation: 'aurora-flow-3 18s ease-in-out infinite',
}}
/>
{/* Glassmorphism card */}
<div <div
style={{ style={{
position: 'relative', position: 'relative',
zIndex: 10, zIndex: 10,
width: '100%', width: '100%',
maxWidth: 420, maxWidth: 420,
background: 'rgba(255,255,255,0.03)', background: 'rgba(255,255,255,0.04)',
backdropFilter: 'blur(24px)', backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(24px)', WebkitBackdropFilter: 'blur(20px)',
border: '1px solid rgba(255,255,255,0.06)',
borderRadius: 20, borderRadius: 20,
padding: '48px 36px', padding: '48px 36px',
boxShadow: ` boxShadow: '0 8px 40px rgba(0,0,0,0.4), 0 0 80px rgba(99,102,241,0.08)',
0 0 0 1px rgba(255,255,255,0.06),
0 8px 40px rgba(0,0,0,0.4),
0 0 80px rgba(99,102,241,0.08)
`,
}} }}
> >
{/* Logo icon */} {/* Logo — 44x44 gradient rounded square with database icon */}
<div <div
style={{ style={{
width: 44, width: 44,
height: 44, height: 44,
borderRadius: 12, borderRadius: 12,
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', background: 'linear-gradient(135deg, #6366f1, #a855f7)',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
margin: '0 auto 28px', margin: '0 auto 24px',
boxShadow: '0 4px 20px rgba(99,102,241,0.35)',
}} }}
> >
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<ellipse cx="12" cy="5" rx="9" ry="3" /> <ellipse cx="12" cy="5" rx="9" ry="3" />
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" /> <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" /> <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
@@ -109,7 +112,7 @@ export default function CenteredCard() {
<form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: 16 }}> <form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
{error && ( {error && (
<div style={{ color: '#f87171', fontSize: 13, textAlign: 'center', padding: '10px 14px', background: 'rgba(248,113,113,0.08)', borderRadius: 8 }}> <div style={{ color: '#f87171', fontSize: 13, textAlign: 'center', padding: '10px 14px', background: 'rgba(248,113,113,0.08)', borderRadius: 10 }}>
{error} {error}
</div> </div>
)} )}
@@ -121,17 +124,25 @@ export default function CenteredCard() {
onChange={(e) => setUsername(e.target.value)} onChange={(e) => setUsername(e.target.value)}
className="w-full" className="w-full"
style={{ style={{
padding: '13px 16px', padding: '14px 18px',
borderRadius: 10, borderRadius: 12,
background: 'rgba(255,255,255,0.06)', background: 'rgba(255,255,255,0.06)',
border: '1px solid rgba(255,255,255,0.08)', border: '1px solid rgba(255,255,255,0.08)',
color: '#fff', color: '#fff',
fontSize: 14, fontSize: 14,
outline: 'none', outline: 'none',
transition: 'border-color 0.2s, background 0.2s', transition: 'border-color 0.3s, background 0.3s, box-shadow 0.3s',
}}
onFocus={(e) => {
e.target.style.borderColor = 'rgba(99,102,241,0.4)';
e.target.style.background = 'rgba(255,255,255,0.08)';
e.target.style.boxShadow = '0 0 0 3px rgba(99,102,241,0.1)';
}}
onBlur={(e) => {
e.target.style.borderColor = 'rgba(255,255,255,0.08)';
e.target.style.background = 'rgba(255,255,255,0.06)';
e.target.style.boxShadow = 'none';
}} }}
onFocus={(e) => { e.target.style.borderColor = 'rgba(99,102,241,0.4)'; e.target.style.background = 'rgba(255,255,255,0.08)'; }}
onBlur={(e) => { e.target.style.borderColor = 'rgba(255,255,255,0.08)'; e.target.style.background = 'rgba(255,255,255,0.06)'; }}
/> />
<input <input
type="password" type="password"
@@ -140,17 +151,25 @@ export default function CenteredCard() {
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
className="w-full" className="w-full"
style={{ style={{
padding: '13px 16px', padding: '14px 18px',
borderRadius: 10, borderRadius: 12,
background: 'rgba(255,255,255,0.06)', background: 'rgba(255,255,255,0.06)',
border: '1px solid rgba(255,255,255,0.08)', border: '1px solid rgba(255,255,255,0.08)',
color: '#fff', color: '#fff',
fontSize: 14, fontSize: 14,
outline: 'none', outline: 'none',
transition: 'border-color 0.2s, background 0.2s', transition: 'border-color 0.3s, background 0.3s, box-shadow 0.3s',
}}
onFocus={(e) => {
e.target.style.borderColor = 'rgba(99,102,241,0.4)';
e.target.style.background = 'rgba(255,255,255,0.08)';
e.target.style.boxShadow = '0 0 0 3px rgba(99,102,241,0.1)';
}}
onBlur={(e) => {
e.target.style.borderColor = 'rgba(255,255,255,0.08)';
e.target.style.background = 'rgba(255,255,255,0.06)';
e.target.style.boxShadow = 'none';
}} }}
onFocus={(e) => { e.target.style.borderColor = 'rgba(99,102,241,0.4)'; e.target.style.background = 'rgba(255,255,255,0.08)'; }}
onBlur={(e) => { e.target.style.borderColor = 'rgba(255,255,255,0.08)'; e.target.style.background = 'rgba(255,255,255,0.06)'; }}
/> />
<button <button
@@ -158,8 +177,8 @@ export default function CenteredCard() {
disabled={loading || !username || !password} disabled={loading || !username || !password}
className="dg-gradient-bg w-full" className="dg-gradient-bg w-full"
style={{ style={{
padding: '13px 20px', padding: '14px 20px',
borderRadius: 10, borderRadius: 12,
border: 'none', border: 'none',
color: '#fff', color: '#fff',
fontWeight: 600, fontWeight: 600,
@@ -175,6 +194,22 @@ export default function CenteredCard() {
</button> </button>
</form> </form>
</div> </div>
{/* Aurora keyframe animations */}
<style>{`
@keyframes aurora-flow-1 {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(120px); }
}
@keyframes aurora-flow-2 {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(-100px); }
}
@keyframes aurora-flow-3 {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(80px); }
}
`}</style>
</div> </div>
); );
} }
+131 -77
View File
@@ -8,81 +8,112 @@ export default function FullImage() {
className="flex items-center justify-center min-h-screen relative" className="flex items-center justify-center min-h-screen relative"
style={{ padding: 24, overflow: 'hidden' }} style={{ padding: 24, overflow: 'hidden' }}
> >
{/* Deep dark background #0d1117 */} {/* Deep navy background */}
<div <div
style={{ style={{
position: 'fixed', position: 'fixed',
inset: 0, inset: 0,
zIndex: 0, zIndex: 0,
background: '#0d1117', background: '#0a1628',
}} }}
/> />
{/* Particle grid — dots and lines */} {/* Star field — 30+ individual stars */}
<div <div style={{ position: 'fixed', inset: 0, zIndex: 1 }}>
style={{ {/* Row 1 - top area */}
position: 'fixed', <div style={{ position: 'absolute', top: '3%', left: '12%', width: 3, height: 3, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.3))', animation: 'star-twinkle 3s ease-in-out infinite' }} />
inset: 0, <div style={{ position: 'absolute', top: '5%', left: '28%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, rgba(147,197,253,0.8), rgba(147,197,253,0.2))' }} />
zIndex: 1, <div style={{ position: 'absolute', top: '2%', left: '45%', width: 3.5, height: 3.5, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))', animation: 'star-twinkle 4s ease-in-out 1s infinite' }} />
backgroundImage: ` <div style={{ position: 'absolute', top: '8%', left: '62%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, rgba(167,139,250,0.8), rgba(167,139,250,0.2))' }} />
radial-gradient(circle at 15% 25%, rgba(99,102,241,0.15) 1px, transparent 1px), <div style={{ position: 'absolute', top: '4%', left: '78%', width: 2.5, height: 2.5, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.3))', animation: 'star-twinkle 5s ease-in-out 2s infinite' }} />
radial-gradient(circle at 35% 60%, rgba(99,102,241,0.12) 1px, transparent 1px), <div style={{ position: 'absolute', top: '6%', left: '91%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, rgba(147,197,253,0.8), rgba(147,197,253,0.2))', animation: 'star-twinkle 4.2s ease-in-out 0.6s infinite' }} />
radial-gradient(circle at 55% 15%, rgba(139,92,246,0.12) 1px, transparent 1px),
radial-gradient(circle at 75% 45%, rgba(99,102,241,0.10) 1px, transparent 1px),
radial-gradient(circle at 85% 70%, rgba(139,92,246,0.10) 1px, transparent 1px),
radial-gradient(circle at 25% 80%, rgba(99,102,241,0.08) 1px, transparent 1px),
radial-gradient(circle at 65% 85%, rgba(139,92,246,0.08) 1px, transparent 1px),
radial-gradient(circle at 45% 40%, rgba(99,102,241,0.14) 1px, transparent 1px),
radial-gradient(circle at 10% 55%, rgba(139,92,246,0.06) 1px, transparent 1px),
radial-gradient(circle at 90% 20%, rgba(99,102,241,0.08) 1px, transparent 1px),
radial-gradient(circle at 5% 90%, rgba(139,92,246,0.07) 1px, transparent 1px),
radial-gradient(circle at 50% 70%, rgba(99,102,241,0.06) 1px, transparent 1px)
`,
backgroundSize: '200px 200px',
}}
/>
{/* Connecting grid lines */} {/* Row 2 */}
<div <div style={{ position: 'absolute', top: '14%', left: '7%', width: 2.5, height: 2.5, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.3))' }} />
style={{ <div style={{ position: 'absolute', top: '16%', left: '22%', width: 3, height: 3, borderRadius: '50%', background: 'radial-gradient(circle, rgba(147,197,253,0.8), rgba(147,197,253,0.2))', animation: 'star-twinkle 4.5s ease-in-out 0.5s infinite' }} />
position: 'fixed', <div style={{ position: 'absolute', top: '12%', left: '55%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))' }} />
inset: 0, <div style={{ position: 'absolute', top: '18%', left: '70%', width: 3, height: 3, borderRadius: '50%', background: 'radial-gradient(circle, rgba(167,139,250,0.8), rgba(167,139,250,0.2))', animation: 'star-twinkle 3.5s ease-in-out 1.5s infinite' }} />
zIndex: 1, <div style={{ position: 'absolute', top: '15%', left: '88%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))' }} />
backgroundImage: `
linear-gradient(rgba(99,102,241,0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(99,102,241,0.03) 1px, transparent 1px)
`,
backgroundSize: '80px 80px',
}}
/>
{/* Large glow orb — top right */} {/* Row 3 */}
<div style={{ position: 'absolute', top: '25%', left: '5%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))', animation: 'star-twinkle 5s ease-in-out 3s infinite' }} />
<div style={{ position: 'absolute', top: '28%', left: '35%', width: 3, height: 3, borderRadius: '50%', background: 'radial-gradient(circle, rgba(147,197,253,0.8), rgba(147,197,253,0.3))' }} />
<div style={{ position: 'absolute', top: '22%', left: '50%', width: 2.5, height: 2.5, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))', animation: 'star-twinkle 4s ease-in-out 2s infinite' }} />
<div style={{ position: 'absolute', top: '30%', left: '82%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, rgba(167,139,250,0.8), rgba(167,139,250,0.2))' }} />
{/* Row 4 — mid area */}
<div style={{ position: 'absolute', top: '38%', left: '15%', width: 2.5, height: 2.5, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))' }} />
<div style={{ position: 'absolute', top: '42%', left: '48%', width: 3.5, height: 3.5, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.3))', animation: 'star-twinkle 6s ease-in-out 1s infinite' }} />
<div style={{ position: 'absolute', top: '36%', left: '75%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, rgba(147,197,253,0.8), rgba(147,197,253,0.2))' }} />
<div style={{ position: 'absolute', top: '44%', left: '92%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))', animation: 'star-twinkle 3s ease-in-out 4s infinite' }} />
{/* Row 5 */}
<div style={{ position: 'absolute', top: '55%', left: '8%', width: 3, height: 3, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.3))', animation: 'star-twinkle 4s ease-in-out 2.5s infinite' }} />
<div style={{ position: 'absolute', top: '52%', left: '40%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, rgba(167,139,250,0.8), rgba(167,139,250,0.2))' }} />
<div style={{ position: 'absolute', top: '58%', left: '60%', width: 2.5, height: 2.5, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))', animation: 'star-twinkle 5.5s ease-in-out 0.8s infinite' }} />
<div style={{ position: 'absolute', top: '50%', left: '85%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, rgba(147,197,253,0.8), rgba(147,197,253,0.2))' }} />
{/* Row 6 — lower area */}
<div style={{ position: 'absolute', top: '68%', left: '18%', width: 2.5, height: 2.5, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))' }} />
<div style={{ position: 'absolute', top: '72%', left: '52%', width: 3, height: 3, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.3))', animation: 'star-twinkle 4.5s ease-in-out 3.5s infinite' }} />
<div style={{ position: 'absolute', top: '65%', left: '72%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, rgba(147,197,253,0.8), rgba(147,197,253,0.2))' }} />
{/* Row 7 — bottom area */}
<div style={{ position: 'absolute', top: '80%', left: '10%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))', animation: 'star-twinkle 3.5s ease-in-out 1.2s infinite' }} />
<div style={{ position: 'absolute', top: '85%', left: '38%', width: 2.5, height: 2.5, borderRadius: '50%', background: 'radial-gradient(circle, rgba(167,139,250,0.8), rgba(167,139,250,0.2))' }} />
<div style={{ position: 'absolute', top: '78%', left: '65%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.3))', animation: 'star-twinkle 5s ease-in-out 0.3s infinite' }} />
<div style={{ position: 'absolute', top: '88%', left: '82%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, rgba(147,197,253,0.8), rgba(147,197,253,0.2))' }} />
{/* Row 8 — very bottom */}
<div style={{ position: 'absolute', top: '93%', left: '25%', width: 2, height: 2, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))' }} />
<div style={{ position: 'absolute', top: '95%', left: '58%', width: 2.5, height: 2.5, borderRadius: '50%', background: 'radial-gradient(circle, #fff, rgba(255,255,255,0.2))', animation: 'star-twinkle 4s ease-in-out 2.2s infinite' }} />
</div>
{/* Constellation lines connecting adjacent stars */}
<div style={{ position: 'fixed', inset: 0, zIndex: 1 }}>
{/* Line 1: top 3%/12% → 5%/28% → 2%/45% */}
<div style={{ position: 'absolute', top: '4%', left: '12%', width: '34%', height: 1, background: 'linear-gradient(90deg, rgba(147,197,253,0.18), rgba(167,139,250,0.1), transparent)', transform: 'rotate(-3deg)' }} />
{/* Line 2: top 16%/22% → 12%/55% */}
<div style={{ position: 'absolute', top: '13%', left: '22%', width: '35%', height: 1, background: 'linear-gradient(90deg, rgba(147,197,253,0.15), rgba(167,139,250,0.08), transparent)', transform: 'rotate(-8deg)' }} />
{/* Line 3: top 42%/48% → 36%/75% → 30%/82% */}
<div style={{ position: 'absolute', top: '38%', left: '48%', width: '30%', height: 1, background: 'linear-gradient(90deg, rgba(147,197,253,0.15), rgba(167,139,250,0.08), transparent)', transform: 'rotate(-12deg)' }} />
{/* Line 4: top 55%/8% → 52%/40% */}
<div style={{ position: 'absolute', top: '53%', left: '8%', width: '34%', height: 1, background: 'linear-gradient(90deg, rgba(147,197,253,0.12), rgba(167,139,250,0.06), transparent)', transform: 'rotate(5deg)' }} />
{/* Line 5: top 72%/52% → 78%/65% */}
<div style={{ position: 'absolute', top: '74%', left: '52%', width: '15%', height: 1, background: 'linear-gradient(90deg, rgba(147,197,253,0.12), rgba(167,139,250,0.05), transparent)', transform: 'rotate(20deg)' }} />
{/* Line 6 (vertical): top 5%/28% → 16%/22% */}
<div style={{ position: 'absolute', top: '5%', left: '27%', width: 1, height: '12%', background: 'linear-gradient(180deg, rgba(147,197,253,0.12), rgba(167,139,250,0.05), transparent)', transform: 'rotate(5deg)' }} />
</div>
{/* Large pulsating glow orb — top right (blue-purple) */}
<div <div
style={{ style={{
position: 'fixed', position: 'fixed',
top: '-15%', top: '-15%',
right: '-10%', right: '-8%',
width: 600, width: 500,
height: 600, height: 500,
borderRadius: '50%', borderRadius: '50%',
background: 'radial-gradient(circle, rgba(99,102,241,0.12) 0%, rgba(139,92,246,0.06) 40%, transparent 70%)', background: 'radial-gradient(circle, rgba(99,102,241,0.25) 0%, rgba(168,85,247,0.1) 40%, transparent 70%)',
zIndex: 1, zIndex: 1,
filter: 'blur(60px)', filter: 'blur(80px)',
animation: 'star-glow-pulse 8s ease-in-out infinite',
}} }}
/> />
{/* Secondary glow — bottom left */} {/* Smaller warm glow orb — bottom left */}
<div <div
style={{ style={{
position: 'fixed', position: 'fixed',
bottom: '-20%', bottom: '-10%',
left: '-10%', left: '-5%',
width: 400, width: 300,
height: 400, height: 300,
borderRadius: '50%', borderRadius: '50%',
background: 'radial-gradient(circle, rgba(139,92,246,0.06), transparent 70%)', background: 'radial-gradient(circle, rgba(249,115,22,0.12) 0%, transparent 70%)',
zIndex: 1, zIndex: 1,
filter: 'blur(40px)', filter: 'blur(60px)',
opacity: 0.08,
}} }}
/> />
@@ -93,55 +124,51 @@ export default function FullImage() {
zIndex: 10, zIndex: 10,
width: '100%', width: '100%',
maxWidth: 400, maxWidth: 400,
background: 'rgba(13, 17, 23, 0.8)', background: 'rgba(10, 22, 40, 0.7)',
backdropFilter: 'blur(20px)', backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(20px)', WebkitBackdropFilter: 'blur(20px)',
border: '1px solid rgba(255,255,255,0.06)',
borderRadius: 16, borderRadius: 16,
padding: '40px 32px', padding: '40px 32px',
boxShadow: ` boxShadow: '0 0 0 1px rgba(255,255,255,0.06), 0 8px 40px rgba(0,0,0,0.5)',
0 0 0 1px rgba(255,255,255,0.08),
0 8px 40px rgba(0,0,0,0.5),
0 0 100px rgba(99,102,241,0.04)
`,
}} }}
> >
{/* Small brand mark at top */} {/* Small brand mark */}
<div style={{ textAlign: 'center', marginBottom: 32 }}> <div style={{ textAlign: 'center', marginBottom: 28 }}>
<div <div
style={{ style={{
width: 36, width: 36,
height: 36, height: 36,
borderRadius: 10, borderRadius: 10,
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', background: 'linear-gradient(135deg, #6366f1, #a855f7)',
display: 'inline-flex', display: 'inline-flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
marginBottom: 20, marginBottom: 18,
boxShadow: '0 2px 12px rgba(99,102,241,0.3)',
}} }}
> >
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<ellipse cx="12" cy="5" rx="9" ry="3" /> <ellipse cx="12" cy="5" rx="9" ry="3" />
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" /> <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" /> <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
</svg> </svg>
</div> </div>
<h1 style={{ fontSize: 20, fontWeight: 600, color: '#fff', marginBottom: 4 }}> <h1 style={{ fontSize: 20, fontWeight: 600, color: '#fff', marginBottom: 4, textAlign: 'center' }}>
SQL审核平台 SQL审核平台
</h1> </h1>
<p style={{ color: 'rgba(255,255,255,0.4)', fontSize: 13 }}> <p style={{ color: 'rgba(255,255,255,0.4)', fontSize: 13, textAlign: 'center' }}>
登录您的工作区 登录您的工作区
</p> </p>
</div> </div>
<form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}> <form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
{error && ( {error && (
<div style={{ color: '#f87171', fontSize: 13, textAlign: 'center', padding: '10px 14px', background: 'rgba(248,113,113,0.06)', borderRadius: 8 }}> <div style={{ color: '#f87171', fontSize: 13, textAlign: 'center', padding: '10px 14px', background: 'rgba(248,113,113,0.06)', borderRadius: 10 }}>
{error} {error}
</div> </div>
)} )}
{/* Dark inset inputs */} {/* Inset-style input: username */}
<input <input
type="text" type="text"
placeholder="用户名" placeholder="用户名"
@@ -150,18 +177,26 @@ export default function FullImage() {
className="w-full" className="w-full"
style={{ style={{
padding: '12px 16px', padding: '12px 16px',
borderRadius: 8, borderRadius: 10,
background: 'rgba(255,255,255,0.04)', background: 'rgba(255,255,255,0.04)',
border: '1px solid rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.06)',
color: '#fff', color: '#fff',
fontSize: 14, fontSize: 14,
outline: 'none', outline: 'none',
transition: 'border-color 0.2s, background 0.2s', transition: 'border-color 0.3s, box-shadow 0.3s',
boxShadow: 'inset 0 1px 3px rgba(0,0,0,0.2)', boxShadow: 'inset 0 1px 3px rgba(0,0,0,0.2)',
}} }}
onFocus={(e) => { e.target.style.borderColor = 'rgba(99,102,241,0.3)'; e.target.style.background = 'rgba(255,255,255,0.06)'; }} onFocus={(e) => {
onBlur={(e) => { e.target.style.borderColor = 'rgba(255,255,255,0.06)'; e.target.style.background = 'rgba(255,255,255,0.04)'; }} e.target.style.borderColor = 'rgba(99,102,241,0.4)';
e.target.style.boxShadow = 'inset 0 1px 3px rgba(0,0,0,0.2), 0 0 0 3px rgba(99,102,241,0.1)';
}}
onBlur={(e) => {
e.target.style.borderColor = 'rgba(255,255,255,0.06)';
e.target.style.boxShadow = 'inset 0 1px 3px rgba(0,0,0,0.2)';
}}
/> />
{/* Inset-style input: password */}
<input <input
type="password" type="password"
placeholder="密码" placeholder="密码"
@@ -170,33 +205,40 @@ export default function FullImage() {
className="w-full" className="w-full"
style={{ style={{
padding: '12px 16px', padding: '12px 16px',
borderRadius: 8, borderRadius: 10,
background: 'rgba(255,255,255,0.04)', background: 'rgba(255,255,255,0.04)',
border: '1px solid rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.06)',
color: '#fff', color: '#fff',
fontSize: 14, fontSize: 14,
outline: 'none', outline: 'none',
transition: 'border-color 0.2s, background 0.2s', transition: 'border-color 0.3s, box-shadow 0.3s',
boxShadow: 'inset 0 1px 3px rgba(0,0,0,0.2)', boxShadow: 'inset 0 1px 3px rgba(0,0,0,0.2)',
}} }}
onFocus={(e) => { e.target.style.borderColor = 'rgba(99,102,241,0.3)'; e.target.style.background = 'rgba(255,255,255,0.06)'; }} onFocus={(e) => {
onBlur={(e) => { e.target.style.borderColor = 'rgba(255,255,255,0.06)'; e.target.style.background = 'rgba(255,255,255,0.04)'; }} e.target.style.borderColor = 'rgba(99,102,241,0.4)';
e.target.style.boxShadow = 'inset 0 1px 3px rgba(0,0,0,0.2), 0 0 0 3px rgba(99,102,241,0.1)';
}}
onBlur={(e) => {
e.target.style.borderColor = 'rgba(255,255,255,0.06)';
e.target.style.boxShadow = 'inset 0 1px 3px rgba(0,0,0,0.2)';
}}
/> />
{/* Full-width submit button */}
<button <button
type="submit" type="submit"
disabled={loading || !username || !password} disabled={loading || !username || !password}
className="dg-gradient-bg w-full" className="dg-gradient-bg w-full"
style={{ style={{
padding: '12px 20px', padding: '12px 20px',
borderRadius: 8, borderRadius: 10,
border: 'none', border: 'none',
color: '#fff', color: '#fff',
fontWeight: 600, fontWeight: 600,
fontSize: 14, fontSize: 14,
cursor: loading || !username || !password ? 'not-allowed' : 'pointer', cursor: loading || !username || !password ? 'not-allowed' : 'pointer',
opacity: loading || !username || !password ? 0.5 : 1, opacity: loading || !username || !password ? 0.5 : 1,
marginTop: 6, marginTop: 4,
transition: 'opacity 0.2s, transform 0.1s', transition: 'opacity 0.2s, transform 0.1s',
}} }}
> >
@@ -204,6 +246,18 @@ export default function FullImage() {
</button> </button>
</form> </form>
</div> </div>
{/* Keyframe animations */}
<style>{`
@keyframes star-twinkle {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
@keyframes star-glow-pulse {
0%, 100% { transform: scale(1); opacity: 0.7; }
50% { transform: scale(1.1); opacity: 1; }
}
`}</style>
</div> </div>
); );
} }
+216 -109
View File
@@ -1,123 +1,230 @@
import { useLogin } from '@/hooks/useLogin'; import { useLogin } from '@/hooks/useLogin';
/* ---------- static star positions (generated once) ---------- */
const STARS = [
{ top: '8%', left: '15%', size: 2, opacity: 0.25 },
{ top: '12%', left: '72%', size: 1, opacity: 0.15 },
{ top: '25%', left: '45%', size: 3, opacity: 0.10 },
{ top: '18%', left: '88%', size: 1, opacity: 0.35 },
{ top: '38%', left: '22%', size: 2, opacity: 0.20 },
{ top: '55%', left: '68%', size: 1, opacity: 0.30 },
{ top: '62%', left: '35%', size: 2, opacity: 0.15 },
{ top: '75%', left: '80%', size: 3, opacity: 0.12 },
{ top: '82%', left: '50%', size: 1, opacity: 0.40 },
{ top: '90%', left: '10%', size: 2, opacity: 0.18 },
];
/* ---------- meteor configs ---------- */
const METEORS = [
{ width: 140, height: 1, duration: '1.5s', delay: '0s', top: '-20px', right: '10%' },
{ width: 120, height: 2, duration: '1.2s', delay: '2s', top: '-60px', right: '30%' },
{ width: 150, height: 1, duration: '1.8s', delay: '4s', top: '-10px', right: '55%' },
{ width: 110, height: 2, duration: '2s', delay: '6.5s', top: '-80px', right: '20%' },
{ width: 130, height: 1, duration: '1.4s', delay: '9s', top: '-40px', right: '70%' },
{ width: 100, height: 1, duration: '1.6s', delay: '12s', top: '-30px', right: '45%' },
];
const KEYFRAMES = METEORS.map((_, i) => `
@keyframes meteor-shoot-${i + 1} {
0% {
opacity: 0;
transform: translate(100vw, -100px) rotate(-35deg);
}
10% {
opacity: 1;
}
100% {
opacity: 0;
transform: translate(-100px, 100vh) rotate(-35deg);
}
}
`).join('\n');
export default function MinimalDark() { export default function MinimalDark() {
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin(); const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
return ( return (
<div <>
className="flex flex-col items-center justify-center min-h-screen" {/* Inject keyframes once */}
style={{ background: '#0a0a0a', padding: 24 }} <style>{KEYFRAMES}</style>
>
<div style={{ width: '100%', maxWidth: 320, textAlign: 'center' }}>
{/* Tiny gradient dot logo */}
<div
style={{
width: 16,
height: 16,
borderRadius: '50%',
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
margin: '0 auto 24px',
boxShadow: '0 0 12px rgba(99,102,241,0.3)',
}}
/>
{/* Brand name with wide tracking */} <div
<h1 style={{
className="tracking-wider" position: 'relative',
style={{ fontSize: 15, fontWeight: 500, color: 'rgba(255,255,255,0.8)', letterSpacing: '3px', marginBottom: 0 }} display: 'flex',
> flexDirection: 'column',
SQL审核平台 alignItems: 'center',
</h1> justifyContent: 'center',
minHeight: '100vh',
{/* Thin divider */} padding: 24,
<div background: 'radial-gradient(ellipse at 50% 50%, #151030 0%, #0c0a1a 70%)',
style={{ overflow: 'hidden',
width: 40, }}
height: 1, >
background: 'rgba(255,255,255,0.1)', {/* -------- Static star dots -------- */}
margin: '32px auto', {STARS.map((s, i) => (
}} <div
/> key={`star-${i}`}
<form
onSubmit={handleLogin}
style={{ display: 'flex', flexDirection: 'column' }}
>
{error && (
<div style={{ color: '#f87171', fontSize: 13, marginBottom: 20, textAlign: 'left' }}>
{error}
</div>
)}
{/* Borderless inputs — bottom line only */}
<div style={{ marginBottom: 24 }}>
<input
type="text"
placeholder="用户名"
value={username}
onChange={(e) => setUsername(e.target.value)}
className="w-full"
style={{
padding: '12px 0',
background: 'transparent',
border: 'none',
borderBottom: '1px solid rgba(255,255,255,0.1)',
borderRadius: 0,
color: '#fff',
fontSize: 14,
outline: 'none',
transition: 'border-color 0.25s',
}}
onFocus={(e) => { e.target.style.borderBottomColor = 'rgba(99,102,241,0.5)'; }}
onBlur={(e) => { e.target.style.borderBottomColor = 'rgba(255,255,255,0.1)'; }}
/>
</div>
<div style={{ marginBottom: 40 }}>
<input
type="password"
placeholder="密码"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full"
style={{
padding: '12px 0',
background: 'transparent',
border: 'none',
borderBottom: '1px solid rgba(255,255,255,0.1)',
borderRadius: 0,
color: '#fff',
fontSize: 14,
outline: 'none',
transition: 'border-color 0.25s',
}}
onFocus={(e) => { e.target.style.borderBottomColor = 'rgba(99,102,241,0.5)'; }}
onBlur={(e) => { e.target.style.borderBottomColor = 'rgba(255,255,255,0.1)'; }}
/>
</div>
{/* Full-width slim button */}
<button
type="submit"
disabled={loading || !username || !password}
className="dg-gradient-bg w-full"
style={{ style={{
padding: '12px 20px', position: 'absolute',
borderRadius: 6, top: s.top,
border: 'none', left: s.left,
color: '#fff', width: s.size,
height: s.size,
borderRadius: '50%',
background: '#fff',
opacity: s.opacity,
pointerEvents: 'none',
}}
/>
))}
{/* -------- Shooting meteors -------- */}
{METEORS.map((m, i) => (
<div
key={`meteor-${i}`}
style={{
position: 'absolute',
top: m.top,
right: m.right,
width: m.width,
height: m.height,
background: 'linear-gradient(to left, rgba(255,255,255,0.8), transparent)',
transform: 'rotate(-35deg)',
animation: `meteor-shoot-${i + 1} ${m.duration} ${m.delay} linear infinite forwards`,
pointerEvents: 'none',
}}
/>
))}
{/* -------- Central content card -------- */}
<div style={{ width: '100%', maxWidth: 320, textAlign: 'center', position: 'relative', zIndex: 1 }}>
{/* Gradient diamond */}
<div
style={{
width: 20,
height: 20,
background: 'linear-gradient(135deg, #6366f1, #a855f7)',
borderRadius: 4,
transform: 'rotate(45deg)',
margin: '0 auto 24px',
boxShadow: '0 0 16px rgba(99,102,241,0.35)',
}}
/>
{/* Brand */}
<h1
style={{
fontSize: 15,
fontWeight: 500, fontWeight: 500,
fontSize: 14, letterSpacing: '3px',
cursor: loading || !username || !password ? 'not-allowed' : 'pointer', color: 'rgba(255,255,255,0.8)',
opacity: loading || !username || !password ? 0.4 : 1, margin: 0,
transition: 'opacity 0.2s, transform 0.1s',
letterSpacing: '0.5px',
}} }}
> >
{loading ? '登录中…' : '登录'} SQL审核平台
</button> </h1>
</form>
{/* Thin divider */}
<div
style={{
width: 40,
height: 1,
background: 'rgba(255,255,255,0.1)',
margin: '32px auto',
}}
/>
{/* Form */}
<form
onSubmit={handleLogin}
style={{ display: 'flex', flexDirection: 'column' }}
>
{error && (
<div style={{ color: '#f87171', fontSize: 13, marginBottom: 20, textAlign: 'left' }}>
{error}
</div>
)}
{/* Username */}
<div style={{ marginBottom: 24 }}>
<input
type="text"
placeholder="用户名"
value={username}
onChange={(e) => setUsername(e.target.value)}
className="w-full"
style={{
padding: '12px 0',
background: 'transparent',
border: 'none',
borderBottom: '1px solid rgba(255,255,255,0.1)',
borderRadius: 0,
color: '#fff',
fontSize: 14,
outline: 'none',
transition: 'border-color 0.25s',
}}
onFocus={(e) => {
e.target.style.borderBottomColor = 'rgba(99,102,241,0.5)';
}}
onBlur={(e) => {
e.target.style.borderBottomColor = 'rgba(255,255,255,0.1)';
}}
/>
</div>
{/* Password */}
<div style={{ marginBottom: 40 }}>
<input
type="password"
placeholder="密码"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full"
style={{
padding: '12px 0',
background: 'transparent',
border: 'none',
borderBottom: '1px solid rgba(255,255,255,0.1)',
borderRadius: 0,
color: '#fff',
fontSize: 14,
outline: 'none',
transition: 'border-color 0.25s',
}}
onFocus={(e) => {
e.target.style.borderBottomColor = 'rgba(99,102,241,0.5)';
}}
onBlur={(e) => {
e.target.style.borderBottomColor = 'rgba(255,255,255,0.1)';
}}
/>
</div>
{/* Submit */}
<button
type="submit"
disabled={loading || !username || !password}
className="dg-gradient-bg w-full"
style={{
padding: 12,
borderRadius: 6,
border: 'none',
color: '#fff',
fontWeight: 500,
fontSize: 14,
cursor: loading || !username || !password ? 'not-allowed' : 'pointer',
opacity: loading || !username || !password ? 0.4 : 1,
transition: 'opacity 0.2s, transform 0.1s',
letterSpacing: '0.5px',
}}
>
{loading ? '登录中…' : '登录'}
</button>
</form>
</div>
</div> </div>
</div> </>
); );
} }
+220 -71
View File
@@ -1,115 +1,262 @@
import { useLogin } from '@/hooks/useLogin'; import { useLogin } from '@/hooks/useLogin';
const features = ['SQL审核', '变更审批', '实时监控', '权限管理'];
export default function SidePanel() { export default function SidePanel() {
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin(); const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
/* 6 orbit light-points evenly spaced at 60° intervals on a 300px ring (radius 150px) */
const orbitDots = [
{ angle: 0, size: 8, opacity: 0.9 },
{ angle: 60, size: 6, opacity: 0.6 },
{ angle: 120, size: 7, opacity: 0.75 },
{ angle: 180, size: 6, opacity: 0.5 },
{ angle: 240, size: 8, opacity: 0.8 },
{ angle: 300, size: 6, opacity: 0.55 },
];
/* Scattered twinkling stars */
const stars = [
{ top: '6%', left: '12%', size: 3, delay: '0s', dur: '3s' },
{ top: '14%', left: '72%', size: 2, delay: '1.2s', dur: '4s' },
{ top: '25%', left: '38%', size: 4, delay: '0.5s', dur: '3.5s' },
{ top: '18%', left: '55%', size: 2, delay: '2s', dur: '5s' },
{ top: '60%', left: '15%', size: 3, delay: '1s', dur: '4.5s' },
{ top: '70%', left: '80%', size: 2, delay: '0.3s', dur: '3.2s' },
{ top: '82%', left: '30%', size: 4, delay: '1.8s', dur: '3.8s' },
{ top: '45%', left: '8%', size: 2, delay: '2.5s', dur: '4.2s' },
{ top: '35%', left: '88%', size: 3, delay: '0.8s', dur: '3.6s' },
{ top: '90%', left: '65%', size: 2, delay: '1.5s', dur: '5.2s' },
];
return ( return (
<div className="flex min-h-screen" style={{ background: 'var(--dg-bg-primary)', color: 'var(--dg-text-primary)' }}> <div className="flex min-h-screen" style={{ background: 'var(--dg-bg-primary)', color: 'var(--dg-text-primary)' }}>
{/* Left Panel — 38% Brand Area */} {/* ========== Left Panel — 40% Celestial Animation ========== */}
<div <div
className="relative flex flex-col justify-between"
style={{ style={{
width: '38%', position: 'relative',
background: 'linear-gradient(180deg, #111827 0%, #1f2937 100%)', width: '40%',
padding: '56px 44px', background: 'linear-gradient(180deg, #1a0a2e 0%, #0f172a 100%)',
overflow: 'hidden', overflow: 'hidden',
}} }}
> >
{/* Subtle decorative gradient orb */} {/* --- Core Celestial Orb (200px, centered-upper) --- */}
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: '-80px', top: '25%',
right: '-80px', left: '50%',
width: 280, transform: 'translate(-50%, -50%)',
height: 280, width: 200,
height: 200,
zIndex: 2,
}}
>
{/* Main orb */}
<div
style={{
width: 200,
height: 200,
borderRadius: '50%',
animation: 'celestial-orb-color 8s ease-in-out infinite',
}}
/>
</div>
{/* --- Orbit Ring (300px, dashed) with 6 light dots --- */}
<div
style={{
position: 'absolute',
top: '25%',
left: '50%',
width: 300,
height: 300,
marginTop: -150,
marginLeft: -150,
borderRadius: '50%', borderRadius: '50%',
background: 'radial-gradient(circle, rgba(99,102,241,0.08), transparent 70%)', border: '1px dashed rgba(255,255,255,0.1)',
filter: 'blur(30px)', animation: 'celestial-orbit-rotate 20s linear infinite',
zIndex: 3,
}}
>
{orbitDots.map((dot, i) => {
const rad = (dot.angle * Math.PI) / 180;
const r = 150; // radius of the 300px ring
const x = 150 + r * Math.cos(rad) - dot.size / 2;
const y = 150 + r * Math.sin(rad) - dot.size / 2;
return (
<div
key={i}
style={{
position: 'absolute',
left: x,
top: y,
width: dot.size,
height: dot.size,
borderRadius: '50%',
background: `rgba(255,255,255,${dot.opacity})`,
boxShadow: `0 0 6px rgba(255,255,255,${dot.opacity * 0.5})`,
}}
/>
);
})}
</div>
{/* --- 3 Meteors --- */}
{/* Meteor 1 */}
<div
style={{
position: 'absolute',
top: '8%',
left: '15%',
width: 2,
height: 60,
background: 'linear-gradient(to bottom right, rgba(255,255,255,0.9), transparent)',
borderRadius: 2,
transform: 'rotate(-45deg)',
animation: 'celestial-meteor-1 1.5s ease-in 3s infinite',
zIndex: 1,
}}
/>
{/* Meteor 2 */}
<div
style={{
position: 'absolute',
top: '5%',
left: '55%',
width: 1.5,
height: 50,
background: 'linear-gradient(to bottom right, rgba(255,255,255,0.7), transparent)',
borderRadius: 2,
transform: 'rotate(-40deg)',
animation: 'celestial-meteor-2 2s ease-in 7s infinite',
zIndex: 1,
}}
/>
{/* Meteor 3 */}
<div
style={{
position: 'absolute',
top: '20%',
left: '35%',
width: 1.5,
height: 55,
background: 'linear-gradient(to bottom right, rgba(255,255,255,0.8), transparent)',
borderRadius: 2,
transform: 'rotate(-50deg)',
animation: 'celestial-meteor-3 1.8s ease-in 11s infinite',
zIndex: 1,
}} }}
/> />
{/* Top brand section */} {/* --- Scattered Twinkling Stars --- */}
<div style={{ position: 'relative', zIndex: 2 }}> {stars.map((s, i) => (
{/* Logo mark */}
<div <div
key={`star-${i}`}
style={{ style={{
width: 40, position: 'absolute',
height: 40, top: s.top,
borderRadius: 10, left: s.left,
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', width: s.size,
display: 'flex', height: s.size,
alignItems: 'center', borderRadius: '50%',
justifyContent: 'center', background: 'rgba(255,255,255,0.7)',
marginBottom: 56, animation: `celestial-twinkle ${s.dur} ease-in-out ${s.delay} infinite`,
zIndex: 1,
}} }}
> />
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> ))}
<ellipse cx="12" cy="5" rx="9" ry="3" />
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
</svg>
</div>
{/* Stacked large text — left aligned */} {/* --- Bottom-left brand text --- */}
<div style={{ marginBottom: 24 }}> <div style={{ position: 'absolute', bottom: 48, left: 44, zIndex: 4 }}>
<div style={{ fontSize: 52, fontWeight: 700, color: '#fff', lineHeight: 1.1, letterSpacing: '-1px' }}> <div style={{ fontSize: 20, fontWeight: 600, color: 'rgba(255,255,255,0.7)', marginBottom: 6 }}>
SQL审核 SQL审核平台
</div> </div>
<div style={{ fontSize: 52, fontWeight: 700, color: '#fff', lineHeight: 1.1, letterSpacing: '-1px', marginTop: 4 }}> <div style={{ fontSize: 13, color: 'rgba(255,255,255,0.3)' }}>
平台 企业级数据库审核与合规管理
</div>
</div> </div>
<p style={{ color: 'rgba(255,255,255,0.4)', fontSize: 15, lineHeight: 1.6, maxWidth: 280 }}>
企业级数据库审核与合规管理解决方案,为团队提供安全可靠的数据管理体验。
</p>
</div> </div>
{/* Bottom feature pills */} {/* --- Keyframes --- */}
<div style={{ position: 'relative', zIndex: 2, display: 'flex', flexWrap: 'wrap', gap: 8 }}> <style>{`
{features.map((tag) => ( /* Celestial orb: sun ↔ moon color cycle */
<span @keyframes celestial-orb-color {
key={tag} 0%, 100% {
style={{ background: radial-gradient(circle, #fbbf24, #f97316);
padding: '6px 14px', box-shadow: 0 0 60px rgba(251,191,36,0.4);
borderRadius: 20, }
background: 'rgba(255,255,255,0.06)', 50% {
color: 'rgba(255,255,255,0.6)', background: radial-gradient(circle, #e2e8f0, #93c5fd);
fontSize: 12, box-shadow: 0 0 60px rgba(147,197,253,0.4);
fontWeight: 500, }
letterSpacing: '0.3px', }
}}
> /* Orbit ring rotation */
{tag} @keyframes celestial-orbit-rotate {
</span> from { transform: rotate(0deg); }
))} to { transform: rotate(360deg); }
</div> }
/* Meteor 1 */
@keyframes celestial-meteor-1 {
0% { opacity: 0; transform: rotate(-45deg) translate(0, 0); }
10% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; transform: rotate(-45deg) translate(180px, 180px); }
}
/* Meteor 2 */
@keyframes celestial-meteor-2 {
0% { opacity: 0; transform: rotate(-40deg) translate(0, 0); }
10% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; transform: rotate(-40deg) translate(160px, 160px); }
}
/* Meteor 3 */
@keyframes celestial-meteor-3 {
0% { opacity: 0; transform: rotate(-50deg) translate(0, 0); }
10% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; transform: rotate(-50deg) translate(170px, 170px); }
}
/* Star twinkle */
@keyframes celestial-twinkle {
0%, 100% { opacity: 0.2; transform: scale(1); }
50% { opacity: 1; transform: scale(1.4); }
}
`}</style>
</div> </div>
{/* Right Panel — 62% Login Form */} {/* ========== Right Panel — 60% Login Form ========== */}
<div <div
className="flex items-center justify-center" className="flex items-center justify-center"
style={{ flex: 1, padding: 64 }} style={{ width: '60%', background: 'var(--dg-bg-primary)' }}
> >
<form <form
onSubmit={handleLogin} onSubmit={handleLogin}
style={{ width: '100%', maxWidth: 380, display: 'flex', flexDirection: 'column' }} style={{ width: '100%', maxWidth: 400, display: 'flex', flexDirection: 'column' }}
> >
<h2 style={{ fontSize: 30, fontWeight: 600, marginBottom: 8, color: 'var(--dg-text-primary)' }}> <h2 style={{ fontSize: 30, fontWeight: 700, marginBottom: 8, color: 'var(--dg-text-primary)' }}>
欢迎回来 欢迎回来
</h2> </h2>
<p style={{ color: 'var(--dg-text-muted)', fontSize: 15, marginBottom: 44 }}> <p style={{ color: 'var(--dg-text-muted)', fontSize: 15, marginBottom: 40 }}>
登录以继续 登录以继续
</p> </p>
{error && ( {error && (
<div style={{ color: '#f87171', fontSize: 13, marginBottom: 16, padding: '10px 14px', background: 'rgba(248,113,113,0.08)', borderRadius: 8 }}> <div style={{
color: '#f87171',
fontSize: 13,
marginBottom: 16,
padding: '10px 14px',
background: 'rgba(248,113,113,0.08)',
borderRadius: 10,
}}>
{error} {error}
</div> </div>
)} )}
{/* Username */}
<div style={{ marginBottom: 24 }}> <div style={{ marginBottom: 24 }}>
<label style={{ display: 'block', fontSize: 13, fontWeight: 500, color: 'var(--dg-text-secondary)', marginBottom: 8 }}> <label style={{ display: 'block', fontSize: 13, fontWeight: 500, color: 'var(--dg-text-secondary)', marginBottom: 8 }}>
用户名 用户名
@@ -128,14 +275,15 @@ export default function SidePanel() {
color: 'var(--dg-text-primary)', color: 'var(--dg-text-primary)',
fontSize: 14, fontSize: 14,
outline: 'none', outline: 'none',
transition: 'background 0.2s', transition: 'background 0.3s',
}} }}
onFocus={(e) => { e.target.style.background = 'var(--dg-bg-tertiary)'; }} onFocus={(e) => { e.target.style.background = 'var(--dg-bg-tertiary)'; }}
onBlur={(e) => { e.target.style.background = 'var(--dg-bg-secondary)'; }} onBlur={(e) => { e.target.style.background = 'var(--dg-bg-secondary)'; }}
/> />
</div> </div>
<div style={{ marginBottom: 36 }}> {/* Password */}
<div style={{ marginBottom: 32 }}>
<label style={{ display: 'block', fontSize: 13, fontWeight: 500, color: 'var(--dg-text-secondary)', marginBottom: 8 }}> <label style={{ display: 'block', fontSize: 13, fontWeight: 500, color: 'var(--dg-text-secondary)', marginBottom: 8 }}>
密码 密码
</label> </label>
@@ -153,13 +301,14 @@ export default function SidePanel() {
color: 'var(--dg-text-primary)', color: 'var(--dg-text-primary)',
fontSize: 14, fontSize: 14,
outline: 'none', outline: 'none',
transition: 'background 0.2s', transition: 'background 0.3s',
}} }}
onFocus={(e) => { e.target.style.background = 'var(--dg-bg-tertiary)'; }} onFocus={(e) => { e.target.style.background = 'var(--dg-bg-tertiary)'; }}
onBlur={(e) => { e.target.style.background = 'var(--dg-bg-secondary)'; }} onBlur={(e) => { e.target.style.background = 'var(--dg-bg-secondary)'; }}
/> />
</div> </div>
{/* Submit */}
<button <button
type="submit" type="submit"
disabled={loading || !username || !password} disabled={loading || !username || !password}
+101 -102
View File
@@ -4,109 +4,95 @@ export default function SplitView() {
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin(); const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
return ( return (
<div className="flex min-h-screen" style={{ background: 'var(--dg-bg-primary)', color: 'var(--dg-text-primary)' }}> <div
{/* Left Panel — 55% Brand Area */} className="flex min-h-screen"
style={{ background: 'var(--dg-bg-primary)', color: 'var(--dg-text-primary)' }}
>
{/* ─── Left Panel 55% — 星河流转 ─── */}
<div <div
className="relative overflow-hidden flex items-center justify-center" className="relative overflow-hidden flex items-center"
style={{ style={{
width: '55%', width: '55%',
background: 'linear-gradient(135deg, #0f172a 0%, #1e293b 100%)', background: 'linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%)',
padding: '48px 56px', padding: '48px 56px',
}} }}
> >
{/* Animated geometric decorations */} {/* Large pulsating glow halo — 200px */}
{/* Large circle — top right */}
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: '-60px', top: '50%',
right: '-40px', left: '50%',
width: 260, width: 200,
height: 260, height: 200,
borderRadius: '50%', borderRadius: '50%',
background: 'rgba(99, 102, 241, 0.08)', background: 'radial-gradient(circle, rgba(99,102,241,0.35) 0%, rgba(139,92,246,0.12) 40%, transparent 70%)',
animation: 'splitFloat1 8s ease-in-out infinite', transform: 'translate(-50%, -50%)',
opacity: 0.15,
animation: 'split-glow-pulse 6s ease-in-out infinite',
}} }}
/> />
{/* Medium circle — bottom left */}
{/* Meteor 1 */}
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
bottom: '40px', top: '-5%',
left: '30px', left: '60%',
width: 160, width: 2,
height: 160, height: 120,
borderRadius: '50%', background: 'linear-gradient(to bottom right, rgba(255,255,255,0.9), transparent)',
background: 'rgba(139, 92, 246, 0.06)', borderRadius: 2,
animation: 'splitFloat2 10s ease-in-out infinite', transform: 'rotate(-45deg)',
opacity: 0,
animation: 'split-meteor-1 4s ease-in 0s infinite',
}} }}
/> />
{/* Small circle — center upper */}
{/* Meteor 2 */}
<div
style={{
position: 'absolute',
top: '10%',
left: '35%',
width: 1.5,
height: 90,
background: 'linear-gradient(to bottom right, rgba(255,255,255,0.7), transparent)',
borderRadius: 2,
transform: 'rotate(-45deg)',
opacity: 0,
animation: 'split-meteor-2 5.5s ease-in 1.8s infinite',
}}
/>
{/* Meteor 3 */}
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: '25%', top: '25%',
right: '18%', left: '75%',
width: 48, width: 2,
height: 48, height: 100,
borderRadius: '50%', background: 'linear-gradient(to bottom right, rgba(255,255,255,0.6), transparent)',
background: 'rgba(99, 102, 241, 0.15)', borderRadius: 2,
animation: 'splitFloat3 6s ease-in-out infinite', transform: 'rotate(-45deg)',
}} opacity: 0,
/> animation: 'split-meteor-3 6.5s ease-in 3.5s infinite',
{/* Tiny dot */}
<div
style={{
position: 'absolute',
top: '65%',
left: '20%',
width: 10,
height: 10,
borderRadius: '50%',
background: 'rgba(255,255,255,0.12)',
animation: 'splitFloat1 7s ease-in-out infinite',
}}
/>
{/* Decorative lines */}
<div
style={{
position: 'absolute',
top: '15%',
left: '10%',
width: 120,
height: 1,
background: 'linear-gradient(90deg, rgba(99,102,241,0.3), transparent)',
transform: 'rotate(-25deg)',
}}
/>
<div
style={{
position: 'absolute',
bottom: '25%',
right: '10%',
width: 180,
height: 1,
background: 'linear-gradient(90deg, rgba(139,92,246,0.2), transparent)',
transform: 'rotate(15deg)',
}}
/>
{/* Another small ring */}
<div
style={{
position: 'absolute',
bottom: '15%',
right: '25%',
width: 32,
height: 32,
borderRadius: '50%',
border: '1px solid rgba(99,102,241,0.2)',
animation: 'splitFloat2 9s ease-in-out infinite',
}} }}
/> />
{/* Floating star dots (7) */}
<div style={{ position: 'absolute', top: '12%', left: '18%', width: 4, height: 4, borderRadius: '50%', background: '#fff', opacity: 0.5, animation: 'split-star-float 8s ease-in-out infinite' }} />
<div style={{ position: 'absolute', top: '28%', left: '72%', width: 2, height: 2, borderRadius: '50%', background: '#c7d2fe', opacity: 0.4, animation: 'split-star-float 10s ease-in-out 1s infinite' }} />
<div style={{ position: 'absolute', top: '45%', left: '8%', width: 6, height: 6, borderRadius: '50%', background: '#fff', opacity: 0.3, animation: 'split-star-float 7s ease-in-out 2s infinite' }} />
<div style={{ position: 'absolute', top: '60%', left: '82%', width: 3, height: 3, borderRadius: '50%', background: '#c7d2fe', opacity: 0.5, animation: 'split-star-float 9s ease-in-out 0.5s infinite' }} />
<div style={{ position: 'absolute', top: '75%', left: '30%', width: 5, height: 5, borderRadius: '50%', background: '#fff', opacity: 0.35, animation: 'split-star-float 11s ease-in-out 3s infinite' }} />
<div style={{ position: 'absolute', top: '18%', left: '48%', width: 3, height: 3, borderRadius: '50%', background: '#c7d2fe', opacity: 0.7, animation: 'split-star-float 6s ease-in-out 1.5s infinite' }} />
<div style={{ position: 'absolute', top: '88%', left: '55%', width: 2, height: 2, borderRadius: '50%', background: '#fff', opacity: 0.45, animation: 'split-star-float 12s ease-in-out 2.5s infinite' }} />
{/* Brand content */} {/* Brand content */}
<div style={{ position: 'relative', zIndex: 2, maxWidth: 420 }}> <div style={{ position: 'relative', zIndex: 2, maxWidth: 420 }}>
{/* Logo mark */} {/* Logo — 48×48 gradient square */}
<div <div
style={{ style={{
width: 48, width: 48,
@@ -117,49 +103,66 @@ export default function SplitView() {
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
marginBottom: 40, marginBottom: 40,
boxShadow: '0 4px 24px rgba(99,102,241,0.3)', boxShadow: '0 4px 24px rgba(99,102,241,0.35)',
}} }}
> >
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<ellipse cx="12" cy="5" rx="9" ry="3" /> <ellipse cx="12" cy="5" rx="9" ry="3" />
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" /> <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" /> <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
</svg> </svg>
</div> </div>
<h1 style={{ fontSize: 40, fontWeight: 700, color: '#fff', marginBottom: 16, letterSpacing: '-0.5px', lineHeight: 1.2 }}> <h1 style={{ fontSize: 40, fontWeight: 700, color: '#fff', marginBottom: 14, letterSpacing: '-0.5px', lineHeight: 1.15 }}>
SQL审核平台 SQL审核平台
</h1> </h1>
<p style={{ color: 'rgba(255,255,255,0.5)', fontSize: 16, lineHeight: 1.7, fontWeight: 400 }}> <p style={{ color: 'rgba(199,210,254,0.55)', fontSize: 15, lineHeight: 1.7 }}>
安全 · 高效 · 可信赖的企业级数据库管理 安全 · 高效 · 可信赖的企业级数据库管理
</p> </p>
</div> </div>
{/* Keyframe animations */} {/* ─── Keyframes ─── */}
<style>{` <style>{`
@keyframes splitFloat1 { @keyframes split-glow-pulse {
0%, 100% { transform: translateY(0); } 0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.15; }
50% { transform: translateY(-20px); } 50% { transform: translate(-50%, -50%) scale(1.2); opacity: 0.25; }
} }
@keyframes splitFloat2 { @keyframes split-meteor-1 {
0%, 100% { transform: translateY(0); } 0% { transform: rotate(-45deg) translate(0, 0); opacity: 0; }
50% { transform: translateY(15px); } 5% { opacity: 1; }
30% { opacity: 1; }
45% { transform: rotate(-45deg) translate(-320px, 320px); opacity: 0; }
100% { opacity: 0; }
} }
@keyframes splitFloat3 { @keyframes split-meteor-2 {
0%, 100% { transform: translate(0, 0); } 0% { transform: rotate(-45deg) translate(0, 0); opacity: 0; }
50% { transform: translate(8px, -12px); } 5% { opacity: 0.8; }
28% { opacity: 0.8; }
40% { transform: rotate(-45deg) translate(-260px, 260px); opacity: 0; }
100% { opacity: 0; }
}
@keyframes split-meteor-3 {
0% { transform: rotate(-45deg) translate(0, 0); opacity: 0; }
4% { opacity: 0.9; }
32% { opacity: 0.9; }
42% { transform: rotate(-45deg) translate(-280px, 280px); opacity: 0; }
100% { opacity: 0; }
}
@keyframes split-star-float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-12px); }
} }
`}</style> `}</style>
</div> </div>
{/* Right Panel — 45% Login Form */} {/* ─── Right Panel 45% — Login Form ─── */}
<div <div
className="flex items-center justify-center" className="flex items-center justify-center"
style={{ width: '45%', padding: 48 }} style={{ width: '45%', padding: 48, background: 'var(--dg-bg-primary)' }}
> >
<form <form
onSubmit={handleLogin} onSubmit={handleLogin}
style={{ width: '100%', maxWidth: 340, display: 'flex', flexDirection: 'column' }} style={{ width: '100%', maxWidth: 360, display: 'flex', flexDirection: 'column' }}
> >
<h2 style={{ fontSize: 28, fontWeight: 600, marginBottom: 8, color: 'var(--dg-text-primary)' }}> <h2 style={{ fontSize: 28, fontWeight: 600, marginBottom: 8, color: 'var(--dg-text-primary)' }}>
登录 登录
@@ -168,11 +171,7 @@ export default function SplitView() {
请输入您的账号信息以继续 请输入您的账号信息以继续
</p> </p>
{error && ( {error && <div style={{ color: '#f87171', fontSize: 13, marginBottom: 16 }}>{error}</div>}
<div style={{ color: '#f87171', fontSize: 13, marginBottom: 16, padding: '10px 14px', background: 'rgba(248,113,113,0.08)', borderRadius: 8 }}>
{error}
</div>
)}
<div style={{ marginBottom: 28 }}> <div style={{ marginBottom: 28 }}>
<label style={{ display: 'block', fontSize: 13, fontWeight: 500, color: 'var(--dg-text-secondary)', marginBottom: 8 }}> <label style={{ display: 'block', fontSize: 13, fontWeight: 500, color: 'var(--dg-text-secondary)', marginBottom: 8 }}>
@@ -193,7 +192,7 @@ export default function SplitView() {
color: 'var(--dg-text-primary)', color: 'var(--dg-text-primary)',
fontSize: 15, fontSize: 15,
outline: 'none', outline: 'none',
transition: 'border-color 0.2s', transition: 'border-color 0.3s',
}} }}
onFocus={(e) => { e.target.style.borderBottomColor = '#6366f1'; }} onFocus={(e) => { e.target.style.borderBottomColor = '#6366f1'; }}
onBlur={(e) => { e.target.style.borderBottomColor = 'var(--dg-border)'; }} onBlur={(e) => { e.target.style.borderBottomColor = 'var(--dg-border)'; }}
@@ -219,7 +218,7 @@ export default function SplitView() {
color: 'var(--dg-text-primary)', color: 'var(--dg-text-primary)',
fontSize: 15, fontSize: 15,
outline: 'none', outline: 'none',
transition: 'border-color 0.2s', transition: 'border-color 0.3s',
}} }}
onFocus={(e) => { e.target.style.borderBottomColor = '#6366f1'; }} onFocus={(e) => { e.target.style.borderBottomColor = '#6366f1'; }}
onBlur={(e) => { e.target.style.borderBottomColor = 'var(--dg-border)'; }} onBlur={(e) => { e.target.style.borderBottomColor = 'var(--dg-border)'; }}
@@ -239,7 +238,7 @@ export default function SplitView() {
fontSize: 15, fontSize: 15,
cursor: loading || !username || !password ? 'not-allowed' : 'pointer', cursor: loading || !username || !password ? 'not-allowed' : 'pointer',
opacity: loading || !username || !password ? 0.5 : 1, opacity: loading || !username || !password ? 0.5 : 1,
transition: 'opacity 0.2s, transform 0.1s', transition: 'opacity 0.2s',
}} }}
> >
{loading ? '登录中…' : '登录'} {loading ? '登录中…' : '登录'}