feat: 登录页3种风格全新重设计 — 数据晶格/晨曦微光/深海鲸歌

- SplitView: 晶格矩阵透视网格+19脉动节点+青绿微光, Tron科技感
- CenteredCard: 黎明渐变+10条金色光柱+地平线光晕+星点残留
- SidePanel: 深海渐变+5层波浪线+15生物发光粒子+3深海光柱
- AppearanceSettings: 预览颜色+缩略图同步更新
- LoginPage: 风格描述文案更新
This commit is contained in:
logan committed 2026-05-27 13:24:24 +08:00
1 parent 439eb6d6d7
commit 03bf7c92a9
5 files changed
+551 -478

No files matched your search

+139 -108
View File
@@ -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 <div
key={`ray-${i}`}
style={{ style={{
position: 'fixed', position: 'fixed',
top: '20%', bottom: 0,
left: '-10%',
width: 600,
height: 200,
background: '#00d4aa',
borderRadius: '50%',
filter: 'blur(80px)',
opacity: 0.2,
zIndex: 1,
animation: 'aurora-flow-1 12s ease-in-out infinite',
}}
/>
{/* 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%', left: '50%',
width: 550, width: 120,
height: 160, height: `${ray.height}vh`,
background: '#a855f7', transform: `translateX(-50%) rotate(${ray.angle}deg)`,
borderRadius: '50%', transformOrigin: 'bottom center',
filter: 'blur(80px)', background: `linear-gradient(to top, rgba(251,191,36,${ray.opacity}), transparent)`,
opacity: 0.15, clipPath: 'polygon(40% 100%, 60% 100%, 55% 0%, 45% 0%)',
opacity: ray.opacity,
zIndex: 1, zIndex: 1,
animation: 'aurora-flow-3 18s ease-in-out infinite', animation: `dawn-ray-pulse ${ray.duration}s ease-in-out infinite`,
pointerEvents: 'none',
}} }}
/> />
))}
{/* Horizon glow */}
<div
style={{
position: 'fixed',
bottom: -100,
left: '-10%',
width: '120%',
height: 200,
background: 'radial-gradient(ellipse at center, rgba(251,191,36,0.12), transparent 70%)',
zIndex: 2,
animation: 'dawn-horizon-glow 8s ease-in-out infinite',
pointerEvents: 'none',
}}
/>
{/* Stars — fading remnants of night */}
{stars.map((star, i) => (
<div
key={`star-${i}`}
style={{
position: 'fixed',
top: star.top,
left: star.left,
width: star.size,
height: star.size,
borderRadius: '50%',
background: '#fff',
opacity: star.opacity,
zIndex: 1,
animation: `dawn-star-fade ${star.duration}s ease-in-out infinite`,
pointerEvents: 'none',
}}
/>
))}
{/* 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>
+197 -199
View File
@@ -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);
} }
50% { @keyframes ocean-wave-2 {
background: radial-gradient(circle, #e2e8f0, #93c5fd); 0%, 100% { transform: translateX(30px); }
box-shadow: 0 0 60px rgba(147,197,253,0.4); 50% { transform: translateX(-30px); }
} }
@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 ? '登录中…' : '登录'}
+169 -143
View File
@@ -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 className="flex min-h-screen" style={{ fontFamily: 'system-ui, -apple-system, sans-serif' }}>
{/* ═══════════════════════════════════════════════════════════════
Left Panel 55% — 晶格矩阵区
═══════════════════════════════════════════════════════════════ */}
<div <div
className="flex min-h-screen" className="relative overflow-hidden"
style={{ background: 'var(--dg-bg-primary)', color: 'var(--dg-text-primary)' }}
>
{/* ─── Left Panel 55% — 星河流转 ─── */}
<div
className="relative overflow-hidden flex items-center"
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%', borderRadius: '50%',
background: 'radial-gradient(circle, rgba(99,102,241,0.35) 0%, rgba(139,92,246,0.12) 40%, transparent 70%)', background: '#00d4aa',
transform: 'translate(-50%, -50%)', boxShadow: '0 0 8px rgba(0,212,170,0.6)',
opacity: 0.15, animation: `lattice-node-pulse ${n.duration}s ease-in-out ${n.delay}s infinite`,
animation: 'split-glow-pulse 6s ease-in-out infinite',
}} }}
/> />
))}
{/* Meteor 1 */} {/* ── Right-Bottom Glow Decoration ── */}
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: '-5%', right: -60,
left: '60%', bottom: -60,
width: 2, width: 300,
height: 120, height: 300,
background: 'linear-gradient(to bottom right, rgba(255,255,255,0.9), transparent)', borderRadius: '50%',
borderRadius: 2, background: 'radial-gradient(circle, #00d4aa, transparent)',
transform: 'rotate(-45deg)', opacity: 0.06,
opacity: 0, animation: 'lattice-glow-drift 15s ease-in-out infinite',
animation: 'split-meteor-1 4s ease-in 0s infinite', pointerEvents: 'none',
}} }}
/> />
{/* Meteor 2 */} {/* ── 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: '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);
} }
@keyframes split-meteor-1 { 50% {
0% { transform: rotate(-45deg) translate(0, 0); opacity: 0; } opacity: 0.8;
5% { opacity: 1; } transform: scale(1.3);
30% { opacity: 1; }
45% { transform: rotate(-45deg) translate(-320px, 320px); opacity: 0; }
100% { opacity: 0; }
} }
@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 { @keyframes lattice-glow-drift {
0% { transform: rotate(-45deg) translate(0, 0); opacity: 0; } 0%, 100% {
4% { opacity: 0.9; } transform: translateX(-30px);
32% { opacity: 0.9; } }
42% { transform: rotate(-45deg) translate(-280px, 280px); opacity: 0; } 50% {
100% { opacity: 0; } transform: translateX(30px);
} }
@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',
}} }}
> >
+43 -25
View File
@@ -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 className="w-2/5 bg-[var(--dg-bg-secondary)] p-2 flex flex-col justify-center gap-1"> </div>
<div className="h-0.5 rounded-sm bg-[var(--dg-bg-hover)]" /> <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-1 rounded-sm mt-0.5" style={{ background: preview.accent, opacity: 0.6 }} /> <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: '#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 className="flex-1 bg-[var(--dg-bg-primary)] p-2 flex flex-col justify-center gap-1"> </div>
<div className="h-0.5 rounded-sm bg-[var(--dg-bg-hover)]" /> <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-1 rounded-sm mt-0.5" style={{ background: preview.accent, opacity: 0.5 }} /> <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: '#06b6d4', opacity: 0.5 }} />
</div> </div>
</div> </div>
)} )}
+3 -3
View File
@@ -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: '优雅暗黑,粉紫流动曲线' },
]; ];