feat: 霓虹都市→白日梦境 + 渐变网格→云石纹理 — 两个白色浅色主题登录页重写

This commit is contained in:
logan committed 2026-05-27 21:53:27 +08:00
1 parent b5739ba955
commit 5a61babd33
241 files changed
+58974 -7370

No files matched your search

@@ -25,13 +25,13 @@ const KEYFRAMES = `
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(25px, 30px); }
}
@keyframes card-glow {
0%, 100% { opacity: 0.4; }
50% { opacity: 0.7; }
@keyframes mesh-drift-6 {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(-15px, 25px); }
}
@keyframes input-glow-pulse {
0%, 100% { opacity: 0.3; }
50% { opacity: 0.6; }
@keyframes mesh-drift-7 {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(20px, -15px); }
}
`;
@@ -45,53 +45,67 @@ export default function GradientMeshView(): React.JSX.Element {
const isReady = username.trim().length > 0 && password.trim().length > 0;
/* ---- gradient mesh blobs ---- */
/* ---- marble / terrazzo blobs (light tones) ---- */
const blobs: React.CSSProperties[] = [
{
position: 'fixed', top: '-100px', left: '-100px',
width: '600px', height: '600px',
background: 'radial-gradient(circle, rgba(99, 102, 241, 0.3), transparent 70%)',
filter: 'blur(80px)', pointerEvents: 'none',
animation: 'mesh-drift-1 12s ease-in-out infinite',
},
{
position: 'fixed', bottom: '-80px', right: '-80px',
width: '500px', height: '500px',
background: 'radial-gradient(circle, rgba(244, 63, 94, 0.2), transparent 70%)',
position: 'fixed', top: '-120px', left: '-80px',
width: '650px', height: '650px',
background: 'radial-gradient(circle, rgba(99,102,241,0.06), transparent 70%)',
filter: 'blur(90px)', pointerEvents: 'none',
animation: 'mesh-drift-2 10s ease-in-out infinite',
animation: 'mesh-drift-1 14s ease-in-out infinite',
},
{
position: 'fixed', top: '40%', right: '10%',
width: '400px', height: '400px',
background: 'radial-gradient(circle, rgba(16, 185, 129, 0.15), transparent 70%)',
filter: 'blur(100px)', pointerEvents: 'none',
animation: 'mesh-drift-3 14s ease-in-out infinite',
},
{
position: 'fixed', bottom: '5%', left: '5%',
width: '450px', height: '450px',
background: 'radial-gradient(circle, rgba(245, 158, 11, 0.1), transparent 70%)',
filter: 'blur(85px)', pointerEvents: 'none',
animation: 'mesh-drift-4 11s ease-in-out infinite',
},
{
position: 'fixed', top: '-50px', right: '20%',
position: 'fixed', bottom: '-100px', right: '-60px',
width: '550px', height: '550px',
background: 'radial-gradient(circle, rgba(139, 92, 246, 0.2), transparent 70%)',
background: 'radial-gradient(circle, rgba(244,63,94,0.04), transparent 70%)',
filter: 'blur(100px)', pointerEvents: 'none',
animation: 'mesh-drift-2 12s ease-in-out infinite',
},
{
position: 'fixed', top: '35%', right: '8%',
width: '450px', height: '450px',
background: 'radial-gradient(circle, rgba(16,185,129,0.05), transparent 70%)',
filter: 'blur(110px)', pointerEvents: 'none',
animation: 'mesh-drift-3 16s ease-in-out infinite',
},
{
position: 'fixed', bottom: '10%', left: '8%',
width: '500px', height: '500px',
background: 'radial-gradient(circle, rgba(139,92,246,0.04), transparent 70%)',
filter: 'blur(95px)', pointerEvents: 'none',
animation: 'mesh-drift-5 13s ease-in-out infinite',
animation: 'mesh-drift-4 13s ease-in-out infinite',
},
{
position: 'fixed', top: '-60px', right: '25%',
width: '580px', height: '580px',
background: 'radial-gradient(circle, rgba(99,102,241,0.04), transparent 70%)',
filter: 'blur(100px)', pointerEvents: 'none',
animation: 'mesh-drift-5 15s ease-in-out infinite',
},
{
position: 'fixed', top: '60%', left: '30%',
width: '400px', height: '400px',
background: 'radial-gradient(circle, rgba(244,63,94,0.03), transparent 70%)',
filter: 'blur(105px)', pointerEvents: 'none',
animation: 'mesh-drift-6 11s ease-in-out infinite',
},
{
position: 'fixed', top: '15%', left: '50%',
width: '480px', height: '480px',
background: 'radial-gradient(circle, rgba(16,185,129,0.04), transparent 70%)',
filter: 'blur(95px)', pointerEvents: 'none',
animation: 'mesh-drift-7 17s ease-in-out infinite',
},
];
/* ---- input base style ---- */
const inputBase: React.CSSProperties = {
width: '100%',
background: 'rgba(255,255,255,0.04)',
border: '1px solid rgba(255,255,255,0.06)',
background: '#fff',
border: '1px solid #e5e7eb',
borderRadius: 12,
padding: '14px 18px',
color: '#fff',
color: '#111827',
fontSize: 15,
outline: 'none',
transition: 'all 0.25s ease',
@@ -100,32 +114,12 @@ export default function GradientMeshView(): React.JSX.Element {
const getInputStyle = (field: string): React.CSSProperties => ({
...inputBase,
borderColor: focusedField === field ? 'rgba(99,102,241,0.5)' : 'rgba(255,255,255,0.06)',
background: focusedField === field ? 'rgba(255,255,255,0.07)' : 'rgba(255,255,255,0.04)',
borderColor: focusedField === field ? '#8b5cf6' : '#e5e7eb',
boxShadow: focusedField === field
? '0 0 0 3px rgba(99,102,241,0.1)'
: 'none',
});
/* ---- card top accent line ---- */
const topAccent: React.CSSProperties = {
position: 'absolute',
top: 0, left: 24, right: 24,
height: 2,
borderRadius: 1,
background: 'linear-gradient(90deg, #6366f1, #f43f5e, #10b981)',
opacity: 0.6,
};
/* ---- subtle glow inside card bottom ---- */
const innerGlow: React.CSSProperties = {
position: 'absolute',
bottom: -60, left: '50%',
transform: 'translateX(-50%)',
width: '70%', height: 120,
background: 'radial-gradient(ellipse, rgba(99,102,241,0.08), transparent 70%)',
filter: 'blur(40px)',
pointerEvents: 'none',
animation: 'card-glow 8s ease-in-out infinite',
};
return (
<>
<style>{KEYFRAMES}</style>
@@ -140,24 +134,25 @@ export default function GradientMeshView(): React.JSX.Element {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#0a0a0f',
background: '#fafaf8',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
}}
>
{/* ---- gradient mesh blobs ---- */}
{/* ---- marble / terrazzo texture blobs ---- */}
{blobs.map((b, i) => (
<div key={i} style={b} />
))}
{/* ---- subtle dot-grid texture ---- */}
{/* ---- subtle dot-grid texture (light version) ---- */}
<div
style={{
position: 'fixed',
inset: 0,
pointerEvents: 'none',
opacity: 0.03,
opacity: 0.28,
backgroundImage:
'radial-gradient(circle, rgba(255,255,255,0.8) 1px, transparent 1px)',
'radial-gradient(circle, rgba(0,0,0,0.08) 1px, transparent 1px)',
backgroundSize: '24px 24px',
}}
/>
@@ -169,22 +164,17 @@ export default function GradientMeshView(): React.JSX.Element {
width: '100%',
maxWidth: 400,
margin: '0 20px',
background: 'rgba(255, 255, 255, 0.03)',
background: 'rgba(255,255,255,0.85)',
backdropFilter: 'blur(40px) saturate(180%)',
WebkitBackdropFilter: 'blur(40px) saturate(180%)',
border: '1px solid rgba(255, 255, 255, 0.08)',
border: 'none',
borderRadius: 24,
padding: '48px 36px',
boxShadow:
'0 0 0 1px rgba(255,255,255,0.04), 0 25px 50px -12px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.05)',
'0 1px 2px rgba(0,0,0,0.04), 0 4px 16px rgba(0,0,0,0.06), 0 12px 40px rgba(0,0,0,0.04)',
zIndex: 10,
}}
>
{/* top accent line */}
<div style={topAccent} />
{/* inner glow */}
<div style={innerGlow} />
{/* ---- brand ---- */}
<div style={{ textAlign: 'center', marginBottom: 36 }}>
@@ -199,7 +189,7 @@ export default function GradientMeshView(): React.JSX.Element {
alignItems: 'center',
justifyContent: 'center',
marginBottom: 20,
boxShadow: '0 8px 24px rgba(99,102,241,0.25)',
boxShadow: '0 6px 20px rgba(99,102,241,0.2)',
}}
>
<svg width={20} height={20} viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2L20 6V11C20 16 16 19.5 12 22C8 19.5 4 16 4 11V6L12 2Z" /><path d="M9 12L11 14L15 10" /></svg>
@@ -207,7 +197,7 @@ export default function GradientMeshView(): React.JSX.Element {
<h1
style={{
color: '#fff',
color: '#111827',
fontSize: 24,
fontWeight: 700,
letterSpacing: '-0.5px',
@@ -218,7 +208,7 @@ export default function GradientMeshView(): React.JSX.Element {
</h1>
<p
style={{
color: 'rgba(255,255,255,0.35)',
color: '#6b7280',
fontSize: 14,
letterSpacing: '0.3px',
margin: 0,
@@ -234,65 +224,35 @@ export default function GradientMeshView(): React.JSX.Element {
style={{ display: 'flex', flexDirection: 'column', gap: 14 }}
>
{/* username */}
<div style={{ position: 'relative' }}>
<input
type="text"
placeholder="用户名"
value={username}
onChange={(e) => setUsername(e.target.value)}
onFocus={() => setFocusedField('username')}
onBlur={() => setFocusedField(null)}
style={getInputStyle('username')}
autoComplete="username"
/>
{focusedField === 'username' && (
<div
style={{
position: 'absolute',
inset: -1,
borderRadius: 13,
background: 'radial-gradient(ellipse at center, rgba(99,102,241,0.12), transparent 70%)',
filter: 'blur(8px)',
pointerEvents: 'none',
animation: 'input-glow-pulse 3s ease-in-out infinite',
}}
/>
)}
</div>
<input
type="text"
placeholder="用户名"
value={username}
onChange={(e) => setUsername(e.target.value)}
onFocus={() => setFocusedField('username')}
onBlur={() => setFocusedField(null)}
style={getInputStyle('username')}
autoComplete="username"
/>
{/* password */}
<div style={{ position: 'relative' }}>
<input
type="password"
placeholder="密码"
value={password}
onChange={(e) => setPassword(e.target.value)}
onFocus={() => setFocusedField('password')}
onBlur={() => setFocusedField(null)}
style={getInputStyle('password')}
autoComplete="current-password"
/>
{focusedField === 'password' && (
<div
style={{
position: 'absolute',
inset: -1,
borderRadius: 13,
background: 'radial-gradient(ellipse at center, rgba(99,102,241,0.12), transparent 70%)',
filter: 'blur(8px)',
pointerEvents: 'none',
animation: 'input-glow-pulse 3s ease-in-out infinite',
}}
/>
)}
</div>
<input
type="password"
placeholder="密码"
value={password}
onChange={(e) => setPassword(e.target.value)}
onFocus={() => setFocusedField('password')}
onBlur={() => setFocusedField(null)}
style={getInputStyle('password')}
autoComplete="current-password"
/>
{/* error */}
{error && (
<div
style={{
background: 'rgba(239,68,68,0.08)',
color: '#f87171',
background: 'rgba(239,68,68,0.06)',
color: '#ef4444',
borderRadius: 10,
padding: '10px 14px',
fontSize: 13,
@@ -319,10 +279,10 @@ export default function GradientMeshView(): React.JSX.Element {
color: '#fff',
cursor: isReady && !loading ? 'pointer' : 'not-allowed',
opacity: isReady ? 1 : 0.5,
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #ec4899 100%)',
background: 'linear-gradient(135deg, #3b82f6, #8b5cf6)',
boxShadow: hoverBtn && isReady && !loading
? '0 8px 32px rgba(99,102,241,0.45)'
: '0 4px 20px rgba(99,102,241,0.3)',
? '0 8px 28px rgba(99,102,241,0.35)'
: '0 4px 16px rgba(99,102,241,0.2)',
transform: hoverBtn && isReady && !loading ? 'translateY(-1px)' : 'translateY(0)',
transition: 'all 0.25s ease',
}}
@@ -335,7 +295,7 @@ export default function GradientMeshView(): React.JSX.Element {
<p
style={{
textAlign: 'center',
color: 'rgba(255,255,255,0.2)',
color: '#9ca3af',
fontSize: 12,
margin: '28px 0 0 0',
}}
+92 -268
View File
@@ -2,103 +2,41 @@ import React from 'react';
import { useLogin } from '@/hooks/useLogin';
const KEYFRAMES = `
@keyframes neon-pulse {
0%, 100% { opacity: 0.6; box-shadow: 0 0 10px currentColor, 0 0 20px currentColor, 0 0 40px currentColor; }
50% { opacity: 1; box-shadow: 0 0 20px currentColor, 0 0 40px currentColor, 0 0 80px currentColor; }
@keyframes float-slow {
0%, 100% { transform: translate(0, 0) scale(1); }
25% { transform: translate(20px, -30px) scale(1.03); }
50% { transform: translate(-10px, -50px) scale(1.01); }
75% { transform: translate(15px, -20px) scale(1.04); }
}
@keyframes neon-flicker {
0%, 100% { opacity: 1; }
3% { opacity: 0.4; }
6% { opacity: 1; }
7% { opacity: 0.2; }
8% { opacity: 1; }
92% { opacity: 1; }
93% { opacity: 0.3; }
94% { opacity: 1; }
@keyframes float-medium {
0%, 100% { transform: translate(0, 0) scale(1); }
25% { transform: translate(-25px, -40px) scale(1.02); }
50% { transform: translate(20px, -60px) scale(0.98); }
75% { transform: translate(-15px, -25px) scale(1.03); }
}
@keyframes rain-fall {
0% { transform: translateY(-100vh) translateX(0); opacity: 0; }
10% { opacity: 0.6; }
90% { opacity: 0.6; }
100% { transform: translateY(100vh) translateX(20px); opacity: 0; }
}
@keyframes grid-move {
0% { transform: perspective(500px) rotateX(60deg) translateY(0); }
100% { transform: perspective(500px) rotateX(60deg) translateY(40px); }
}
@keyframes city-breathe {
0%, 100% { transform: scaleY(1); }
50% { transform: scaleY(1.02); }
}
@keyframes neon-glow-text {
0%, 100% { text-shadow: 0 0 10px rgba(236, 72, 153, 0.5), 0 0 20px rgba(236, 72, 153, 0.3); }
50% { text-shadow: 0 0 20px rgba(236, 72, 153, 0.8), 0 0 40px rgba(236, 72, 153, 0.5); }
@keyframes float-fast {
0%, 100% { transform: translate(0, 0) scale(1); }
25% { transform: translate(15px, -20px) scale(1.01); }
50% { transform: translate(-20px, -35px) scale(1.02); }
75% { transform: translate(10px, -15px) scale(0.99); }
}
`;
// Building data: [width, heightPercent, leftPercent, glowColor]
const BUILDINGS: [number, number, number, string][] = [
[40, 18, 0, 'rgba(236, 72, 153, 0.4)'],
[55, 28, 4, 'rgba(168, 85, 247, 0.35)'],
[35, 15, 9, 'rgba(6, 182, 212, 0.3)'],
[70, 35, 12, 'rgba(236, 72, 153, 0.45)'],
[45, 22, 19, 'rgba(168, 85, 247, 0.4)'],
[50, 30, 23, 'rgba(6, 182, 212, 0.35)'],
[30, 16, 28, 'rgba(236, 72, 153, 0.3)'],
[80, 38, 31, 'rgba(168, 85, 247, 0.5)'],
[42, 20, 39, 'rgba(6, 182, 212, 0.4)'],
[60, 32, 43, 'rgba(236, 72, 153, 0.45)'],
[38, 17, 49, 'rgba(168, 85, 247, 0.3)'],
[75, 36, 52, 'rgba(6, 182, 212, 0.5)'],
[48, 25, 60, 'rgba(236, 72, 153, 0.4)'],
[55, 30, 65, 'rgba(168, 85, 247, 0.45)'],
[33, 14, 70, 'rgba(6, 182, 212, 0.3)'],
[85, 40, 73, 'rgba(236, 72, 153, 0.5)'],
[44, 21, 82, 'rgba(168, 85, 247, 0.4)'],
[58, 33, 86, 'rgba(6, 182, 212, 0.45)'],
[36, 16, 92, 'rgba(236, 72, 153, 0.35)'],
[50, 27, 95, 'rgba(168, 85, 247, 0.4)'],
];
// Neon tube data: [topPercent, color, animationDelay, animationDuration]
const NEON_TUBES: [number, string, string, string][] = [
[22, '#ec4899', '0s', '3s'],
[38, '#06b6d4', '1s', '4s'],
[52, '#a855f7', '2s', '3.5s'],
];
// Rain drop data: [leftPercent, delay, duration]
const RAIN_DROPS: [number, string, string][] = [
[5, '0s', '1.5s'],
[15, '0.3s', '1.8s'],
[25, '0.7s', '1.3s'],
[35, '0.2s', '1.6s'],
[45, '0.9s', '1.4s'],
[55, '0.5s', '1.7s'],
[65, '0.1s', '1.5s'],
[75, '0.8s', '1.9s'],
[85, '0.4s', '1.3s'],
[95, '0.6s', '1.6s'],
[10, '1.1s', '1.4s'],
[30, '1.3s', '1.7s'],
[50, '1.0s', '1.5s'],
[70, '1.5s', '1.8s'],
[90, '1.2s', '1.4s'],
[20, '0.15s', '2.0s'],
[40, '0.85s', '1.35s'],
[60, '1.25s', '1.65s'],
[80, '0.45s', '1.75s'],
[8, '0.95s', '1.55s'],
// Floating shapes: [width, height, top, left, color, opacity, animation, duration]
const FLOATING_SHAPES: [number, number, string, string, string, number, string, string][] = [
[340, 280, '8%', '5%', 'linear-gradient(135deg, #e0f2fe 0%, #f3e8ff 100%)', 0.5, 'float-slow', '20s'],
[240, 200, '55%', '2%', 'linear-gradient(135deg, #f3e8ff 0%, #ede9fe 100%)', 0.4, 'float-medium', '25s'],
[180, 160, '70%', '15%', 'linear-gradient(135deg, #dbeafe 0%, #e0f2fe 100%)', 0.35, 'float-fast', '18s'],
[280, 240, '20%', '18%', 'linear-gradient(135deg, #ede9fe 0%, #e0f2fe 100%)', 0.3, 'float-slow', '22s'],
];
export default function NeonCityView(): JSX.Element {
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
const isDisabled = !username || !password || loading;
return (
<>
<style>{KEYFRAMES}</style>
@@ -110,124 +48,30 @@ export default function NeonCityView(): JSX.Element {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'linear-gradient(180deg, #0a0418 0%, #1a0b2e 40%, #0d0718 100%)',
background: 'linear-gradient(180deg, #fafbfc 0%, #f0f2f5 100%)',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
}}
>
{/* Grid floor background */}
<div
style={{
position: 'absolute',
bottom: '-60%',
left: '-50%',
width: '200%',
height: '100%',
backgroundImage:
'linear-gradient(rgba(236, 72, 153, 0.04) 1px, transparent 1px), linear-gradient(90deg, rgba(6, 182, 212, 0.04) 1px, transparent 1px)',
backgroundSize: '60px 60px',
animation: 'grid-move 8s linear infinite',
pointerEvents: 'none',
}}
/>
{/* Neon tubes */}
{NEON_TUBES.map(([top, color, delay, duration], i) => (
{/* Floating soft geometric shapes */}
{FLOATING_SHAPES.map(([w, h, top, left, bg, opacity, anim, dur], i) => (
<div
key={`neon-${i}`}
key={`shape-${i}`}
style={{
position: 'absolute',
top: `${top}%`,
left: '5%',
right: '5%',
height: '2px',
background: color,
color: color,
boxShadow: `0 0 10px ${color}, 0 0 20px ${color}, 0 0 40px ${color}`,
animation: `neon-pulse ${duration} ease-in-out infinite ${delay}, neon-flicker ${duration} ease-in-out infinite ${delay}`,
borderRadius: '1px',
top,
left,
width: w,
height: h,
background: bg,
opacity,
borderRadius: '50%',
filter: 'blur(40px)',
animation: `${anim} ${dur} ease-in-out infinite`,
pointerEvents: 'none',
zIndex: 1,
}}
/>
))}
{/* City skyline buildings */}
<div
style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '40%',
animation: 'city-breathe 6s ease-in-out infinite',
pointerEvents: 'none',
zIndex: 2,
}}
>
{BUILDINGS.map(([width, heightPct, leftPct, glowColor], i) => (
<div
key={`building-${i}`}
style={{
position: 'absolute',
bottom: 0,
left: `${leftPct}%`,
width: `${width}px`,
height: `${heightPct}%`,
background: 'rgba(5, 3, 15, 0.9)',
borderRadius: '2px 2px 0 0',
boxShadow: `0 -2px 10px ${glowColor}`,
}}
/>
))}
</div>
{/* Rain drops */}
{RAIN_DROPS.map(([left, delay, duration], i) => (
<div
key={`rain-${i}`}
style={{
position: 'absolute',
left: `${left}%`,
top: 0,
width: '1px',
height: '25px',
background: 'linear-gradient(180deg, transparent, rgba(6, 182, 212, 0.3))',
animation: `rain-fall ${duration} linear infinite ${delay}`,
pointerEvents: 'none',
zIndex: 3,
}}
/>
))}
{/* Fog layer */}
<div
style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '35%',
background: 'linear-gradient(180deg, transparent 0%, rgba(10, 4, 24, 0.6) 50%, rgba(10, 4, 24, 0.85) 100%)',
pointerEvents: 'none',
zIndex: 4,
}}
/>
{/* Top glow */}
<div
style={{
position: 'absolute',
top: '-10%',
left: '50%',
transform: 'translateX(-50%)',
width: '600px',
height: '300px',
background: 'radial-gradient(ellipse, rgba(168, 85, 247, 0.12) 0%, transparent 70%)',
pointerEvents: 'none',
zIndex: 0,
}}
/>
{/* Login card */}
<div
style={{
@@ -236,58 +80,41 @@ export default function NeonCityView(): JSX.Element {
width: '100%',
maxWidth: 400,
margin: '0 20px',
background: 'rgba(10, 5, 25, 0.8)',
backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(20px)',
border: '1px solid rgba(236, 72, 153, 0.2)',
borderRadius: 16,
padding: '44px 36px',
boxShadow: '0 0 20px rgba(236, 72, 153, 0.1), 0 8px 40px rgba(0,0,0,0.5)',
overflow: 'hidden',
background: 'rgba(255, 255, 255, 0.72)',
backdropFilter: 'blur(24px)',
WebkitBackdropFilter: 'blur(24px)',
borderRadius: 20,
padding: '48px 40px',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.06)',
}}
>
{/* Top neon line decoration */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '2px',
background: 'linear-gradient(90deg, #ec4899, #06b6d4, #a855f7, #ec4899)',
backgroundSize: '200% 100%',
animation: 'neon-pulse 3s ease-in-out infinite',
}}
/>
{/* Brand area */}
<div style={{ textAlign: 'center', marginBottom: 32 }}>
<div style={{ textAlign: 'center', marginBottom: 36 }}>
{/* Logo */}
<div
style={{
width: 44,
height: 44,
borderRadius: 12,
width: 48,
height: 48,
borderRadius: 14,
background: 'linear-gradient(135deg, #3b82f6, #8b5cf6)',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 20,
boxShadow: '0 0 15px rgba(236, 72, 153, 0.5), 0 0 30px rgba(6, 182, 212, 0.3)',
boxShadow: '0 4px 16px rgba(59, 130, 246, 0.25)',
}}
>
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2L20 6V11C20 16 16 19.5 12 22C8 19.5 4 16 4 11V6L12 2Z" /><path d="M9 12L11 14L15 10" /></svg>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2L20 6V11C20 16 16 19.5 12 22C8 19.5 4 16 4 11V6L12 2Z" /><path d="M9 12L11 14L15 10" /></svg>
</div>
{/* Title */}
<h1
style={{
fontSize: 24,
fontSize: 26,
fontWeight: 700,
color: '#fff',
color: '#1a1a2e',
margin: '0 0 8px',
letterSpacing: '0.5px',
animation: 'neon-glow-text 3s ease-in-out infinite',
letterSpacing: '0.3px',
}}
>
OpsxSQL
@@ -297,9 +124,9 @@ export default function NeonCityView(): JSX.Element {
<p
style={{
fontSize: 14,
color: 'rgba(6, 182, 212, 0.8)',
color: '#64748b',
margin: 0,
letterSpacing: '0.3px',
letterSpacing: '0.2px',
}}
>
企业级数据库审核与合规管理
@@ -309,16 +136,16 @@ export default function NeonCityView(): JSX.Element {
{/* Login form */}
<form onSubmit={handleLogin}>
{/* Username input */}
<div style={{ marginBottom: 18 }}>
<div style={{ marginBottom: 20 }}>
<label
htmlFor="username"
style={{
display: 'block',
fontSize: 13,
fontWeight: 500,
color: 'rgba(255,255,255,0.7)',
color: '#64748b',
marginBottom: 8,
letterSpacing: '0.3px',
letterSpacing: '0.2px',
}}
>
用户名
@@ -335,36 +162,36 @@ export default function NeonCityView(): JSX.Element {
height: 46,
padding: '0 16px',
fontSize: 14,
color: '#fff',
background: 'rgba(255,255,255,0.05)',
border: '1px solid rgba(255,255,255,0.08)',
color: '#1a1a2e',
background: '#f5f5f5',
border: '1.5px solid transparent',
borderRadius: 10,
outline: 'none',
boxSizing: 'border-box',
transition: 'border-color 0.3s ease, box-shadow 0.3s ease',
transition: 'border-color 0.25s ease, background 0.25s ease',
}}
onFocus={(e) => {
e.currentTarget.style.borderColor = 'rgba(236, 72, 153, 0.6)';
e.currentTarget.style.boxShadow = '0 0 10px rgba(236, 72, 153, 0.2)';
e.currentTarget.style.borderColor = '#93c5fd';
e.currentTarget.style.background = '#fff';
}}
onBlur={(e) => {
e.currentTarget.style.borderColor = 'rgba(255,255,255,0.08)';
e.currentTarget.style.boxShadow = 'none';
e.currentTarget.style.borderColor = 'transparent';
e.currentTarget.style.background = '#f5f5f5';
}}
/>
</div>
{/* Password input */}
<div style={{ marginBottom: 24 }}>
<div style={{ marginBottom: 28 }}>
<label
htmlFor="password"
style={{
display: 'block',
fontSize: 13,
fontWeight: 500,
color: 'rgba(255,255,255,0.7)',
color: '#64748b',
marginBottom: 8,
letterSpacing: '0.3px',
letterSpacing: '0.2px',
}}
>
密码
@@ -381,21 +208,21 @@ export default function NeonCityView(): JSX.Element {
height: 46,
padding: '0 16px',
fontSize: 14,
color: '#fff',
background: 'rgba(255,255,255,0.05)',
border: '1px solid rgba(255,255,255,0.08)',
color: '#1a1a2e',
background: '#f5f5f5',
border: '1.5px solid transparent',
borderRadius: 10,
outline: 'none',
boxSizing: 'border-box',
transition: 'border-color 0.3s ease, box-shadow 0.3s ease',
transition: 'border-color 0.25s ease, background 0.25s ease',
}}
onFocus={(e) => {
e.currentTarget.style.borderColor = 'rgba(6, 182, 212, 0.6)';
e.currentTarget.style.boxShadow = '0 0 10px rgba(6, 182, 212, 0.2)';
e.currentTarget.style.borderColor = '#93c5fd';
e.currentTarget.style.background = '#fff';
}}
onBlur={(e) => {
e.currentTarget.style.borderColor = 'rgba(255,255,255,0.08)';
e.currentTarget.style.boxShadow = 'none';
e.currentTarget.style.borderColor = 'transparent';
e.currentTarget.style.background = '#f5f5f5';
}}
/>
</div>
@@ -406,13 +233,10 @@ export default function NeonCityView(): JSX.Element {
style={{
marginBottom: 16,
padding: '10px 14px',
borderRadius: 8,
background: 'rgba(239, 68, 68, 0.1)',
border: '1px solid rgba(239, 68, 68, 0.2)',
color: '#f87171',
borderRadius: 10,
background: '#fef2f2',
color: '#dc2626',
fontSize: 13,
textShadow: '0 0 8px rgba(239, 68, 68, 0.4)',
animation: 'neon-flicker 0.5s ease-in-out',
}}
>
{error}
@@ -422,36 +246,36 @@ export default function NeonCityView(): JSX.Element {
{/* Login button */}
<button
type="submit"
disabled={!username || !password || loading}
disabled={isDisabled}
style={{
width: '100%',
height: 48,
fontSize: 15,
fontWeight: 600,
color: '#fff',
background: 'linear-gradient(135deg, #ec4899, #06b6d4)',
background: isDisabled
? '#cbd5e1'
: 'linear-gradient(135deg, #3b82f6, #8b5cf6)',
border: 'none',
borderRadius: 10,
cursor: !username || !password || loading ? 'not-allowed' : 'pointer',
opacity: !username || !password ? 0.6 : 1,
boxShadow: '0 0 15px rgba(236, 72, 153, 0.4)',
transition: 'all 0.3s ease',
cursor: isDisabled ? 'not-allowed' : 'pointer',
transition: 'all 0.25s ease',
letterSpacing: '0.5px',
}}
onMouseEnter={(e) => {
if (username && password && !loading) {
e.currentTarget.style.boxShadow = '0 0 25px rgba(236, 72, 153, 0.6), 0 0 40px rgba(6, 182, 212, 0.3)';
e.currentTarget.style.transform = 'translateY(-1px)';
if (!isDisabled) {
e.currentTarget.style.transform = 'scale(1.02)';
e.currentTarget.style.boxShadow = '0 6px 20px rgba(59, 130, 246, 0.3)';
}
}}
onMouseLeave={(e) => {
e.currentTarget.style.boxShadow = '0 0 15px rgba(236, 72, 153, 0.4)';
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.transform = 'scale(1)';
e.currentTarget.style.boxShadow = 'none';
}}
>
{loading ? (
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2L20 6V11C20 16 16 19.5 12 22C8 19.5 4 16 4 11V6L12 2Z" /><path d="M9 12L11 14L15 10" /></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" /><animateTransform attributeName="transform" type="rotate" from="0 12 12" to="360 12 12" dur="1s" repeatCount="indefinite" /></svg>
登录中...
</span>
) : (
@@ -465,8 +289,8 @@ export default function NeonCityView(): JSX.Element {
style={{
textAlign: 'center',
fontSize: 12,
color: 'rgba(255,255,255,0.25)',
marginTop: 28,
color: '#94a3b8',
marginTop: 32,
marginBottom: 0,
}}
>
+24 -25
View File
@@ -15,8 +15,8 @@ const LOGIN_PREVIEW_COLORS: Record<LoginStyle, { bg: string; accent: string; lab
aurora: { bg: 'linear-gradient(180deg, #050810 0%, #0a0e1a 30%, #0d1425 60%, #080c14 100%)', accent: '#10b981', label: '极光之境' },
cosmos: { bg: '#030508', accent: '#8b5cf6', label: '星际漫游' },
inkwash: { bg: 'linear-gradient(180deg, #f5f0e8 0%, #ebe4d6 100%)', accent: '#c9a96e', label: '水墨山水' },
neoncity: { bg: 'linear-gradient(180deg, #0a0418 0%, #1a0b2e 40%, #0d0718 100%)', accent: '#ec4899', label: '霓虹都市' },
gradientmesh: { bg: '#0a0a0f', accent: '#6366f1', label: '渐变网格' },
neoncity: { bg: 'linear-gradient(180deg, #fafbfc 0%, #f0f2f5 100%)', accent: '#3b82f6', label: '白日梦境' },
gradientmesh: { bg: '#fafaf8', accent: '#8b5cf6', label: '云石纹理' },
};
export default function AppearanceSettings() {
@@ -293,36 +293,35 @@ export default function AppearanceSettings() {
</div>
)}
{opt.id === 'neoncity' && (
<div className="absolute inset-0" style={{ background: 'linear-gradient(180deg, #0a0418 0%, #1a0b2e 40%, #0d0718 100%)' }}>
<div className="absolute" style={{ top: '25%', left: 0, right: 0, height: 1, background: 'linear-gradient(90deg, transparent, rgba(236,72,153,0.4), transparent)', boxShadow: '0 0 4px rgba(236,72,153,0.3)' }} />
<div className="absolute" style={{ top: '50%', left: 0, right: 0, height: 1, background: 'linear-gradient(90deg, transparent, rgba(6,182,212,0.3), transparent)', boxShadow: '0 0 4px rgba(6,182,212,0.2)' }} />
{[10, 25, 40, 55, 70, 85].map((l, i) => (
<div key={i} className="absolute bottom-0" style={{ left: `${l}%`, width: `${8 + i * 3}px`, height: `${20 + (i % 3) * 12}%`, background: 'rgba(5,3,15,0.9)' }} />
))}
<div className="absolute inset-0" style={{ background: 'linear-gradient(180deg, #fafbfc 0%, #f0f2f5 100%)' }}>
<div className="absolute" style={{ top: '10%', left: '5%', width: '45%', height: '40%', background: 'radial-gradient(ellipse, rgba(147,197,253,0.25), transparent 70%)', filter: 'blur(12px)' }} />
<div className="absolute" style={{ bottom: '10%', right: '5%', width: '50%', height: '45%', background: 'radial-gradient(ellipse, rgba(196,181,253,0.2), transparent 70%)', filter: 'blur(12px)' }} />
<div className="absolute" style={{ top: '40%', left: '50%', width: '30%', height: '30%', background: 'radial-gradient(ellipse, rgba(147,197,253,0.15), transparent 70%)', filter: 'blur(10px)' }} />
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-3/4 rounded-md p-1.5 flex flex-col items-center gap-0.5" style={{ background: 'rgba(10,5,25,0.8)', border: '1px solid rgba(236,72,153,0.2)', backdropFilter: 'blur(4px)' }}>
<div className="w-3 h-1 rounded-sm" style={{ background: 'rgba(236,72,153,0.5)' }} />
<div className="w-5 h-0.5 rounded-sm" style={{ background: 'rgba(6,182,212,0.3)' }} />
<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: 'linear-gradient(90deg, #ec4899, #06b6d4)', opacity: 0.5 }} />
<div className="w-3/4 rounded-lg p-1.5 flex flex-col items-center gap-0.5" style={{ background: 'rgba(255,255,255,0.72)', backdropFilter: 'blur(8px)' }}>
<div className="w-3 h-1 rounded-sm" style={{ background: 'linear-gradient(135deg, #3b82f6, #8b5cf6)' }} />
<div className="w-5 h-0.5 rounded-sm" style={{ background: '#94a3b8' }} />
<div className="w-full h-0.5 rounded-sm" style={{ background: '#f5f5f5' }} />
<div className="w-full h-0.5 rounded-sm" style={{ background: '#f5f5f5' }} />
<div className="w-full h-1 rounded-sm mt-0.5" style={{ background: 'linear-gradient(90deg, #3b82f6, #8b5cf6)', opacity: 0.8 }} />
</div>
</div>
</div>
)}
{opt.id === 'gradientmesh' && (
<div className="absolute inset-0" style={{ background: '#0a0a0f' }}>
<div className="absolute" style={{ top: '-20%', left: '-15%', width: '60%', height: '60%', background: 'radial-gradient(circle, rgba(99,102,241,0.2), transparent 70%)', filter: 'blur(10px)' }} />
<div className="absolute" style={{ bottom: '-15%', right: '-10%', width: '55%', height: '55%', background: 'radial-gradient(circle, rgba(244,63,94,0.15), transparent 70%)', filter: 'blur(10px)' }} />
<div className="absolute" style={{ top: '30%', right: '10%', width: '35%', height: '35%', background: 'radial-gradient(circle, rgba(16,185,129,0.1), transparent 70%)', filter: 'blur(8px)' }} />
<div className="absolute inset-0" style={{ backgroundImage: 'radial-gradient(circle, rgba(255,255,255,0.04) 0.5px, transparent 0.5px)', backgroundSize: '14px 14px' }} />
<div className="absolute inset-0" style={{ background: '#fafaf8' }}>
<div className="absolute" style={{ top: '-20%', left: '-15%', width: '60%', height: '60%', background: 'radial-gradient(circle, rgba(99,102,241,0.06), transparent 70%)', filter: 'blur(10px)' }} />
<div className="absolute" style={{ bottom: '-15%', right: '-10%', width: '55%', height: '55%', background: 'radial-gradient(circle, rgba(244,63,94,0.04), transparent 70%)', filter: 'blur(10px)' }} />
<div className="absolute" style={{ top: '30%', right: '10%', width: '35%', height: '35%', background: 'radial-gradient(circle, rgba(16,185,129,0.05), transparent 70%)', filter: 'blur(8px)' }} />
<div className="absolute" style={{ top: '5%', right: '25%', width: '40%', height: '40%', background: 'radial-gradient(circle, rgba(139,92,246,0.05), transparent 70%)', filter: 'blur(12px)' }} />
<div className="absolute inset-0" style={{ backgroundImage: 'radial-gradient(circle, rgba(0,0,0,0.06) 0.5px, transparent 0.5px)', backgroundSize: '14px 14px' }} />
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-3/4 rounded-md p-1.5 flex flex-col items-center gap-0.5" style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.08)', backdropFilter: 'blur(4px)' }}>
<div className="w-3 h-1 rounded-sm" style={{ background: 'rgba(99,102,241,0.5)' }} />
<div className="w-5 h-0.5 rounded-sm" style={{ background: 'rgba(255,255,255,0.12)' }} />
<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: 'linear-gradient(90deg, #6366f1, #ec4899)', opacity: 0.5 }} />
<div className="w-3/4 rounded-lg p-1.5 flex flex-col items-center gap-0.5" style={{ background: 'rgba(255,255,255,0.85)', boxShadow: '0 2px 8px rgba(0,0,0,0.06)' }}>
<div className="w-3 h-1 rounded-sm" style={{ background: 'linear-gradient(135deg, #3b82f6, #8b5cf6)' }} />
<div className="w-5 h-0.5 rounded-sm" style={{ background: '#9ca3af' }} />
<div className="w-full h-0.5 rounded-sm" style={{ background: '#fff', border: '1px solid #e5e7eb' }} />
<div className="w-full h-0.5 rounded-sm" style={{ background: '#fff', border: '1px solid #e5e7eb' }} />
<div className="w-full h-1 rounded-sm mt-0.5" style={{ background: 'linear-gradient(90deg, #3b82f6, #8b5cf6)', opacity: 0.8 }} />
</div>
</div>
</div>
+2 -2
View File
@@ -34,8 +34,8 @@ export const LOGIN_STYLE_OPTIONS: { id: LoginStyle; name: string; desc: string }
{ id: 'aurora', name: '极光之境', desc: '北极光动态渐变,紫绿流光' },
{ id: 'cosmos', name: '星际漫游', desc: '深邃星空星云,银白粒子' },
{ id: 'inkwash', name: '水墨山水', desc: '东方水墨意境,动态墨痕' },
{ id: 'neoncity', name: '霓虹都市', desc: '赛博朋克城市,彩色霓虹' },
{ id: 'gradientmesh', name: '渐变网格', desc: '柔和渐变网格,现代呼吸感' },
{ id: 'neoncity', name: '白日梦境', desc: '纯白柔和几何,轻盈漂浮' },
{ id: 'gradientmesh', name: '云石纹理', desc: '暖白大理石纹理,优雅质感' },
];
export default function LoginPage() {