feat: 登录页扩展至10种风格 — 新增极光之境/星际漫游/水墨山水/霓虹都市/渐变网格

- 新增 AuroraView.tsx: 4条极光光带+15星光+毛玻璃卡片
- 新增 CosmosView.tsx: 3星云+45颗星星+2流星+行星装饰
- 新增 InkWashView.tsx: 三层远山+水墨晕染+金色点缀+亮色表单
- 新增 NeonCityView.tsx: 城市剪影+霓虹灯管+雨滴+赛博朋克风格
- 新增 GradientMeshView.tsx: 5渐变光晕+点阵纹理+呼吸动画
- LoginPage.tsx: LoginStyle类型扩展10种+映射+选项
- AppearanceSettings.tsx: 预览颜色+缩略图10种
This commit is contained in:
logan committed 2026-05-27 17:52:34 +08:00
1 parent 872c204e80
commit bf5c077b5b
7 files changed
+2433 -2

No files matched your search

@@ -0,0 +1,505 @@
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 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 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); }
}
`;
// 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'],
];
export default function NeonCityView(): JSX.Element {
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
return (
<>
<style>{KEYFRAMES}</style>
<div
style={{
minHeight: '100vh',
position: 'relative',
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'linear-gradient(180deg, #0a0418 0%, #1a0b2e 40%, #0d0718 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) => (
<div
key={`neon-${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',
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={{
position: 'relative',
zIndex: 10,
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',
}}
>
{/* 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 }}>
{/* Logo */}
<div
style={{
width: 44,
height: 44,
borderRadius: 12,
background: 'linear-gradient(135deg, #ec4899, #06b6d4)',
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)',
}}
>
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#fff"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<ellipse cx="12" cy="5" rx="9" ry="3" />
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
</svg>
</div>
{/* Title */}
<h1
style={{
fontSize: 24,
fontWeight: 700,
color: '#fff',
margin: '0 0 8px',
letterSpacing: '0.5px',
animation: 'neon-glow-text 3s ease-in-out infinite',
}}
>
SQL审核平台
</h1>
{/* Subtitle */}
<p
style={{
fontSize: 14,
color: 'rgba(6, 182, 212, 0.8)',
margin: 0,
letterSpacing: '0.3px',
}}
>
企业级数据库审核与合规管理
</p>
</div>
{/* Login form */}
<form onSubmit={handleLogin}>
{/* Username input */}
<div style={{ marginBottom: 18 }}>
<label
htmlFor="username"
style={{
display: 'block',
fontSize: 13,
fontWeight: 500,
color: 'rgba(255,255,255,0.7)',
marginBottom: 8,
letterSpacing: '0.3px',
}}
>
用户名
</label>
<input
id="username"
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="请输入用户名"
autoComplete="username"
style={{
width: '100%',
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)',
borderRadius: 10,
outline: 'none',
boxSizing: 'border-box',
transition: 'border-color 0.3s ease, box-shadow 0.3s 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)';
}}
onBlur={(e) => {
e.currentTarget.style.borderColor = 'rgba(255,255,255,0.08)';
e.currentTarget.style.boxShadow = 'none';
}}
/>
</div>
{/* Password input */}
<div style={{ marginBottom: 24 }}>
<label
htmlFor="password"
style={{
display: 'block',
fontSize: 13,
fontWeight: 500,
color: 'rgba(255,255,255,0.7)',
marginBottom: 8,
letterSpacing: '0.3px',
}}
>
密码
</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="请输入密码"
autoComplete="current-password"
style={{
width: '100%',
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)',
borderRadius: 10,
outline: 'none',
boxSizing: 'border-box',
transition: 'border-color 0.3s ease, box-shadow 0.3s 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)';
}}
onBlur={(e) => {
e.currentTarget.style.borderColor = 'rgba(255,255,255,0.08)';
e.currentTarget.style.boxShadow = 'none';
}}
/>
</div>
{/* Error message */}
{error && (
<div
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',
fontSize: 13,
textShadow: '0 0 8px rgba(239, 68, 68, 0.4)',
animation: 'neon-flicker 0.5s ease-in-out',
}}
>
{error}
</div>
)}
{/* Login button */}
<button
type="submit"
disabled={!username || !password || loading}
style={{
width: '100%',
height: 48,
fontSize: 15,
fontWeight: 600,
color: '#fff',
background: 'linear-gradient(135deg, #ec4899, #06b6d4)',
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',
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)';
}
}}
onMouseLeave={(e) => {
e.currentTarget.style.boxShadow = '0 0 15px rgba(236, 72, 153, 0.4)';
e.currentTarget.style.transform = 'translateY(0)';
}}
>
{loading ? (
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
style={{ animation: 'spin 1s linear infinite' }}
>
<style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
</svg>
登录中...
</span>
) : (
'登 录'
)}
</button>
</form>
{/* Copyright */}
<p
style={{
textAlign: 'center',
fontSize: 12,
color: 'rgba(255,255,255,0.25)',
marginTop: 28,
marginBottom: 0,
}}
>
© SQL审核平台 · 安全合规的数据库管理
</p>
</div>
</div>
</>
);
}