feat: 登录页3种风格全新重设计 — 数据晶格/晨曦微光/深海鲸歌
- SplitView: 晶格矩阵透视网格+19脉动节点+青绿微光, Tron科技感 - CenteredCard: 黎明渐变+10条金色光柱+地平线光晕+星点残留 - SidePanel: 深海渐变+5层波浪线+15生物发光粒子+3深海光柱 - AppearanceSettings: 预览颜色+缩略图同步更新 - LoginPage: 风格描述文案更新
This commit is contained in:
1 parent
439eb6d6d7
commit
03bf7c92a9
5 files changed
+562
-489
No files matched your search
@@ -3,6 +3,54 @@ import { useLogin } from '@/hooks/useLogin';
|
|||||||
export default function CenteredCard() {
|
export default function CenteredCard() {
|
||||||
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
|
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
|
||||||
|
|
||||||
|
// Light rays: 10 beams fanning out from bottom center
|
||||||
|
const rays = [
|
||||||
|
{ angle: -55, height: 70, opacity: 0.12, duration: 8 },
|
||||||
|
{ angle: -42, height: 85, opacity: 0.08, duration: 7 },
|
||||||
|
{ angle: -28, height: 60, opacity: 0.15, duration: 9 },
|
||||||
|
{ angle: -14, height: 90, opacity: 0.06, duration: 6 },
|
||||||
|
{ angle: -4, height: 75, opacity: 0.10, duration: 10 },
|
||||||
|
{ angle: 5, height: 80, opacity: 0.09, duration: 7 },
|
||||||
|
{ angle: 16, height: 65, opacity: 0.13, duration: 9 },
|
||||||
|
{ angle: 30, height: 88, opacity: 0.07, duration: 8 },
|
||||||
|
{ angle: 44, height: 72, opacity: 0.11, duration: 6 },
|
||||||
|
{ angle: 56, height: 55, opacity: 0.14, duration: 10 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Fading stars at the top
|
||||||
|
const stars = [
|
||||||
|
{ top: '6%', left: '15%', size: 2, opacity: 0.2, duration: 5 },
|
||||||
|
{ top: '10%', left: '72%', size: 3, opacity: 0.15, duration: 6 },
|
||||||
|
{ top: '3%', left: '45%', size: 2, opacity: 0.25, duration: 4.5 },
|
||||||
|
{ top: '14%', left: '30%', size: 2, opacity: 0.1, duration: 5.5 },
|
||||||
|
{ top: '8%', left: '88%', size: 3, opacity: 0.18, duration: 7 },
|
||||||
|
{ top: '5%', left: '60%', size: 2, opacity: 0.3, duration: 4 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const inputStyle = {
|
||||||
|
padding: '14px 18px',
|
||||||
|
borderRadius: 10,
|
||||||
|
background: 'rgba(255,255,255,0.04)',
|
||||||
|
border: '1px solid rgba(251,191,36,0.06)',
|
||||||
|
color: '#fef3c7',
|
||||||
|
fontSize: 14,
|
||||||
|
outline: 'none' as const,
|
||||||
|
width: '100%',
|
||||||
|
transition: 'border-color 0.3s, background 0.3s, box-shadow 0.3s',
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
|
||||||
|
e.target.style.borderColor = 'rgba(251,191,36,0.3)';
|
||||||
|
e.target.style.background = 'rgba(255,255,255,0.06)';
|
||||||
|
e.target.style.boxShadow = '0 0 0 3px rgba(251,191,36,0.08)';
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
|
||||||
|
e.target.style.borderColor = 'rgba(251,191,36,0.06)';
|
||||||
|
e.target.style.background = 'rgba(255,255,255,0.04)';
|
||||||
|
e.target.style.boxShadow = 'none';
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -13,59 +61,65 @@ export default function CenteredCard() {
|
|||||||
minHeight: '100vh',
|
minHeight: '100vh',
|
||||||
padding: 24,
|
padding: 24,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
background: '#0a0a2e',
|
background: 'linear-gradient(to bottom, #0a0a1a 0%, #0f1729 25%, #1a1035 50%, #2d1810 75%, #1a0f05 100%)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Aurora band 1 — Emerald green */}
|
{/* Dawn light rays */}
|
||||||
|
{rays.map((ray, i) => (
|
||||||
|
<div
|
||||||
|
key={`ray-${i}`}
|
||||||
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
bottom: 0,
|
||||||
|
left: '50%',
|
||||||
|
width: 120,
|
||||||
|
height: `${ray.height}vh`,
|
||||||
|
transform: `translateX(-50%) rotate(${ray.angle}deg)`,
|
||||||
|
transformOrigin: 'bottom center',
|
||||||
|
background: `linear-gradient(to top, rgba(251,191,36,${ray.opacity}), transparent)`,
|
||||||
|
clipPath: 'polygon(40% 100%, 60% 100%, 55% 0%, 45% 0%)',
|
||||||
|
opacity: ray.opacity,
|
||||||
|
zIndex: 1,
|
||||||
|
animation: `dawn-ray-pulse ${ray.duration}s ease-in-out infinite`,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Horizon glow */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
top: '20%',
|
bottom: -100,
|
||||||
left: '-10%',
|
left: '-10%',
|
||||||
width: 600,
|
width: '120%',
|
||||||
height: 200,
|
height: 200,
|
||||||
background: '#00d4aa',
|
background: 'radial-gradient(ellipse at center, rgba(251,191,36,0.12), transparent 70%)',
|
||||||
borderRadius: '50%',
|
zIndex: 2,
|
||||||
filter: 'blur(80px)',
|
animation: 'dawn-horizon-glow 8s ease-in-out infinite',
|
||||||
opacity: 0.2,
|
pointerEvents: 'none',
|
||||||
zIndex: 1,
|
|
||||||
animation: 'aurora-flow-1 12s ease-in-out infinite',
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Aurora band 2 — Indigo */}
|
{/* Stars — fading remnants of night */}
|
||||||
<div
|
{stars.map((star, i) => (
|
||||||
style={{
|
<div
|
||||||
position: 'fixed',
|
key={`star-${i}`}
|
||||||
top: '40%',
|
style={{
|
||||||
left: '30%',
|
position: 'fixed',
|
||||||
width: 500,
|
top: star.top,
|
||||||
height: 180,
|
left: star.left,
|
||||||
background: '#4f46e5',
|
width: star.size,
|
||||||
borderRadius: '50%',
|
height: star.size,
|
||||||
filter: 'blur(80px)',
|
borderRadius: '50%',
|
||||||
opacity: 0.25,
|
background: '#fff',
|
||||||
zIndex: 1,
|
opacity: star.opacity,
|
||||||
animation: 'aurora-flow-2 15s ease-in-out infinite',
|
zIndex: 1,
|
||||||
}}
|
animation: `dawn-star-fade ${star.duration}s ease-in-out infinite`,
|
||||||
/>
|
pointerEvents: 'none',
|
||||||
|
}}
|
||||||
{/* 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 */}
|
{/* Glassmorphism card */}
|
||||||
<div
|
<div
|
||||||
@@ -74,40 +128,46 @@ export default function CenteredCard() {
|
|||||||
zIndex: 10,
|
zIndex: 10,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
maxWidth: 420,
|
maxWidth: 420,
|
||||||
background: 'rgba(255,255,255,0.04)',
|
background: 'rgba(10,10,26,0.6)',
|
||||||
backdropFilter: 'blur(20px)',
|
backdropFilter: 'blur(20px)',
|
||||||
WebkitBackdropFilter: 'blur(20px)',
|
WebkitBackdropFilter: 'blur(20px)',
|
||||||
border: '1px solid rgba(255,255,255,0.06)',
|
border: '1px solid rgba(251,191,36,0.08)',
|
||||||
borderRadius: 20,
|
borderRadius: 20,
|
||||||
padding: '48px 36px',
|
padding: '48px 36px',
|
||||||
boxShadow: '0 8px 40px rgba(0,0,0,0.4), 0 0 80px rgba(99,102,241,0.08)',
|
boxShadow: '0 8px 40px rgba(0,0,0,0.4), 0 0 60px rgba(251,191,36,0.05)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Logo — 44x44 gradient rounded square with database icon */}
|
{/* Logo — 44x44 golden gradient with sunrise SVG icon */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: 44,
|
width: 44,
|
||||||
height: 44,
|
height: 44,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
background: 'linear-gradient(135deg, #6366f1, #a855f7)',
|
background: 'linear-gradient(135deg, #fbbf24, #f59e0b)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
margin: '0 auto 24px',
|
margin: '0 auto 24px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<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" />
|
<circle cx="12" cy="12" r="4" />
|
||||||
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
|
<line x1="12" y1="2" x2="12" y2="5" />
|
||||||
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
|
<line x1="12" y1="19" x2="12" y2="22" />
|
||||||
|
<line x1="4.93" y1="4.93" x2="7.05" y2="7.05" />
|
||||||
|
<line x1="16.95" y1="16.95" x2="19.07" y2="19.07" />
|
||||||
|
<line x1="2" y1="12" x2="5" y2="12" />
|
||||||
|
<line x1="19" y1="12" x2="22" y2="12" />
|
||||||
|
<line x1="4.93" y1="19.07" x2="7.05" y2="16.95" />
|
||||||
|
<line x1="16.95" y1="7.05" x2="19.07" y2="4.93" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 style={{ textAlign: 'center', fontSize: 22, fontWeight: 600, color: '#fff', marginBottom: 6 }}>
|
<h1 style={{ textAlign: 'center', fontSize: 22, fontWeight: 600, color: '#fef3c7', marginBottom: 6 }}>
|
||||||
SQL审核平台
|
SQL审核平台
|
||||||
</h1>
|
</h1>
|
||||||
<p style={{ textAlign: 'center', color: 'rgba(255,255,255,0.45)', fontSize: 14, marginBottom: 36 }}>
|
<p style={{ textAlign: 'center', color: 'rgba(251,191,36,0.5)', fontSize: 14, marginBottom: 36 }}>
|
||||||
登录您的工作区
|
守护每一次数据变更
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
<form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||||
@@ -123,26 +183,9 @@ export default function CenteredCard() {
|
|||||||
value={username}
|
value={username}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
style={{
|
style={inputStyle}
|
||||||
padding: '14px 18px',
|
onFocus={handleFocus}
|
||||||
borderRadius: 12,
|
onBlur={handleBlur}
|
||||||
background: 'rgba(255,255,255,0.06)',
|
|
||||||
border: '1px solid rgba(255,255,255,0.08)',
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 14,
|
|
||||||
outline: 'none',
|
|
||||||
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';
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
@@ -150,26 +193,9 @@ export default function CenteredCard() {
|
|||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
style={{
|
style={inputStyle}
|
||||||
padding: '14px 18px',
|
onFocus={handleFocus}
|
||||||
borderRadius: 12,
|
onBlur={handleBlur}
|
||||||
background: 'rgba(255,255,255,0.06)',
|
|
||||||
border: '1px solid rgba(255,255,255,0.08)',
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 14,
|
|
||||||
outline: 'none',
|
|
||||||
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';
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@@ -178,7 +204,7 @@ export default function CenteredCard() {
|
|||||||
className="dg-gradient-bg w-full"
|
className="dg-gradient-bg w-full"
|
||||||
style={{
|
style={{
|
||||||
padding: '14px 20px',
|
padding: '14px 20px',
|
||||||
borderRadius: 12,
|
borderRadius: 10,
|
||||||
border: 'none',
|
border: 'none',
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
@@ -187,7 +213,8 @@ export default function CenteredCard() {
|
|||||||
opacity: loading || !username || !password ? 0.5 : 1,
|
opacity: loading || !username || !password ? 0.5 : 1,
|
||||||
marginTop: 8,
|
marginTop: 8,
|
||||||
transition: 'opacity 0.2s, transform 0.1s',
|
transition: 'opacity 0.2s, transform 0.1s',
|
||||||
boxShadow: '0 4px 20px rgba(99,102,241,0.25)',
|
background: 'linear-gradient(135deg, #fbbf24, #f59e0b)',
|
||||||
|
boxShadow: '0 4px 20px rgba(251,191,36,0.25)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading ? '登录中…' : '登录'}
|
{loading ? '登录中…' : '登录'}
|
||||||
@@ -195,19 +222,23 @@ export default function CenteredCard() {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Aurora keyframe animations */}
|
{/* Dawn keyframe animations */}
|
||||||
<style>{`
|
<style>{`
|
||||||
@keyframes aurora-flow-1 {
|
@keyframes dawn-ray-pulse {
|
||||||
0%, 100% { transform: translateX(0); }
|
0%, 100% { opacity: 0.08; }
|
||||||
50% { transform: translateX(120px); }
|
50% { opacity: 0.15; }
|
||||||
}
|
}
|
||||||
@keyframes aurora-flow-2 {
|
@keyframes dawn-horizon-glow {
|
||||||
0%, 100% { transform: translateX(0); }
|
0%, 100% { opacity: 0.8; }
|
||||||
50% { transform: translateX(-100px); }
|
50% { opacity: 1; }
|
||||||
}
|
}
|
||||||
@keyframes aurora-flow-3 {
|
@keyframes dawn-star-fade {
|
||||||
0%, 100% { transform: translateX(0); }
|
0%, 100% { opacity: 0.1; }
|
||||||
50% { transform: translateX(80px); }
|
50% { opacity: 0.3; }
|
||||||
|
}
|
||||||
|
|
||||||
|
input::placeholder {
|
||||||
|
color: rgba(251,191,36,0.3);
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,262 +3,238 @@ import { useLogin } from '@/hooks/useLogin';
|
|||||||
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) */
|
/* 15 bioluminescent particles scattered across the deep-sea scene */
|
||||||
const orbitDots = [
|
const biolumParticles = [
|
||||||
{ angle: 0, size: 8, opacity: 0.9 },
|
{ top: '8%', left: '20%', size: 4, color: 'rgba(6,182,212,0.4)', delay: '0s', dur: '4s' },
|
||||||
{ angle: 60, size: 6, opacity: 0.6 },
|
{ top: '15%', left: '65%', size: 5, color: 'rgba(20,184,166,0.3)', delay: '1.2s', dur: '5s' },
|
||||||
{ angle: 120, size: 7, opacity: 0.75 },
|
{ top: '22%', left: '40%', size: 3, color: 'rgba(34,211,238,0.5)', delay: '0.5s', dur: '3.5s' },
|
||||||
{ angle: 180, size: 6, opacity: 0.5 },
|
{ top: '28%', left: '12%', size: 6, color: 'rgba(6,182,212,0.4)', delay: '2.8s', dur: '4.5s' },
|
||||||
{ angle: 240, size: 8, opacity: 0.8 },
|
{ top: '35%', left: '78%', size: 4, color: 'rgba(6,182,212,0.4)', delay: '3.5s', dur: '3s' },
|
||||||
{ angle: 300, size: 6, opacity: 0.55 },
|
{ top: '42%', left: '30%', size: 7, color: 'rgba(20,184,166,0.3)', delay: '1s', dur: '5.5s' },
|
||||||
|
{ top: '48%', left: '55%', size: 3, color: 'rgba(34,211,238,0.5)', delay: '4.2s', dur: '4s' },
|
||||||
|
{ top: '55%', left: '18%', size: 5, color: 'rgba(6,182,212,0.4)', delay: '0.8s', dur: '6s' },
|
||||||
|
{ top: '60%', left: '70%', size: 4, color: 'rgba(6,182,212,0.4)', delay: '5s', dur: '3.5s' },
|
||||||
|
{ top: '67%', left: '45%', size: 8, color: 'rgba(20,184,166,0.3)', delay: '2s', dur: '4.5s' },
|
||||||
|
{ top: '73%', left: '25%', size: 3, color: 'rgba(6,182,212,0.4)', delay: '6s', dur: '5s' },
|
||||||
|
{ top: '78%', left: '85%', size: 5, color: 'rgba(34,211,238,0.5)', delay: '1.5s', dur: '3s' },
|
||||||
|
{ top: '83%', left: '50%', size: 4, color: 'rgba(6,182,212,0.4)', delay: '7s', dur: '4.5s' },
|
||||||
|
{ top: '88%', left: '15%', size: 6, color: 'rgba(6,182,212,0.4)', delay: '3s', dur: '5.5s' },
|
||||||
|
{ top: '92%', left: '60%', size: 3, color: 'rgba(20,184,166,0.3)', delay: '8s', dur: '3.5s' },
|
||||||
];
|
];
|
||||||
|
|
||||||
/* Scattered twinkling stars */
|
/* 5 ocean wave layers */
|
||||||
const stars = [
|
const waveLayers = [
|
||||||
{ top: '6%', left: '12%', size: 3, delay: '0s', dur: '3s' },
|
{ top: '20%', height: 80, border: 'border-bottom', borderColor: 'rgba(6,182,212,0.06)', anim: 'ocean-wave-1 12s ease-in-out infinite' },
|
||||||
{ top: '14%', left: '72%', size: 2, delay: '1.2s', dur: '4s' },
|
{ top: '30%', height: 100, border: 'border-bottom', borderColor: 'rgba(20,184,166,0.08)', anim: 'ocean-wave-2 15s ease-in-out infinite' },
|
||||||
{ top: '25%', left: '38%', size: 4, delay: '0.5s', dur: '3.5s' },
|
{ top: '45%', height: 120, border: 'border-top', borderColor: 'rgba(6,182,212,0.10)', anim: 'ocean-wave-3 18s ease-in-out infinite' },
|
||||||
{ top: '18%', left: '55%', size: 2, delay: '2s', dur: '5s' },
|
{ top: '60%', height: 90, border: 'border-bottom', borderColor: 'rgba(20,184,166,0.06)', anim: 'ocean-wave-4 14s ease-in-out infinite' },
|
||||||
{ top: '60%', left: '15%', size: 3, delay: '1s', dur: '4.5s' },
|
{ top: '75%', height: 70, border: 'border-top', borderColor: 'rgba(6,182,212,0.04)', anim: 'ocean-wave-5 20s ease-in-out infinite' },
|
||||||
{ 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' },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/* Feature pills */
|
||||||
|
const featurePills = ['安全审核', '变更管控', '权限隔离'];
|
||||||
|
|
||||||
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">
|
||||||
{/* ========== Left Panel — 40% Celestial Animation ========== */}
|
{/* ========== Left 40% — Deep Sea Scene ========== */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
width: '40%',
|
width: '40%',
|
||||||
background: 'linear-gradient(180deg, #1a0a2e 0%, #0f172a 100%)',
|
background: 'linear-gradient(180deg, #020810 0%, #041420 30%, #062030 60%, #041820 100%)',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* --- Core Celestial Orb (200px, centered-upper) --- */}
|
{/* --- Deep-sea light shafts --- */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '25%',
|
top: 0,
|
||||||
left: '50%',
|
left: '25%',
|
||||||
transform: 'translate(-50%, -50%)',
|
width: 1,
|
||||||
width: 200,
|
height: '100%',
|
||||||
height: 200,
|
background: 'linear-gradient(to bottom, rgba(6,182,212,0.08), transparent)',
|
||||||
zIndex: 2,
|
animation: 'ocean-lightshaft 5s ease-in-out 0s infinite',
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* 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%',
|
|
||||||
border: '1px dashed rgba(255,255,255,0.1)',
|
|
||||||
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,
|
zIndex: 1,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* Meteor 2 */}
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '5%',
|
top: 0,
|
||||||
left: '55%',
|
left: '55%',
|
||||||
width: 1.5,
|
width: 2,
|
||||||
height: 50,
|
height: '100%',
|
||||||
background: 'linear-gradient(to bottom right, rgba(255,255,255,0.7), transparent)',
|
background: 'linear-gradient(to bottom, rgba(6,182,212,0.06), transparent)',
|
||||||
borderRadius: 2,
|
animation: 'ocean-lightshaft 7s ease-in-out 2s infinite',
|
||||||
transform: 'rotate(-40deg)',
|
|
||||||
animation: 'celestial-meteor-2 2s ease-in 7s infinite',
|
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* Meteor 3 */}
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '20%',
|
top: 0,
|
||||||
left: '35%',
|
left: '78%',
|
||||||
width: 1.5,
|
width: 1,
|
||||||
height: 55,
|
height: '100%',
|
||||||
background: 'linear-gradient(to bottom right, rgba(255,255,255,0.8), transparent)',
|
background: 'linear-gradient(to bottom, rgba(20,184,166,0.05), transparent)',
|
||||||
borderRadius: 2,
|
animation: 'ocean-lightshaft 4s ease-in-out 4s infinite',
|
||||||
transform: 'rotate(-50deg)',
|
|
||||||
animation: 'celestial-meteor-3 1.8s ease-in 11s infinite',
|
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* --- Scattered Twinkling Stars --- */}
|
{/* --- 5 Wave layers --- */}
|
||||||
{stars.map((s, i) => (
|
{waveLayers.map((wave, i) => (
|
||||||
<div
|
<div
|
||||||
key={`star-${i}`}
|
key={`wave-${i}`}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: s.top,
|
top: wave.top,
|
||||||
left: s.left,
|
left: '-50%',
|
||||||
width: s.size,
|
width: '200%',
|
||||||
height: s.size,
|
height: wave.height,
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
background: 'rgba(255,255,255,0.7)',
|
[wave.border]: `1px solid ${wave.borderColor}`,
|
||||||
animation: `celestial-twinkle ${s.dur} ease-in-out ${s.delay} infinite`,
|
animation: wave.anim,
|
||||||
zIndex: 1,
|
zIndex: 2,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* --- Bottom-left brand text --- */}
|
{/* --- 15 Bioluminescent particles --- */}
|
||||||
<div style={{ position: 'absolute', bottom: 48, left: 44, zIndex: 4 }}>
|
{biolumParticles.map((p, i) => (
|
||||||
<div style={{ fontSize: 20, fontWeight: 600, color: 'rgba(255,255,255,0.7)', marginBottom: 6 }}>
|
<div
|
||||||
|
key={`biolum-${i}`}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: p.top,
|
||||||
|
left: p.left,
|
||||||
|
width: p.size,
|
||||||
|
height: p.size,
|
||||||
|
borderRadius: '50%',
|
||||||
|
background: p.color,
|
||||||
|
boxShadow: `0 0 ${p.size * 2}px ${p.color}`,
|
||||||
|
animation: `ocean-biolum-pulse ${p.dur} ease-in-out ${p.delay} infinite`,
|
||||||
|
zIndex: 3,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* --- Brand content (left-aligned, bottom section) --- */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
zIndex: 10,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
height: '100%',
|
||||||
|
padding: '0 44px 56px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Main title */}
|
||||||
|
<div style={{ fontSize: 42, fontWeight: 700, color: 'rgba(6,182,212,0.9)', marginBottom: 12, lineHeight: 1.2 }}>
|
||||||
SQL审核平台
|
SQL审核平台
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 13, color: 'rgba(255,255,255,0.3)' }}>
|
{/* Subtitle */}
|
||||||
企业级数据库审核与合规管理
|
<div style={{ fontSize: 15, color: 'rgba(148,163,184,0.5)', marginBottom: 32 }}>
|
||||||
|
让数据安全如深海般沉静
|
||||||
|
</div>
|
||||||
|
{/* Feature pills */}
|
||||||
|
<div style={{ display: 'flex', gap: 10 }}>
|
||||||
|
{featurePills.map((pill) => (
|
||||||
|
<span
|
||||||
|
key={pill}
|
||||||
|
style={{
|
||||||
|
padding: '4px 12px',
|
||||||
|
borderRadius: 20,
|
||||||
|
background: 'rgba(6,182,212,0.08)',
|
||||||
|
color: 'rgba(6,182,212,0.5)',
|
||||||
|
fontSize: 11,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{pill}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* --- Keyframes --- */}
|
{/* --- Keyframes (ocean- prefix) --- */}
|
||||||
<style>{`
|
<style>{`
|
||||||
/* Celestial orb: sun ↔ moon color cycle */
|
@keyframes ocean-wave-1 {
|
||||||
@keyframes celestial-orb-color {
|
0%, 100% { transform: translateX(-30px); }
|
||||||
0%, 100% {
|
50% { transform: translateX(30px); }
|
||||||
background: radial-gradient(circle, #fbbf24, #f97316);
|
}
|
||||||
box-shadow: 0 0 60px rgba(251,191,36,0.4);
|
@keyframes ocean-wave-2 {
|
||||||
}
|
0%, 100% { transform: translateX(30px); }
|
||||||
50% {
|
50% { transform: translateX(-30px); }
|
||||||
background: radial-gradient(circle, #e2e8f0, #93c5fd);
|
}
|
||||||
box-shadow: 0 0 60px rgba(147,197,253,0.4);
|
@keyframes ocean-wave-3 {
|
||||||
}
|
0%, 100% { transform: translateX(-20px); }
|
||||||
|
50% { transform: translateX(20px); }
|
||||||
|
}
|
||||||
|
@keyframes ocean-wave-4 {
|
||||||
|
0%, 100% { transform: translateX(25px); }
|
||||||
|
50% { transform: translateX(-25px); }
|
||||||
|
}
|
||||||
|
@keyframes ocean-wave-5 {
|
||||||
|
0%, 100% { transform: translateX(-30px); }
|
||||||
|
50% { transform: translateX(30px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Orbit ring rotation */
|
@keyframes ocean-biolum-pulse {
|
||||||
@keyframes celestial-orbit-rotate {
|
0%, 100% { opacity: 0.1; transform: scale(1); }
|
||||||
from { transform: rotate(0deg); }
|
50% { opacity: 0.6; transform: scale(1.5); }
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Meteor 1 */
|
@keyframes ocean-lightshaft {
|
||||||
@keyframes celestial-meteor-1 {
|
0%, 100% { opacity: 0.3; }
|
||||||
0% { opacity: 0; transform: rotate(-45deg) translate(0, 0); }
|
50% { opacity: 0.8; }
|
||||||
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>
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ========== Right Panel — 60% Login Form ========== */}
|
{/* ========== Right 60% — Login Form ========== */}
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-center"
|
className="flex items-center justify-center"
|
||||||
style={{ width: '60%', background: 'var(--dg-bg-primary)' }}
|
style={{ width: '60%', background: '#060e18' }}
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={handleLogin}
|
onSubmit={handleLogin}
|
||||||
style={{ width: '100%', maxWidth: 400, display: 'flex', flexDirection: 'column' }}
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 400,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
padding: '0 40px',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<h2 style={{ fontSize: 30, fontWeight: 700, marginBottom: 8, color: 'var(--dg-text-primary)' }}>
|
{/* Form title */}
|
||||||
|
<h2 style={{ fontSize: 28, fontWeight: 700, marginBottom: 8, color: '#e2e8f0' }}>
|
||||||
欢迎回来
|
欢迎回来
|
||||||
</h2>
|
</h2>
|
||||||
<p style={{ color: 'var(--dg-text-muted)', fontSize: 15, marginBottom: 40 }}>
|
<p style={{ fontSize: 14, color: 'rgba(148,163,184,0.5)', marginBottom: 40 }}>
|
||||||
登录以继续
|
登录你的工作空间
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{/* Error message */}
|
||||||
{error && (
|
{error && (
|
||||||
<div style={{
|
<div style={{ color: '#f87171', marginBottom: 16 }}>
|
||||||
color: '#f87171',
|
|
||||||
fontSize: 13,
|
|
||||||
marginBottom: 16,
|
|
||||||
padding: '10px 14px',
|
|
||||||
background: 'rgba(248,113,113,0.08)',
|
|
||||||
borderRadius: 10,
|
|
||||||
}}>
|
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Username */}
|
{/* Username field */}
|
||||||
<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: 12,
|
||||||
|
color: 'rgba(6,182,212,0.5)',
|
||||||
|
marginBottom: 8,
|
||||||
|
fontWeight: 500,
|
||||||
|
}}
|
||||||
|
>
|
||||||
用户名
|
用户名
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -270,21 +246,35 @@ export default function SidePanel() {
|
|||||||
style={{
|
style={{
|
||||||
padding: '13px 16px',
|
padding: '13px 16px',
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
background: 'var(--dg-bg-secondary)',
|
background: 'rgba(6,30,50,0.6)',
|
||||||
border: 'none',
|
border: '1px solid rgba(6,182,212,0.08)',
|
||||||
color: 'var(--dg-text-primary)',
|
color: '#e2e8f0',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
transition: 'background 0.3s',
|
transition: 'border-color 0.3s, background 0.3s',
|
||||||
|
}}
|
||||||
|
onFocus={(e) => {
|
||||||
|
e.target.style.borderColor = 'rgba(6,182,212,0.3)';
|
||||||
|
e.target.style.background = 'rgba(6,30,50,0.8)';
|
||||||
|
}}
|
||||||
|
onBlur={(e) => {
|
||||||
|
e.target.style.borderColor = 'rgba(6,182,212,0.08)';
|
||||||
|
e.target.style.background = 'rgba(6,30,50,0.6)';
|
||||||
}}
|
}}
|
||||||
onFocus={(e) => { e.target.style.background = 'var(--dg-bg-tertiary)'; }}
|
|
||||||
onBlur={(e) => { e.target.style.background = 'var(--dg-bg-secondary)'; }}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Password */}
|
{/* Password field */}
|
||||||
<div style={{ marginBottom: 32 }}>
|
<div style={{ marginBottom: 32 }}>
|
||||||
<label style={{ display: 'block', fontSize: 13, fontWeight: 500, color: 'var(--dg-text-secondary)', marginBottom: 8 }}>
|
<label
|
||||||
|
style={{
|
||||||
|
display: 'block',
|
||||||
|
fontSize: 12,
|
||||||
|
color: 'rgba(6,182,212,0.5)',
|
||||||
|
marginBottom: 8,
|
||||||
|
fontWeight: 500,
|
||||||
|
}}
|
||||||
|
>
|
||||||
密码
|
密码
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -296,19 +286,25 @@ export default function SidePanel() {
|
|||||||
style={{
|
style={{
|
||||||
padding: '13px 16px',
|
padding: '13px 16px',
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
background: 'var(--dg-bg-secondary)',
|
background: 'rgba(6,30,50,0.6)',
|
||||||
border: 'none',
|
border: '1px solid rgba(6,182,212,0.08)',
|
||||||
color: 'var(--dg-text-primary)',
|
color: '#e2e8f0',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
transition: 'background 0.3s',
|
transition: 'border-color 0.3s, background 0.3s',
|
||||||
|
}}
|
||||||
|
onFocus={(e) => {
|
||||||
|
e.target.style.borderColor = 'rgba(6,182,212,0.3)';
|
||||||
|
e.target.style.background = 'rgba(6,30,50,0.8)';
|
||||||
|
}}
|
||||||
|
onBlur={(e) => {
|
||||||
|
e.target.style.borderColor = 'rgba(6,182,212,0.08)';
|
||||||
|
e.target.style.background = 'rgba(6,30,50,0.6)';
|
||||||
}}
|
}}
|
||||||
onFocus={(e) => { e.target.style.background = 'var(--dg-bg-tertiary)'; }}
|
|
||||||
onBlur={(e) => { e.target.style.background = 'var(--dg-bg-secondary)'; }}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Submit */}
|
{/* Submit button */}
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading || !username || !password}
|
disabled={loading || !username || !password}
|
||||||
@@ -323,6 +319,8 @@ export default function SidePanel() {
|
|||||||
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, transform 0.1s',
|
||||||
|
background: 'linear-gradient(135deg, #06b6d4, #14b8a6)',
|
||||||
|
boxShadow: '0 4px 20px rgba(6,182,212,0.2)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading ? '登录中…' : '登录'}
|
{loading ? '登录中…' : '登录'}
|
||||||
|
|||||||
@@ -1,227 +1,251 @@
|
|||||||
import { useLogin } from '@/hooks/useLogin';
|
import { useLogin } from '@/hooks/useLogin';
|
||||||
|
|
||||||
|
/* ── Lattice node positions (percentage-based) ── */
|
||||||
|
const LATTICE_NODES = [
|
||||||
|
{ top: '15%', left: '12%', size: 5, delay: 0, duration: 2.5 },
|
||||||
|
{ top: '15%', left: '42%', size: 4, delay: 0.6, duration: 3 },
|
||||||
|
{ top: '15%', left: '72%', size: 5, delay: 1.2, duration: 2.8 },
|
||||||
|
{ top: '30%', left: '12%', size: 4, delay: 0.3, duration: 3.2 },
|
||||||
|
{ top: '30%', left: '42%', size: 6, delay: 0.9, duration: 2.6 },
|
||||||
|
{ top: '30%', left: '72%', size: 4, delay: 1.5, duration: 3.5 },
|
||||||
|
{ top: '45%', left: '12%', size: 5, delay: 0.2, duration: 2.4 },
|
||||||
|
{ top: '45%', left: '42%', size: 4, delay: 1.1, duration: 3.8 },
|
||||||
|
{ top: '45%', left: '72%', size: 5, delay: 0.7, duration: 2.9 },
|
||||||
|
{ top: '60%', left: '12%', size: 4, delay: 1.4, duration: 3.1 },
|
||||||
|
{ top: '60%', left: '42%', size: 5, delay: 0.4, duration: 2.7 },
|
||||||
|
{ top: '60%', left: '72%', size: 4, delay: 1.0, duration: 3.4 },
|
||||||
|
{ top: '75%', left: '27%', size: 6, delay: 0.5, duration: 2.3 },
|
||||||
|
{ top: '75%', left: '57%', size: 5, delay: 1.3, duration: 3.6 },
|
||||||
|
{ top: '85%', left: '42%', size: 4, delay: 0.8, duration: 2.2 },
|
||||||
|
{ top: '22%', left: '27%', size: 4, delay: 1.6, duration: 3.3 },
|
||||||
|
{ top: '22%', left: '57%', size: 5, delay: 0.1, duration: 2.1 },
|
||||||
|
{ top: '52%', left: '27%', size: 4, delay: 1.7, duration: 4.0 },
|
||||||
|
{ top: '52%', left: '57%', size: 5, delay: 0.35, duration: 2.5 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const INPUT_STYLE: React.CSSProperties = {
|
||||||
|
padding: '12px 0',
|
||||||
|
background: 'transparent',
|
||||||
|
border: 'none',
|
||||||
|
borderBottom: '1px solid rgba(148,163,184,0.15)',
|
||||||
|
borderRadius: 0,
|
||||||
|
color: '#e2e8f0',
|
||||||
|
fontSize: 14,
|
||||||
|
outline: 'none',
|
||||||
|
width: '100%',
|
||||||
|
transition: 'border-color 0.3s',
|
||||||
|
};
|
||||||
|
|
||||||
|
const LABEL_STYLE: React.CSSProperties = {
|
||||||
|
display: 'block',
|
||||||
|
fontSize: 12,
|
||||||
|
color: 'rgba(148,163,184,0.5)',
|
||||||
|
marginBottom: 8,
|
||||||
|
};
|
||||||
|
|
||||||
export default function SplitView() {
|
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
|
<div className="flex min-h-screen" style={{ fontFamily: 'system-ui, -apple-system, sans-serif' }}>
|
||||||
className="flex min-h-screen"
|
{/* ═══════════════════════════════════════════════════════════════
|
||||||
style={{ background: 'var(--dg-bg-primary)', color: 'var(--dg-text-primary)' }}
|
Left Panel 55% — 晶格矩阵区
|
||||||
>
|
═══════════════════════════════════════════════════════════════ */}
|
||||||
{/* ─── Left Panel 55% — 星河流转 ─── */}
|
|
||||||
<div
|
<div
|
||||||
className="relative overflow-hidden flex items-center"
|
className="relative overflow-hidden"
|
||||||
style={{
|
style={{
|
||||||
width: '55%',
|
width: '55%',
|
||||||
background: 'linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%)',
|
background: '#080c14',
|
||||||
padding: '48px 56px',
|
display: 'flex',
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
padding: '0 56px 80px 56px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Large pulsating glow halo — 200px */}
|
{/* ── Perspective Grid ── */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '50%',
|
inset: 0,
|
||||||
left: '50%',
|
perspective: '500px',
|
||||||
width: 200,
|
overflow: 'hidden',
|
||||||
height: 200,
|
}}
|
||||||
|
>
|
||||||
|
{/* Horizontal lines (11) */}
|
||||||
|
{Array.from({ length: 11 }, (_, i) => (
|
||||||
|
<div
|
||||||
|
key={`h-${i}`}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: `${8 + i * 8}%`,
|
||||||
|
height: 1,
|
||||||
|
background: 'rgba(0, 212, 170, 0.08)',
|
||||||
|
transform: 'rotateX(60deg)',
|
||||||
|
transformOrigin: 'center center',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{/* Vertical lines (11) */}
|
||||||
|
{Array.from({ length: 11 }, (_, i) => (
|
||||||
|
<div
|
||||||
|
key={`v-${i}`}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: `${8 + i * 8}%`,
|
||||||
|
width: 1,
|
||||||
|
background: 'rgba(0, 212, 170, 0.08)',
|
||||||
|
transform: 'rotateX(60deg)',
|
||||||
|
transformOrigin: 'center center',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ── Lattice Glow Nodes ── */}
|
||||||
|
{LATTICE_NODES.map((n, i) => (
|
||||||
|
<div
|
||||||
|
key={`node-${i}`}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: n.top,
|
||||||
|
left: n.left,
|
||||||
|
width: n.size,
|
||||||
|
height: n.size,
|
||||||
|
borderRadius: '50%',
|
||||||
|
background: '#00d4aa',
|
||||||
|
boxShadow: '0 0 8px rgba(0,212,170,0.6)',
|
||||||
|
animation: `lattice-node-pulse ${n.duration}s ease-in-out ${n.delay}s infinite`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* ── Right-Bottom Glow Decoration ── */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
right: -60,
|
||||||
|
bottom: -60,
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
background: 'radial-gradient(circle, rgba(99,102,241,0.35) 0%, rgba(139,92,246,0.12) 40%, transparent 70%)',
|
background: 'radial-gradient(circle, #00d4aa, transparent)',
|
||||||
transform: 'translate(-50%, -50%)',
|
opacity: 0.06,
|
||||||
opacity: 0.15,
|
animation: 'lattice-glow-drift 15s ease-in-out infinite',
|
||||||
animation: 'split-glow-pulse 6s ease-in-out infinite',
|
pointerEvents: 'none',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Meteor 1 */}
|
{/* ── Brand Content (centered-low, above grid) ── */}
|
||||||
<div
|
<div style={{ position: 'relative', zIndex: 10, maxWidth: 400 }}>
|
||||||
style={{
|
{/* Logo — 48×48 gradient square with hexagon icon */}
|
||||||
position: 'absolute',
|
|
||||||
top: '-5%',
|
|
||||||
left: '60%',
|
|
||||||
width: 2,
|
|
||||||
height: 120,
|
|
||||||
background: 'linear-gradient(to bottom right, rgba(255,255,255,0.9), transparent)',
|
|
||||||
borderRadius: 2,
|
|
||||||
transform: 'rotate(-45deg)',
|
|
||||||
opacity: 0,
|
|
||||||
animation: 'split-meteor-1 4s ease-in 0s infinite',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 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
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: '25%',
|
|
||||||
left: '75%',
|
|
||||||
width: 2,
|
|
||||||
height: 100,
|
|
||||||
background: 'linear-gradient(to bottom right, rgba(255,255,255,0.6), transparent)',
|
|
||||||
borderRadius: 2,
|
|
||||||
transform: 'rotate(-45deg)',
|
|
||||||
opacity: 0,
|
|
||||||
animation: 'split-meteor-3 6.5s ease-in 3.5s 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 */}
|
|
||||||
<div style={{ position: 'relative', zIndex: 2, maxWidth: 420 }}>
|
|
||||||
{/* Logo — 48×48 gradient square */}
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
|
background: 'linear-gradient(135deg, #00d4aa, #0ea5e9)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
marginBottom: 40,
|
marginBottom: 32,
|
||||||
boxShadow: '0 4px 24px rgba(99,102,241,0.35)',
|
boxShadow: '0 4px 24px rgba(0,212,170,0.3)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg width="24" height="24" 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="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<ellipse cx="12" cy="5" rx="9" ry="3" />
|
<path d="M12 2L22 8.5V15.5L12 22L2 15.5V8.5L12 2Z" />
|
||||||
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
|
<path d="M12 22V15.5" />
|
||||||
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
|
<path d="M22 8.5L12 15.5L2 8.5" />
|
||||||
|
<path d="M2 15.5L12 8.5L22 15.5" />
|
||||||
|
<line x1="12" y1="2" x2="12" y2="8.5" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 style={{ fontSize: 40, fontWeight: 700, color: '#fff', marginBottom: 14, letterSpacing: '-0.5px', lineHeight: 1.15 }}>
|
<h1 style={{ fontSize: 36, fontWeight: 700, color: '#e2e8f0', marginBottom: 10, letterSpacing: '-0.5px', lineHeight: 1.2 }}>
|
||||||
SQL审核平台
|
SQL审核平台
|
||||||
</h1>
|
</h1>
|
||||||
<p style={{ color: 'rgba(199,210,254,0.55)', fontSize: 15, lineHeight: 1.7 }}>
|
<p style={{ color: 'rgba(148,163,184,0.7)', fontSize: 14, marginBottom: 24, lineHeight: 1.6 }}>
|
||||||
安全 · 高效 · 可信赖的企业级数据库管理
|
数据安全的第一道防线
|
||||||
|
</p>
|
||||||
|
<p style={{ color: 'rgba(0,212,170,0.5)', fontSize: 12, letterSpacing: '0.5px' }}>
|
||||||
|
实时审核 · 智能分析 · 合规管控
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ─── Keyframes ─── */}
|
{/* ── Keyframes ── */}
|
||||||
<style>{`
|
<style>{`
|
||||||
@keyframes split-glow-pulse {
|
@keyframes lattice-node-pulse {
|
||||||
0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.15; }
|
0%, 100% {
|
||||||
50% { transform: translate(-50%, -50%) scale(1.2); opacity: 0.25; }
|
opacity: 0.2;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(1.3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@keyframes split-meteor-1 {
|
@keyframes lattice-glow-drift {
|
||||||
0% { transform: rotate(-45deg) translate(0, 0); opacity: 0; }
|
0%, 100% {
|
||||||
5% { opacity: 1; }
|
transform: translateX(-30px);
|
||||||
30% { opacity: 1; }
|
}
|
||||||
45% { transform: rotate(-45deg) translate(-320px, 320px); opacity: 0; }
|
50% {
|
||||||
100% { opacity: 0; }
|
transform: translateX(30px);
|
||||||
}
|
}
|
||||||
@keyframes split-meteor-2 {
|
|
||||||
0% { transform: rotate(-45deg) translate(0, 0); opacity: 0; }
|
|
||||||
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% — 登录表单区
|
||||||
|
═══════════════════════════════════════════════════════════════ */}
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-center"
|
className="flex items-center justify-center"
|
||||||
style={{ width: '45%', padding: 48, background: 'var(--dg-bg-primary)' }}
|
style={{
|
||||||
|
width: '45%',
|
||||||
|
background: '#0c1017',
|
||||||
|
padding: 48,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={handleLogin}
|
onSubmit={handleLogin}
|
||||||
style={{ width: '100%', maxWidth: 360, 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: 24, fontWeight: 600, color: '#e2e8f0', marginBottom: 8 }}>
|
||||||
登录
|
登录
|
||||||
</h2>
|
</h2>
|
||||||
<p style={{ color: 'var(--dg-text-muted)', fontSize: 14, marginBottom: 40 }}>
|
<p style={{ color: 'rgba(148,163,184,0.6)', fontSize: 14, marginBottom: 40 }}>
|
||||||
请输入您的账号信息以继续
|
访问你的工作空间
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{error && <div style={{ color: '#f87171', fontSize: 13, marginBottom: 16 }}>{error}</div>}
|
{error && <div style={{ color: '#f87171', fontSize: 13, marginBottom: 16 }}>{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={LABEL_STYLE}>用户名</label>
|
||||||
用户名
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="请输入用户名"
|
placeholder="请输入用户名"
|
||||||
value={username}
|
value={username}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
style={{
|
style={INPUT_STYLE}
|
||||||
padding: '12px 0',
|
onFocus={(e) => { e.target.style.borderBottomColor = '#00d4aa'; }}
|
||||||
background: 'transparent',
|
onBlur={(e) => { e.target.style.borderBottomColor = 'rgba(148,163,184,0.15)'; }}
|
||||||
border: 'none',
|
|
||||||
borderBottom: '1px solid var(--dg-border)',
|
|
||||||
borderRadius: 0,
|
|
||||||
color: 'var(--dg-text-primary)',
|
|
||||||
fontSize: 15,
|
|
||||||
outline: 'none',
|
|
||||||
transition: 'border-color 0.3s',
|
|
||||||
}}
|
|
||||||
onFocus={(e) => { e.target.style.borderBottomColor = '#6366f1'; }}
|
|
||||||
onBlur={(e) => { e.target.style.borderBottomColor = 'var(--dg-border)'; }}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ marginBottom: 36 }}>
|
<div style={{ marginBottom: 36 }}>
|
||||||
<label style={{ display: 'block', fontSize: 13, fontWeight: 500, color: 'var(--dg-text-secondary)', marginBottom: 8 }}>
|
<label style={LABEL_STYLE}>密码</label>
|
||||||
密码
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="请输入密码"
|
placeholder="请输入密码"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
style={{
|
style={INPUT_STYLE}
|
||||||
padding: '12px 0',
|
onFocus={(e) => { e.target.style.borderBottomColor = '#00d4aa'; }}
|
||||||
background: 'transparent',
|
onBlur={(e) => { e.target.style.borderBottomColor = 'rgba(148,163,184,0.15)'; }}
|
||||||
border: 'none',
|
|
||||||
borderBottom: '1px solid var(--dg-border)',
|
|
||||||
borderRadius: 0,
|
|
||||||
color: 'var(--dg-text-primary)',
|
|
||||||
fontSize: 15,
|
|
||||||
outline: 'none',
|
|
||||||
transition: 'border-color 0.3s',
|
|
||||||
}}
|
|
||||||
onFocus={(e) => { e.target.style.borderBottomColor = '#6366f1'; }}
|
|
||||||
onBlur={(e) => { e.target.style.borderBottomColor = 'var(--dg-border)'; }}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -233,11 +257,13 @@ export default function SplitView() {
|
|||||||
padding: '13px 20px',
|
padding: '13px 20px',
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
border: 'none',
|
border: 'none',
|
||||||
|
background: 'linear-gradient(135deg, #00d4aa, #0ea5e9)',
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
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,
|
||||||
|
boxShadow: '0 4px 20px rgba(0,212,170,0.2)',
|
||||||
transition: 'opacity 0.2s',
|
transition: 'opacity 0.2s',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import { toast } from '@/hooks/useToast';
|
|||||||
|
|
||||||
// Login style preview color themes (independent from app theme)
|
// Login style preview color themes (independent from app theme)
|
||||||
const LOGIN_PREVIEW_COLORS: Record<LoginStyle, { bg: string; accent: string; label: string }> = {
|
const LOGIN_PREVIEW_COLORS: Record<LoginStyle, { bg: string; accent: string; label: string }> = {
|
||||||
split: { bg: 'linear-gradient(135deg, #0f172a 0%, #1e293b 100%)', accent: '#6366f1', label: 'Linear/Notion' },
|
split: { bg: 'linear-gradient(135deg, #080c14 0%, #0c1420 100%)', accent: '#00d4aa', label: '晶格科技' },
|
||||||
centered: { bg: 'radial-gradient(ellipse at 50% 40%, #0a0a1a 0%, #1a0a2e 100%)', accent: '#8b5cf6', label: 'Stripe/Vercel' },
|
centered: { bg: 'linear-gradient(180deg, #0a0a1a 0%, #1a1035 50%, #2d1810 100%)', accent: '#fbbf24', label: '晨曦微光' },
|
||||||
sidepanel: { bg: 'linear-gradient(180deg, #111827 0%, #1f2937 100%)', accent: '#6366f1', label: 'Slack/Figma' },
|
sidepanel: { bg: 'linear-gradient(180deg, #020810 0%, #062030 100%)', accent: '#06b6d4', label: '深海鲸歌' },
|
||||||
fullimage: { bg: '#0d1117', accent: '#6366f1', label: 'GitHub/AWS' },
|
fullimage: { bg: '#0d1117', accent: '#6366f1', label: 'GitHub/AWS' },
|
||||||
minimal: { bg: 'linear-gradient(160deg, #0a0a1a 0%, #0f0a2a 40%, #150825 70%, #0a0a1a 100%)', accent: '#ec4899', label: 'Premium Dark' },
|
minimal: { bg: 'linear-gradient(160deg, #0a0a1a 0%, #0f0a2a 40%, #150825 70%, #0a0a1a 100%)', accent: '#ec4899', label: 'Premium Dark' },
|
||||||
};
|
};
|
||||||
@@ -141,42 +141,60 @@ export default function AppearanceSettings() {
|
|||||||
{opt.id === 'split' && (
|
{opt.id === 'split' && (
|
||||||
<>
|
<>
|
||||||
<div className="absolute inset-0 flex">
|
<div className="absolute inset-0 flex">
|
||||||
<div className="flex-1 p-2 flex flex-col justify-center gap-1">
|
{/* Grid lines preview */}
|
||||||
<div className="w-6 h-1.5 rounded-sm" style={{ background: 'rgba(255,255,255,0.3)' }} />
|
<div className="flex-1 relative overflow-hidden" style={{ background: '#080c14' }}>
|
||||||
<div className="w-8 h-1 rounded-sm" style={{ background: 'rgba(255,255,255,0.15)' }} />
|
<div className="absolute inset-0" style={{ backgroundImage: 'linear-gradient(rgba(0,212,170,0.08) 1px, transparent 1px), linear-gradient(90deg, rgba(0,212,170,0.08) 1px, transparent 1px)', backgroundSize: '10px 10px' }} />
|
||||||
|
{[20, 45, 70].map(t => <div key={t} className="absolute rounded-full" style={{ width: 3, height: 3, background: '#00d4aa', top: `${t}%`, left: `${30 + t * 0.5}%`, boxShadow: '0 0 4px #00d4aa' }} />)}
|
||||||
|
<div className="absolute bottom-2 left-2 flex flex-col gap-0.5">
|
||||||
|
<div className="w-5 h-1 rounded-sm" style={{ background: 'rgba(226,232,240,0.4)' }} />
|
||||||
|
<div className="w-7 h-0.5 rounded-sm" style={{ background: 'rgba(0,212,170,0.3)' }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-2/5 bg-[var(--dg-bg-secondary)] p-2 flex flex-col justify-center gap-1">
|
<div className="w-2/5 p-2 flex flex-col justify-center gap-1" style={{ background: '#0c1017' }}>
|
||||||
<div className="h-0.5 rounded-sm bg-[var(--dg-bg-hover)]" />
|
<div className="h-0.5 rounded-sm" style={{ background: 'rgba(148,163,184,0.15)' }} />
|
||||||
<div className="h-0.5 rounded-sm bg-[var(--dg-bg-hover)]" />
|
<div className="h-0.5 rounded-sm" style={{ background: 'rgba(148,163,184,0.15)' }} />
|
||||||
<div className="h-1 rounded-sm mt-0.5" style={{ background: preview.accent, opacity: 0.6 }} />
|
<div className="h-1 rounded-sm mt-0.5" style={{ background: '#00d4aa', opacity: 0.5 }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{opt.id === 'centered' && (
|
{opt.id === 'centered' && (
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
<div className="absolute inset-0 flex items-center justify-center" style={{ background: 'linear-gradient(180deg, #0a0a1a 0%, #1a1035 50%, #2d1810 100%)' }}>
|
||||||
<div className="w-3/4 rounded-lg p-2 flex flex-col items-center gap-1" style={{ background: 'rgba(255,255,255,0.04)', backdropFilter: 'blur(4px)', border: '1px solid rgba(255,255,255,0.06)' }}>
|
{/* Dawn rays */}
|
||||||
<div className="w-4 h-1 rounded-sm" style={{ background: 'rgba(255,255,255,0.4)' }} />
|
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-full h-3/4" style={{ background: 'radial-gradient(ellipse at 50% 100%, rgba(251,191,36,0.12), transparent 70%)' }} />
|
||||||
<div className="w-6 h-0.5 rounded-sm" style={{ background: 'rgba(255,255,255,0.15)' }} />
|
<div className="w-3/4 rounded-lg p-2 flex flex-col items-center gap-1 relative" style={{ background: 'rgba(10,10,26,0.6)', backdropFilter: 'blur(4px)', border: '1px solid rgba(251,191,36,0.1)' }}>
|
||||||
<div className="w-full h-0.5 rounded-sm" style={{ background: 'rgba(255,255,255,0.08)' }} />
|
<div className="w-4 h-1 rounded-sm" style={{ background: 'rgba(251,191,36,0.5)' }} />
|
||||||
<div className="w-full h-0.5 rounded-sm" style={{ background: 'rgba(255,255,255,0.08)' }} />
|
<div className="w-6 h-0.5 rounded-sm" style={{ background: 'rgba(251,191,36,0.2)' }} />
|
||||||
<div className="w-full h-1 rounded-sm mt-0.5" style={{ background: preview.accent, opacity: 0.5 }} />
|
<div className="w-full h-0.5 rounded-sm" style={{ background: 'rgba(255,255,255,0.06)' }} />
|
||||||
|
<div className="w-full h-0.5 rounded-sm" style={{ background: 'rgba(255,255,255,0.06)' }} />
|
||||||
|
<div className="w-full h-1 rounded-sm mt-0.5" style={{ background: '#fbbf24', opacity: 0.5 }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{opt.id === 'sidepanel' && (
|
{opt.id === 'sidepanel' && (
|
||||||
<div className="absolute inset-0 flex">
|
<div className="absolute inset-0 flex">
|
||||||
<div className="w-[38%] p-2 flex flex-col justify-end gap-1" style={{ background: preview.bg }}>
|
<div className="w-[38%] relative overflow-hidden" style={{ background: 'linear-gradient(180deg, #020810 0%, #062030 100%)' }}>
|
||||||
<div className="flex flex-wrap gap-0.5">
|
{/* Wave lines */}
|
||||||
{['SQL', 'DDL', 'ACL'].map(t => (
|
{[25, 40, 55, 70].map(t => (
|
||||||
<span key={t} className="px-1 rounded text-[5px]" style={{ background: 'rgba(255,255,255,0.08)', color: 'rgba(255,255,255,0.5)' }}>{t}</span>
|
<div key={t} className="absolute left-1/2 -translate-x-1/2 rounded-full" style={{ width: '120%', height: 20, top: `${t}%`, borderTop: '1px solid rgba(6,182,212,0.1)', borderRadius: '50%' }} />
|
||||||
))}
|
))}
|
||||||
|
{/* Biolum dots */}
|
||||||
|
{[30, 50, 65].map(t => (
|
||||||
|
<div key={t} className="absolute rounded-full" style={{ width: 3, height: 3, background: 'rgba(6,182,212,0.5)', top: `${t}%`, left: '30%', boxShadow: '0 0 4px rgba(6,182,212,0.4)' }} />
|
||||||
|
))}
|
||||||
|
<div className="absolute bottom-2 left-2 flex flex-col gap-0.5">
|
||||||
|
<div className="w-6 h-1 rounded-sm" style={{ background: 'rgba(6,182,212,0.5)' }} />
|
||||||
|
<div className="flex gap-0.5">
|
||||||
|
{['审核', '管控'].map(t => (
|
||||||
|
<span key={t} className="px-1 rounded text-[5px]" style={{ background: 'rgba(6,182,212,0.1)', color: 'rgba(6,182,212,0.5)' }}>{t}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 bg-[var(--dg-bg-primary)] p-2 flex flex-col justify-center gap-1">
|
<div className="flex-1 p-2 flex flex-col justify-center gap-1" style={{ background: '#060e18' }}>
|
||||||
<div className="h-0.5 rounded-sm bg-[var(--dg-bg-hover)]" />
|
<div className="h-0.5 rounded-sm" style={{ background: 'rgba(6,182,212,0.1)' }} />
|
||||||
<div className="h-0.5 rounded-sm bg-[var(--dg-bg-hover)]" />
|
<div className="h-0.5 rounded-sm" style={{ background: 'rgba(6,182,212,0.1)' }} />
|
||||||
<div className="h-1 rounded-sm mt-0.5" style={{ background: preview.accent, opacity: 0.5 }} />
|
<div className="h-1 rounded-sm mt-0.5" style={{ background: '#06b6d4', opacity: 0.5 }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ const LOGIN_STYLES: Record<LoginStyle, React.ComponentType> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const LOGIN_STYLE_OPTIONS: { id: LoginStyle; name: string; desc: string }[] = [
|
export const LOGIN_STYLE_OPTIONS: { id: LoginStyle; name: string; desc: string }[] = [
|
||||||
{ id: 'split', name: '经典分屏', desc: '左右分屏布局,功能特性展示' },
|
{ id: 'split', name: '经典分屏', desc: '发光网格晶格,Tron 科技感' },
|
||||||
{ id: 'centered', name: '居中卡片', desc: '渐变背景,居中登录卡片' },
|
{ id: 'centered', name: '居中卡片', desc: '黎明光柱,金色晨曦氛围' },
|
||||||
{ id: 'sidepanel', name: '侧边面板', desc: '左侧品牌面板 + 右侧表单' },
|
{ id: 'sidepanel', name: '侧边面板', desc: '深海波浪,生物发光粒子' },
|
||||||
{ id: 'fullimage', name: '全屏背景', desc: '科技背景,毛玻璃卡片' },
|
{ id: 'fullimage', name: '全屏背景', desc: '科技背景,毛玻璃卡片' },
|
||||||
{ id: 'minimal', name: '深蓝紫曲线', desc: '优雅暗黑,粉紫流动曲线' },
|
{ id: 'minimal', name: '深蓝紫曲线', desc: '优雅暗黑,粉紫流动曲线' },
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in new issue
Block a user