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,429 @@
import React from 'react';
import { useLogin } from '@/hooks/useLogin';
/* ──────────────── 极光之境 · 登录组件 ──────────────── */
const KEYFRAMES = `
@keyframes aurora-drift-1 {
0% { transform: translate(-30%, -10%) rotate(-12deg) scaleX(1.2); opacity: 0.35; }
25% { transform: translate(-10%, 5%) rotate(-8deg) scaleX(1.5); opacity: 0.5; }
50% { transform: translate(10%, -5%) rotate(-14deg) scaleX(1.3); opacity: 0.4; }
75% { transform: translate(-5%, 10%) rotate(-10deg) scaleX(1.6); opacity: 0.55; }
100% { transform: translate(-30%, -10%) rotate(-12deg) scaleX(1.2); opacity: 0.35; }
}
@keyframes aurora-drift-2 {
0% { transform: translate(20%, 15%) rotate(8deg) scaleX(1.3); opacity: 0.25; }
30% { transform: translate(-10%, -5%) rotate(12deg) scaleX(1.6); opacity: 0.45; }
60% { transform: translate(5%, 10%) rotate(6deg) scaleX(1.4); opacity: 0.35; }
100% { transform: translate(20%, 15%) rotate(8deg) scaleX(1.3); opacity: 0.25; }
}
@keyframes aurora-drift-3 {
0% { transform: translate(-15%, 25%) rotate(-5deg) scaleX(1.5); opacity: 0.3; }
33% { transform: translate(15%, 5%) rotate(-9deg) scaleX(1.8); opacity: 0.5; }
66% { transform: translate(0%, 20%) rotate(-3deg) scaleX(1.4); opacity: 0.4; }
100% { transform: translate(-15%, 25%) rotate(-5deg) scaleX(1.5); opacity: 0.3; }
}
@keyframes aurora-drift-4 {
0% { transform: translate(10%, -15%) rotate(15deg) scaleX(1.4); opacity: 0.2; }
40% { transform: translate(-20%, 0%) rotate(10deg) scaleX(1.7); opacity: 0.4; }
80% { transform: translate(0%, 8%) rotate(18deg) scaleX(1.5); opacity: 0.3; }
100% { transform: translate(10%, -15%) rotate(15deg) scaleX(1.4); opacity: 0.2; }
}
@keyframes twinkle {
0%, 100% { opacity: 0.15; transform: scale(0.8); }
50% { opacity: 0.9; transform: scale(1.2); }
}
@keyframes float-card {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-6px); }
}
@keyframes pulse-glow {
0%, 100% { box-shadow: 0 0 20px rgba(124, 58, 237, 0.15), 0 0 60px rgba(5, 150, 105, 0.08); }
50% { box-shadow: 0 0 30px rgba(124, 58, 237, 0.25), 0 0 80px rgba(5, 150, 105, 0.15); }
}
`;
/* ──────────────── 星星数据 ──────────────── */
const STARS = [
{ left: '8%', top: '5%', size: 2, delay: 0, duration: 3 },
{ left: '18%', top: '15%', size: 1.5, delay: 0.7, duration: 4 },
{ left: '5%', top: '35%', size: 1, delay: 1.2, duration: 2.5 },
{ left: '25%', top: '8%', size: 2.5, delay: 0.3, duration: 3.5 },
{ left: '75%', top: '6%', size: 1.5, delay: 1.5, duration: 4.5 },
{ left: '88%', top: '12%', size: 2, delay: 0.5, duration: 3 },
{ left: '92%', top: '30%', size: 1, delay: 2, duration: 2.8 },
{ left: '82%', top: '45%', size: 1.5, delay: 0.9, duration: 3.8 },
{ left: '68%', top: '18%', size: 1, delay: 1.8, duration: 2.2 },
{ left: '38%', top: '3%', size: 2, delay: 0.2, duration: 4 },
{ left: '55%', top: '10%', size: 1.5, delay: 1.1, duration: 3.2 },
{ left: '45%', top: '22%', size: 1, delay: 0.6, duration: 2.6 },
{ left: '12%', top: '55%', size: 1, delay: 1.6, duration: 3.6 },
{ left: '95%', top: '55%', size: 1.5, delay: 0.4, duration: 2.4 },
{ left: '60%', top: '30%', size: 1, delay: 2.2, duration: 4.2 },
];
export default function AuroraView(): JSX.Element {
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
return (
<>
<style>{KEYFRAMES}</style>
{/* ═══════════════ 全屏背景容器 ═══════════════ */}
<div
style={{
position: 'relative',
minHeight: '100vh',
width: '100%',
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
background: 'linear-gradient(180deg, #050810 0%, #0a0e1a 30%, #0d1425 60%, #080c14 100%)',
}}
>
{/* ────────── 极光光带 1:紫罗兰色 ────────── */}
<div
style={{
position: 'absolute',
top: '15%',
left: '10%',
width: '80%',
height: '35%',
background: 'linear-gradient(135deg, rgba(168, 85, 247, 0.6) 0%, rgba(124, 58, 237, 0.3) 40%, transparent 80%)',
filter: 'blur(60px)',
borderRadius: '50%',
animation: 'aurora-drift-1 18s ease-in-out infinite',
pointerEvents: 'none',
}}
/>
{/* ────────── 极光光带 2:翠绿色 ────────── */}
<div
style={{
position: 'absolute',
top: '25%',
left: '20%',
width: '75%',
height: '30%',
background: 'linear-gradient(120deg, rgba(16, 185, 129, 0.5) 0%, rgba(5, 150, 105, 0.25) 50%, transparent 90%)',
filter: 'blur(70px)',
borderRadius: '50%',
animation: 'aurora-drift-2 22s ease-in-out infinite',
pointerEvents: 'none',
}}
/>
{/* ────────── 极光光带 3:青蓝色 ────────── */}
<div
style={{
position: 'absolute',
top: '35%',
left: '5%',
width: '70%',
height: '28%',
background: 'linear-gradient(145deg, rgba(6, 182, 212, 0.4) 0%, rgba(6, 182, 212, 0.15) 45%, transparent 85%)',
filter: 'blur(55px)',
borderRadius: '50%',
animation: 'aurora-drift-3 20s ease-in-out infinite',
pointerEvents: 'none',
}}
/>
{/* ────────── 极光光带 4:紫绿混合 ────────── */}
<div
style={{
position: 'absolute',
top: '10%',
left: '30%',
width: '65%',
height: '40%',
background: 'linear-gradient(100deg, rgba(124, 58, 237, 0.35) 0%, rgba(16, 185, 129, 0.2) 50%, rgba(6, 182, 212, 0.15) 100%)',
filter: 'blur(75px)',
borderRadius: '50%',
animation: 'aurora-drift-4 25s ease-in-out infinite',
pointerEvents: 'none',
}}
/>
{/* ────────── 星光点缀 ────────── */}
{STARS.map((star, i) => (
<div
key={i}
style={{
position: 'absolute',
left: star.left,
top: star.top,
width: star.size,
height: star.size,
borderRadius: '50%',
background: '#fff',
boxShadow: `0 0 ${star.size * 2}px rgba(255,255,255,0.5)`,
animation: `twinkle ${star.duration}s ease-in-out ${star.delay}s infinite`,
pointerEvents: 'none',
}}
/>
))}
{/* ═══════════════ 登录卡片 ═══════════════ */}
<div
style={{
position: 'relative',
zIndex: 10,
width: '100%',
maxWidth: 400,
margin: '0 20px',
padding: '44px 36px',
borderRadius: 20,
background: 'rgba(8, 12, 25, 0.65)',
backdropFilter: 'blur(24px)',
WebkitBackdropFilter: 'blur(24px)',
border: '1px solid rgba(255,255,255,0.06)',
boxShadow: '0 0 0 1px rgba(255,255,255,0.04), 0 20px 60px rgba(0,0,0,0.4)',
animation: 'float-card 8s ease-in-out infinite, pulse-glow 6s ease-in-out infinite',
}}
>
{/* ──────── 品牌区域 ──────── */}
<div style={{ textAlign: 'center', marginBottom: 32 }}>
{/* Logo */}
<div
style={{
width: 44,
height: 44,
borderRadius: 12,
background: 'linear-gradient(135deg, #7c3aed 0%, #059669 100%)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: '0 auto 16px',
boxShadow: '0 4px 20px rgba(124, 58, 237, 0.3), 0 2px 10px rgba(5, 150, 105, 0.2)',
}}
>
<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>
{/* 标题 */}
<h1
style={{
fontSize: 22,
fontWeight: 600,
color: '#fff',
margin: '0 0 6px',
letterSpacing: 0.5,
}}
>
SQL审核平台
</h1>
{/* 副标题 */}
<p
style={{
fontSize: 14,
color: 'rgba(255,255,255,0.4)',
margin: 0,
letterSpacing: 0.3,
}}
>
企业级数据库审核与合规管理
</p>
</div>
{/* ──────── 表单区域 ──────── */}
<form onSubmit={handleLogin} noValidate>
{/* 错误提示 */}
{error && (
<div
style={{
marginBottom: 16,
padding: '10px 14px',
borderRadius: 10,
background: 'rgba(239, 68, 68, 0.12)',
border: '1px solid rgba(239, 68, 68, 0.2)',
color: '#f87171',
fontSize: 13,
textAlign: 'center',
}}
>
{error}
</div>
)}
{/* 用户名输入框 */}
<div style={{ marginBottom: 14 }}>
<input
type="text"
placeholder="用户名"
value={username}
onChange={(e) => setUsername(e.target.value)}
disabled={loading}
autoComplete="username"
style={{
width: '100%',
height: 46,
padding: '0 16px',
borderRadius: 10,
background: 'rgba(255,255,255,0.05)',
border: '1px solid rgba(255,255,255,0.08)',
color: '#fff',
fontSize: 14,
outline: 'none',
boxSizing: 'border-box',
transition: 'border-color 0.25s ease, box-shadow 0.25s ease',
fontFamily: 'inherit',
}}
onFocus={(e) => {
e.currentTarget.style.borderColor = 'rgba(124, 58, 237, 0.4)';
e.currentTarget.style.boxShadow = '0 0 0 3px rgba(124, 58, 237, 0.08)';
}}
onBlur={(e) => {
e.currentTarget.style.borderColor = 'rgba(255,255,255,0.08)';
e.currentTarget.style.boxShadow = 'none';
}}
/>
</div>
{/* 密码输入框 */}
<div style={{ marginBottom: 20 }}>
<input
type="password"
placeholder="密码"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={loading}
autoComplete="current-password"
style={{
width: '100%',
height: 46,
padding: '0 16px',
borderRadius: 10,
background: 'rgba(255,255,255,0.05)',
border: '1px solid rgba(255,255,255,0.08)',
color: '#fff',
fontSize: 14,
outline: 'none',
boxSizing: 'border-box',
transition: 'border-color 0.25s ease, box-shadow 0.25s ease',
fontFamily: 'inherit',
}}
onFocus={(e) => {
e.currentTarget.style.borderColor = 'rgba(124, 58, 237, 0.4)';
e.currentTarget.style.boxShadow = '0 0 0 3px rgba(124, 58, 237, 0.08)';
}}
onBlur={(e) => {
e.currentTarget.style.borderColor = 'rgba(255,255,255,0.08)';
e.currentTarget.style.boxShadow = 'none';
}}
/>
</div>
{/* 登录按钮 */}
<button
type="submit"
disabled={!username || !password || loading}
style={{
width: '100%',
height: 46,
borderRadius: 12,
border: 'none',
background: 'linear-gradient(135deg, #7c3aed 0%, #059669 100%)',
color: '#fff',
fontSize: 15,
fontWeight: 500,
cursor: !username || !password || loading ? 'not-allowed' : 'pointer',
opacity: !username || !password ? 0.55 : 1,
transition: 'all 0.3s ease',
fontFamily: 'inherit',
letterSpacing: 0.5,
position: 'relative',
overflow: 'hidden',
}}
onMouseEnter={(e) => {
if (username && password && !loading) {
e.currentTarget.style.transform = 'translateY(-2px)';
e.currentTarget.style.boxShadow = '0 8px 30px rgba(124, 58, 237, 0.35), 0 4px 15px rgba(5, 150, 105, 0.25)';
}
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = 'none';
}}
>
{loading ? (
<span style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}>
<span
style={{
display: 'inline-block',
width: 16,
height: 16,
border: '2px solid rgba(255,255,255,0.3)',
borderTopColor: '#fff',
borderRadius: '50%',
animation: 'spin 0.8s linear infinite',
}}
/>
登录中...
</span>
) : (
'登 录'
)}
</button>
</form>
{/* ──────── 版权信息 ──────── */}
<div
style={{
marginTop: 28,
textAlign: 'center',
fontSize: 12,
color: 'rgba(255,255,255,0.2)',
letterSpacing: 0.3,
}}
>
© 2025 SQL审核平台
</div>
</div>
{/* ──────── 底部辉光装饰 ──────── */}
<div
style={{
position: 'absolute',
bottom: '-20%',
left: '50%',
transform: 'translateX(-50%)',
width: '120%',
height: '50%',
background: 'radial-gradient(ellipse at center, rgba(124, 58, 237, 0.08) 0%, rgba(5, 150, 105, 0.05) 30%, transparent 70%)',
pointerEvents: 'none',
}}
/>
</div>
{/* 全局旋转动画 */}
<style>{`
@keyframes spin {
to { transform: rotate(360deg); }
}
`}</style>
</>
);
}
@@ -0,0 +1,547 @@
import React, { useMemo } from 'react';
import { useLogin } from '@/hooks/useLogin';
/* ── 动画关键帧 ── */
const KEYFRAMES = `
@keyframes star-twinkle {
0%, 100% { opacity: 0.3; transform: scale(0.8); }
50% { opacity: 1; transform: scale(1.2); }
}
@keyframes meteor-fly {
0% { transform: translateX(0) translateY(0) rotate(-45deg); opacity: 1; }
100% { transform: translateX(600px) translateY(600px) rotate(-45deg); opacity: 0; }
}
@keyframes planet-rotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes float-particle {
0%, 100% { transform: translateY(0) translateX(0); opacity: 0.4; }
25% { transform: translateY(-20px) translateX(10px); opacity: 0.8; }
50% { transform: translateY(-40px) translateX(-5px); opacity: 0.5; }
75% { transform: translateY(-20px) translateX(15px); opacity: 0.9; }
}
@keyframes nebula-drift {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(30px, -20px) scale(1.05); }
66% { transform: translate(-20px, 15px) scale(0.95); }
}
@keyframes card-glow {
0%, 100% { box-shadow: 0 0 0 1px rgba(255,255,255,0.03), 0 24px 80px rgba(0,0,0,0.5), 0 0 60px rgba(79,70,229,0.08); }
50% { box-shadow: 0 0 0 1px rgba(255,255,255,0.05), 0 24px 80px rgba(0,0,0,0.5), 0 0 80px rgba(139,92,246,0.12); }
}
@keyframes shooting-star {
0% { transform: translateX(0) translateY(0) rotate(-35deg) scaleX(1); opacity: 1; }
70% { opacity: 0.6; }
100% { transform: translateX(500px) translateY(500px) rotate(-35deg) scaleX(0.5); opacity: 0; }
}
`;
/* ── 类型 ── */
interface Star {
id: number;
x: number;
y: number;
size: number;
opacity: number;
color: string;
twinkle: boolean;
delay: number;
duration: number;
}
interface Particle {
id: number;
x: number;
y: number;
size: number;
delay: number;
duration: number;
}
/* ── 组件 ── */
export default function CosmosView(): JSX.Element {
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
/* 生成静态星星数据 */
const stars = useMemo<Star[]>(() => {
const colors = ['#ffffff', '#dbeafe', '#e0e7ff', '#f3e8ff', '#ede9fe'];
return Array.from({ length: 45 }, (_, i) => ({
id: i,
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * 2.5 + 0.5,
opacity: Math.random() * 0.7 + 0.3,
color: colors[Math.floor(Math.random() * colors.length)],
twinkle: Math.random() > 0.4,
delay: Math.random() * 5,
duration: Math.random() * 2 + 1.5,
}));
}, []);
/* 生成漂浮粒子数据 */
const particles = useMemo<Particle[]>(() => {
return Array.from({ length: 15 }, (_, i) => ({
id: i,
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * 2 + 1,
delay: Math.random() * 6,
duration: Math.random() * 4 + 4,
}));
}, []);
/* 输入框基础样式 */
const inputStyle: React.CSSProperties = {
width: '100%',
height: 48,
padding: '0 16px',
background: 'rgba(255,255,255,0.05)',
border: '1px solid rgba(255,255,255,0.08)',
borderRadius: 12,
color: '#fff',
fontSize: 14,
outline: 'none',
transition: 'all 0.25s ease',
boxSizing: 'border-box',
};
const isFormValid = username.length > 0 && password.length > 0;
return (
<div
style={{
position: 'relative',
width: '100%',
minHeight: '100vh',
overflow: 'hidden',
background: '#030508',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif",
}}
>
<style>{KEYFRAMES}</style>
{/* ── 星云色块 ── */}
<div
style={{
position: 'absolute',
top: '-10%',
left: '-15%',
width: '60%',
height: '60%',
background: 'radial-gradient(circle, rgba(59,130,246,0.15), transparent 70%)',
borderRadius: '50%',
filter: 'blur(100px)',
animation: 'nebula-drift 20s ease-in-out infinite',
pointerEvents: 'none',
}}
/>
<div
style={{
position: 'absolute',
bottom: '-15%',
right: '-10%',
width: '55%',
height: '55%',
background: 'radial-gradient(circle, rgba(139,92,246,0.12), transparent 70%)',
borderRadius: '50%',
filter: 'blur(100px)',
animation: 'nebula-drift 25s ease-in-out infinite reverse',
pointerEvents: 'none',
}}
/>
<div
style={{
position: 'absolute',
top: '30%',
right: '25%',
width: '35%',
height: '35%',
background: 'radial-gradient(circle, rgba(236,72,153,0.08), transparent 70%)',
borderRadius: '50%',
filter: 'blur(100px)',
animation: 'nebula-drift 18s ease-in-out infinite 3s',
pointerEvents: 'none',
}}
/>
{/* ── 星星层 ── */}
{stars.map((star) => (
<div
key={star.id}
style={{
position: 'absolute',
left: `${star.x}%`,
top: `${star.y}%`,
width: star.size,
height: star.size,
borderRadius: '50%',
background: star.color,
opacity: star.twinkle ? undefined : star.opacity,
boxShadow: `0 0 ${star.size * 2}px ${star.color}`,
animation: star.twinkle
? `star-twinkle ${star.duration}s ease-in-out infinite ${star.delay}s`
: 'none',
pointerEvents: 'none',
}}
/>
))}
{/* ── 流星 ── */}
<div
style={{
position: 'absolute',
top: '8%',
left: '5%',
width: 120,
height: 2,
background: 'linear-gradient(90deg, rgba(255,255,255,0.9), rgba(255,255,255,0))',
borderRadius: '0 50% 50% 0',
transform: 'rotate(-45deg)',
animation: 'meteor-fly 4s ease-out infinite 2s',
pointerEvents: 'none',
filter: 'blur(0.5px)',
}}
/>
<div
style={{
position: 'absolute',
top: '15%',
left: '25%',
width: 80,
height: 1.5,
background: 'linear-gradient(90deg, rgba(200,220,255,0.8), rgba(255,255,255,0))',
borderRadius: '0 50% 50% 0',
transform: 'rotate(-35deg)',
animation: 'shooting-star 5s ease-out infinite 7s',
pointerEvents: 'none',
filter: 'blur(0.3px)',
}}
/>
{/* ── 漂浮粒子星尘 ── */}
{particles.map((p) => (
<div
key={`p-${p.id}`}
style={{
position: 'absolute',
left: `${p.x}%`,
top: `${p.y}%`,
width: p.size,
height: p.size,
borderRadius: '50%',
background: 'rgba(200, 210, 255, 0.5)',
boxShadow: '0 0 4px rgba(200,210,255,0.3)',
animation: `float-particle ${p.duration}s ease-in-out infinite ${p.delay}s`,
pointerEvents: 'none',
}}
/>
))}
{/* ── 行星装饰(左下角) ── */}
<div
style={{
position: 'absolute',
bottom: '8%',
left: '5%',
width: 100,
height: 100,
animation: 'planet-rotate 60s linear infinite',
pointerEvents: 'none',
}}
>
{/* 行星本体 */}
<div
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 40,
height: 40,
borderRadius: '50%',
background: 'radial-gradient(circle at 35% 35%, rgba(100,149,237,0.9), rgba(65,45,120,0.8), rgba(30,20,60,0.95))',
boxShadow: 'inset -8px -8px 20px rgba(0,0,0,0.5), 0 0 20px rgba(100,149,237,0.2)',
}}
/>
{/* 行星光环 */}
<div
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%) rotateX(75deg) rotateY(15deg)',
width: 72,
height: 72,
borderRadius: '50%',
border: '2px solid rgba(180,180,255,0.25)',
boxShadow: '0 0 8px rgba(180,180,255,0.15)',
}}
/>
{/* 内环 */}
<div
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%) rotateX(75deg) rotateY(15deg)',
width: 56,
height: 56,
borderRadius: '50%',
border: '1px solid rgba(200,200,255,0.12)',
}}
/>
</div>
{/* ── 小行星(右上角装饰) ── */}
<div
style={{
position: 'absolute',
top: '12%',
right: '12%',
width: 24,
height: 24,
borderRadius: '50%',
background: 'radial-gradient(circle at 40% 40%, rgba(236,200,160,0.7), rgba(160,120,80,0.5))',
boxShadow: 'inset -4px -4px 10px rgba(0,0,0,0.4), 0 0 15px rgba(236,200,160,0.15)',
animation: 'float-particle 10s ease-in-out infinite',
pointerEvents: 'none',
}}
/>
{/* ── 登录卡片 ── */}
<div
style={{
position: 'relative',
zIndex: 10,
marginLeft: '18vw',
maxWidth: 400,
width: '100%',
padding: '48px 36px',
background: 'rgba(5, 8, 16, 0.75)',
backdropFilter: 'blur(30px)',
WebkitBackdropFilter: 'blur(30px)',
borderRadius: 24,
border: '1px solid rgba(255,255,255,0.05)',
animation: 'card-glow 6s ease-in-out infinite',
boxSizing: 'border-box',
}}
>
{/* 品牌区域 */}
<div style={{ textAlign: 'center', marginBottom: 36 }}>
{/* Logo */}
<div
style={{
width: 44,
height: 44,
borderRadius: 14,
background: 'linear-gradient(135deg, #1e3a8a, #7c3aed)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: '0 auto 16px',
boxShadow: '0 4px 20px rgba(79,70,229,0.3), inset 0 1px 0 rgba(255,255,255,0.15)',
}}
>
<svg width="20" height="20" 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>
<h1
style={{
fontSize: 22,
fontWeight: 700,
color: '#fff',
margin: '0 0 8px',
letterSpacing: '0.5px',
}}
>
SQL审核平台
</h1>
<p
style={{
fontSize: 13,
color: 'rgba(255,255,255,0.4)',
margin: 0,
letterSpacing: '0.3px',
}}
>
企业级数据库审核与合规管理
</p>
</div>
{/* 表单区域 */}
<form onSubmit={handleLogin}>
{/* 用户名 */}
<div style={{ marginBottom: 16 }}>
<label
style={{
display: 'block',
fontSize: 12,
fontWeight: 500,
color: 'rgba(255,255,255,0.5)',
marginBottom: 6,
letterSpacing: '0.3px',
}}
>
用户名
</label>
<input
type="text"
placeholder="请输入用户名"
value={username}
onChange={(e) => setUsername(e.target.value)}
style={inputStyle}
onFocus={(e) => {
e.currentTarget.style.borderColor = 'rgba(139,92,246,0.4)';
e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
}}
onBlur={(e) => {
e.currentTarget.style.borderColor = 'rgba(255,255,255,0.08)';
e.currentTarget.style.background = 'rgba(255,255,255,0.05)';
}}
/>
</div>
{/* 密码 */}
<div style={{ marginBottom: 24 }}>
<label
style={{
display: 'block',
fontSize: 12,
fontWeight: 500,
color: 'rgba(255,255,255,0.5)',
marginBottom: 6,
letterSpacing: '0.3px',
}}
>
密码
</label>
<input
type="password"
placeholder="请输入密码"
value={password}
onChange={(e) => setPassword(e.target.value)}
style={inputStyle}
onFocus={(e) => {
e.currentTarget.style.borderColor = 'rgba(139,92,246,0.4)';
e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
}}
onBlur={(e) => {
e.currentTarget.style.borderColor = 'rgba(255,255,255,0.08)';
e.currentTarget.style.background = 'rgba(255,255,255,0.05)';
}}
/>
</div>
{/* 登录按钮 */}
<button
type="submit"
disabled={!isFormValid || loading}
style={{
width: '100%',
height: 48,
borderRadius: 12,
border: 'none',
background: isFormValid
? 'linear-gradient(135deg, #4f46e5, #8b5cf6)'
: 'rgba(255,255,255,0.08)',
color: isFormValid ? '#fff' : 'rgba(255,255,255,0.3)',
fontSize: 15,
fontWeight: 600,
cursor: isFormValid && !loading ? 'pointer' : 'not-allowed',
transition: 'all 0.3s ease',
position: 'relative',
overflow: 'hidden',
letterSpacing: '0.5px',
boxShadow: isFormValid
? '0 4px 20px rgba(79,70,229,0.35), inset 0 1px 0 rgba(255,255,255,0.15)'
: 'none',
}}
onMouseEnter={(e) => {
if (isFormValid && !loading) {
e.currentTarget.style.transform = 'translateY(-2px)';
e.currentTarget.style.boxShadow = '0 8px 30px rgba(79,70,229,0.45), inset 0 1px 0 rgba(255,255,255,0.2)';
}
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = isFormValid
? '0 4px 20px rgba(79,46,229,0.35), inset 0 1px 0 rgba(255,255,255,0.15)'
: 'none';
}}
>
{loading ? (
<span style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}>
<span
style={{
display: 'inline-block',
width: 16,
height: 16,
border: '2px solid rgba(255,255,255,0.3)',
borderTopColor: '#fff',
borderRadius: '50%',
animation: 'spin 0.8s linear infinite',
}}
/>
登录中...
</span>
) : (
'登 录'
)}
</button>
{/* 错误提示 */}
{error && (
<div
style={{
marginTop: 14,
padding: '10px 14px',
borderRadius: 10,
background: 'rgba(239,68,68,0.1)',
border: '1px solid rgba(239,68,68,0.15)',
color: '#f87171',
fontSize: 13,
textAlign: 'center',
}}
>
{error}
</div>
)}
</form>
{/* 底部版权 */}
<p
style={{
marginTop: 28,
textAlign: 'center',
fontSize: 11,
color: 'rgba(255,255,255,0.2)',
letterSpacing: '0.3px',
}}
>
&copy; 2025 SQL审核平台 — 企业级数据库安全合规
</p>
</div>
{/* 全局旋转动画补充 */}
<style>{`
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`}</style>
</div>
);
}
@@ -0,0 +1,362 @@
import React, { useState } from 'react';
import { useLogin } from '@/hooks/useLogin';
/* ------------------------------------------------------------------ */
/* KEYFRAMES */
/* ------------------------------------------------------------------ */
const KEYFRAMES = `
@keyframes mesh-drift-1 {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(30px, 20px); }
}
@keyframes mesh-drift-2 {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(-25px, -30px); }
}
@keyframes mesh-drift-3 {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(20px, -25px); }
}
@keyframes mesh-drift-4 {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(-30px, 20px); }
}
@keyframes mesh-drift-5 {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(25px, 30px); }
}
@keyframes card-glow {
0%, 100% { opacity: 0.4; }
50% { opacity: 0.7; }
}
@keyframes input-glow-pulse {
0%, 100% { opacity: 0.3; }
50% { opacity: 0.6; }
}
`;
/* ------------------------------------------------------------------ */
/* COMPONENT */
/* ------------------------------------------------------------------ */
export default function GradientMeshView(): React.JSX.Element {
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
const [focusedField, setFocusedField] = useState<string | null>(null);
const [hoverBtn, setHoverBtn] = useState(false);
const isReady = username.trim().length > 0 && password.trim().length > 0;
/* ---- gradient mesh blobs ---- */
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%)',
filter: 'blur(90px)', pointerEvents: 'none',
animation: 'mesh-drift-2 10s 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%',
width: '550px', height: '550px',
background: 'radial-gradient(circle, rgba(139, 92, 246, 0.2), transparent 70%)',
filter: 'blur(95px)', pointerEvents: 'none',
animation: 'mesh-drift-5 13s 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)',
borderRadius: 12,
padding: '14px 18px',
color: '#fff',
fontSize: 15,
outline: 'none',
transition: 'all 0.25s ease',
boxSizing: 'border-box',
};
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)',
});
/* ---- 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>
{/* ===================== ROOT CONTAINER ===================== */}
<div
style={{
minHeight: '100vh',
width: '100%',
position: 'relative',
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#0a0a0f',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
}}
>
{/* ---- gradient mesh blobs ---- */}
{blobs.map((b, i) => (
<div key={i} style={b} />
))}
{/* ---- subtle dot-grid texture ---- */}
<div
style={{
position: 'fixed',
inset: 0,
pointerEvents: 'none',
opacity: 0.03,
backgroundImage:
'radial-gradient(circle, rgba(255,255,255,0.8) 1px, transparent 1px)',
backgroundSize: '24px 24px',
}}
/>
{/* ===================== LOGIN CARD ===================== */}
<div
style={{
position: 'relative',
width: '100%',
maxWidth: 400,
margin: '0 20px',
background: 'rgba(255, 255, 255, 0.03)',
backdropFilter: 'blur(40px) saturate(180%)',
WebkitBackdropFilter: 'blur(40px) saturate(180%)',
border: '1px solid rgba(255, 255, 255, 0.08)',
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)',
zIndex: 10,
}}
>
{/* top accent line */}
<div style={topAccent} />
{/* inner glow */}
<div style={innerGlow} />
{/* ---- brand ---- */}
<div style={{ textAlign: 'center', marginBottom: 36 }}>
{/* Logo */}
<div
style={{
width: 48,
height: 48,
borderRadius: 16,
background: 'linear-gradient(135deg, #6366f1, #f43f5e)',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 20,
boxShadow: '0 8px 24px rgba(99,102,241,0.25)',
}}
>
<svg
width={20}
height={20}
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>
<h1
style={{
color: '#fff',
fontSize: 24,
fontWeight: 700,
letterSpacing: '-0.5px',
margin: '0 0 8px 0',
}}
>
SQL审核平台
</h1>
<p
style={{
color: 'rgba(255,255,255,0.35)',
fontSize: 14,
letterSpacing: '0.3px',
margin: 0,
}}
>
企业级数据库审核与合规管理
</p>
</div>
{/* ---- form ---- */}
<form
onSubmit={handleLogin}
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>
{/* 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>
{/* error */}
{error && (
<div
style={{
background: 'rgba(239,68,68,0.08)',
color: '#f87171',
borderRadius: 10,
padding: '10px 14px',
fontSize: 13,
}}
>
{error}
</div>
)}
{/* submit */}
<button
type="submit"
disabled={!isReady || loading}
onMouseEnter={() => setHoverBtn(true)}
onMouseLeave={() => setHoverBtn(false)}
style={{
width: '100%',
marginTop: 4,
padding: '14px 24px',
borderRadius: 12,
border: 'none',
fontSize: 15,
fontWeight: 600,
color: '#fff',
cursor: isReady && !loading ? 'pointer' : 'not-allowed',
opacity: isReady ? 1 : 0.5,
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #ec4899 100%)',
boxShadow: hoverBtn && isReady && !loading
? '0 8px 32px rgba(99,102,241,0.45)'
: '0 4px 20px rgba(99,102,241,0.3)',
transform: hoverBtn && isReady && !loading ? 'translateY(-1px)' : 'translateY(0)',
transition: 'all 0.25s ease',
}}
>
{loading ? '登录中...' : '登录'}
</button>
</form>
{/* ---- copyright ---- */}
<p
style={{
textAlign: 'center',
color: 'rgba(255,255,255,0.2)',
fontSize: 12,
margin: '28px 0 0 0',
}}
>
&copy; 2025 SQL审核平台
</p>
</div>
</div>
</>
);
}
@@ -0,0 +1,479 @@
import React from 'react';
import { useLogin } from '@/hooks/useLogin';
/* ------------------------------------------------------------------ */
/* KEYFRAMES */
/* ------------------------------------------------------------------ */
const KEYFRAMES = `
@keyframes ink-spread {
0%, 100% {
transform: scale(1);
opacity: 0.45;
}
50% {
transform: scale(1.15);
opacity: 0.6;
}
}
@keyframes mist-flow {
0% {
transform: translateX(-8%);
}
50% {
transform: translateX(4%);
}
100% {
transform: translateX(-8%);
}
}
@keyframes gold-shimmer {
0%, 100% {
opacity: 0.35;
transform: scaleX(1);
}
50% {
opacity: 0.8;
transform: scaleX(1.06);
}
}
`;
/* ------------------------------------------------------------------ */
/* Component */
/* ------------------------------------------------------------------ */
export default function InkWashView(): 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: '#f5f0e8',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif',
}}
>
{/* ────────── 水墨晕染装饰 ────────── */}
{/* 左上角淡墨点 */}
<div
style={{
position: 'absolute',
top: '-8%',
left: '-6%',
width: '50vw',
height: '50vh',
borderRadius: '50%',
background: 'radial-gradient(ellipse, rgba(160, 150, 135, 0.25) 0%, transparent 70%)',
filter: 'blur(80px)',
animation: 'ink-spread 10s ease-in-out infinite',
pointerEvents: 'none',
}}
/>
{/* 右下角浓墨点 */}
<div
style={{
position: 'absolute',
bottom: '-10%',
right: '-8%',
width: '55vw',
height: '55vh',
borderRadius: '50%',
background: 'radial-gradient(ellipse, rgba(80, 72, 60, 0.35) 0%, transparent 70%)',
filter: 'blur(90px)',
animation: 'ink-spread 14s ease-in-out infinite reverse',
pointerEvents: 'none',
}}
/>
{/* 中央偏上淡淡墨痕 */}
<div
style={{
position: 'absolute',
top: '18%',
left: '35%',
width: '30vw',
height: '20vh',
borderRadius: '50%',
background: 'radial-gradient(ellipse, rgba(140, 130, 115, 0.15) 0%, transparent 70%)',
filter: 'blur(70px)',
animation: 'ink-spread 12s ease-in-out infinite 2s',
pointerEvents: 'none',
}}
/>
{/* ────────── 远山轮廓 ────────── */}
{/* 最远山 */}
<div
style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '40%',
background: 'linear-gradient(to top, rgba(180, 175, 165, 0.4) 0%, transparent 60%)',
borderRadius: '50% 50% 0 0 / 80px 80px 0 0',
pointerEvents: 'none',
}}
/>
{/* 中山 */}
<div
style={{
position: 'absolute',
bottom: 0,
left: '-10%',
right: '-5%',
height: '25%',
background: 'linear-gradient(to top, rgba(120, 115, 108, 0.5) 0%, transparent 50%)',
borderRadius: '40% 60% 0 0 / 60px 50px 0 0',
pointerEvents: 'none',
}}
/>
{/* 近山 */}
<div
style={{
position: 'absolute',
bottom: 0,
left: '-15%',
right: '-15%',
height: '15%',
background: 'linear-gradient(to top, rgba(60, 58, 52, 0.6) 0%, transparent 40%)',
borderRadius: '60% 40% 0 0 / 40px 30px 0 0',
pointerEvents: 'none',
}}
/>
{/* ────────── 雾气效果 ────────── */}
<div
style={{
position: 'absolute',
bottom: '8%',
left: 0,
right: 0,
height: '22%',
background: 'linear-gradient(to top, rgba(245, 240, 232, 0.85) 0%, rgba(245, 240, 232, 0.3) 50%, transparent 100%)',
animation: 'mist-flow 18s ease-in-out infinite',
pointerEvents: 'none',
}}
/>
{/* ────────── 金色点缀线(水面微光) ────────── */}
<div
style={{
position: 'absolute',
bottom: '12%',
left: '20%',
width: '25%',
height: '1px',
background: '#c9a96e',
opacity: 0.35,
animation: 'gold-shimmer 6s ease-in-out infinite',
pointerEvents: 'none',
}}
/>
<div
style={{
position: 'absolute',
bottom: '15%',
right: '18%',
width: '18%',
height: '1px',
background: '#c9a96e',
opacity: 0.3,
animation: 'gold-shimmer 8s ease-in-out infinite 1.5s',
pointerEvents: 'none',
}}
/>
{/* ────────── 竖排 SQL 背景装饰 ────────── */}
<div
style={{
position: 'absolute',
top: '12%',
right: '8%',
writingMode: 'vertical-rl',
fontSize: 72,
fontWeight: 700,
color: 'rgba(45, 42, 36, 0.04)',
letterSpacing: 12,
userSelect: 'none',
pointerEvents: 'none',
fontFamily: 'Georgia, "Times New Roman", serif',
}}
>
SQL
</div>
<div
style={{
position: 'absolute',
bottom: '18%',
left: '6%',
writingMode: 'vertical-rl',
fontSize: 56,
fontWeight: 700,
color: 'rgba(45, 42, 36, 0.03)',
letterSpacing: 8,
userSelect: 'none',
pointerEvents: 'none',
fontFamily: 'Georgia, "Times New Roman", serif',
}}
>
AUDIT
</div>
{/* ===================== 登录卡片 ===================== */}
<div
style={{
position: 'relative',
zIndex: 10,
width: '100%',
maxWidth: 380,
margin: '0 20px',
marginTop: '-6vh',
background: 'rgba(255, 253, 248, 0.85)',
backdropFilter: 'blur(16px)',
WebkitBackdropFilter: 'blur(16px)',
border: '1px solid rgba(201, 169, 110, 0.2)',
borderRadius: 16,
padding: '44px 36px',
boxShadow: '0 4px 30px rgba(0,0,0,0.06), 0 0 0 1px rgba(255,255,255,0.8)',
}}
>
{/* ──── 品牌区域 ──── */}
<div style={{ textAlign: 'center', marginBottom: 32 }}>
{/* Logo */}
<div
style={{
width: 44,
height: 44,
borderRadius: 10,
background: 'linear-gradient(135deg, #3a362e, #1f1d19)',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 18,
boxShadow: '0 2px 8px rgba(0,0,0,0.12)',
}}
>
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#c9a96e"
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>
{/* 标题 */}
<h1
style={{
fontSize: 22,
fontWeight: 600,
color: '#2d2a24',
margin: '0 0 6px',
letterSpacing: 1,
}}
>
SQL审核平台
</h1>
{/* 副标题 */}
<p
style={{
fontSize: 13,
color: '#8a8478',
margin: 0,
letterSpacing: 0.5,
}}
>
企业级数据库审核与合规管理
</p>
</div>
{/* ──── 中国风细线装饰(上方) ──── */}
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 24, gap: 8 }}>
<div style={{ width: 32, height: 1, background: 'linear-gradient(to left, #c9a96e, transparent)' }} />
<div style={{ width: 4, height: 4, borderRadius: '50%', background: '#c9a96e', opacity: 0.5 }} />
<div style={{ width: 32, height: 1, background: 'linear-gradient(to right, #c9a96e, transparent)' }} />
</div>
{/* ──── 表单 ──── */}
<form onSubmit={handleLogin}>
{/* 用户名 */}
<div style={{ marginBottom: 16 }}>
<label
style={{
display: 'block',
fontSize: 13,
fontWeight: 500,
color: '#5a5448',
marginBottom: 6,
}}
>
用户名
</label>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="请输入用户名"
style={{
width: '100%',
height: 44,
padding: '0 14px',
fontSize: 14,
borderRadius: 10,
outline: 'none',
boxSizing: 'border-box',
transition: 'border-color 0.3s, box-shadow 0.3s',
background: '#faf8f4',
border: '1px solid #e5e0d5',
color: '#2d2a24',
}}
onFocus={(e) => {
e.currentTarget.style.borderColor = '#c9a96e';
e.currentTarget.style.boxShadow = '0 0 0 3px rgba(201, 169, 110, 0.12)';
}}
onBlur={(e) => {
e.currentTarget.style.borderColor = '#e5e0d5';
e.currentTarget.style.boxShadow = 'none';
}}
/>
</div>
{/* 密码 */}
<div style={{ marginBottom: 20 }}>
<label
style={{
display: 'block',
fontSize: 13,
fontWeight: 500,
color: '#5a5448',
marginBottom: 6,
}}
>
密码
</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="请输入密码"
style={{
width: '100%',
height: 44,
padding: '0 14px',
fontSize: 14,
borderRadius: 10,
outline: 'none',
boxSizing: 'border-box',
transition: 'border-color 0.3s, box-shadow 0.3s',
background: '#faf8f4',
border: '1px solid #e5e0d5',
color: '#2d2a24',
}}
onFocus={(e) => {
e.currentTarget.style.borderColor = '#c9a96e';
e.currentTarget.style.boxShadow = '0 0 0 3px rgba(201, 169, 110, 0.12)';
}}
onBlur={(e) => {
e.currentTarget.style.borderColor = '#e5e0d5';
e.currentTarget.style.boxShadow = 'none';
}}
/>
</div>
{/* 错误提示 */}
{error && (
<div
style={{
marginBottom: 16,
padding: '10px 14px',
borderRadius: 8,
background: '#fef2f2',
color: '#dc2626',
fontSize: 13,
lineHeight: 1.5,
}}
>
{error}
</div>
)}
{/* 登录按钮 */}
<button
type="submit"
disabled={!username || !password || loading}
style={{
width: '100%',
height: 46,
fontSize: 15,
fontWeight: 500,
color: '#fff',
border: 'none',
borderRadius: 10,
cursor: !username || !password || loading ? 'not-allowed' : 'pointer',
opacity: !username || !password || loading ? 0.65 : 1,
background: 'linear-gradient(135deg, #2d2a24, #4a453d)',
transition: 'all 0.3s ease',
letterSpacing: 1,
position: 'relative',
overflow: 'hidden',
}}
onMouseEnter={(e) => {
if (username && password && !loading) {
e.currentTarget.style.background = 'linear-gradient(135deg, #3a362e, #5a554d)';
e.currentTarget.style.transform = 'translateY(-1px)';
e.currentTarget.style.boxShadow = '0 4px 16px rgba(45, 42, 36, 0.25)';
}
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'linear-gradient(135deg, #2d2a24, #4a453d)';
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = 'none';
}}
>
{loading ? '登录中...' : '登 录'}
</button>
</form>
{/* ──── 中国风细线装饰(下方) ──── */}
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', marginTop: 24, gap: 8 }}>
<div style={{ width: 24, height: 1, background: 'linear-gradient(to left, #c9a96e, transparent)' }} />
<div style={{ width: 3, height: 3, borderRadius: '50%', background: '#c9a96e', opacity: 0.4 }} />
<div style={{ width: 24, height: 1, background: 'linear-gradient(to right, #c9a96e, transparent)' }} />
</div>
{/* ──── 版权信息 ──── */}
<p
style={{
textAlign: 'center',
fontSize: 12,
color: '#a09a8e',
marginTop: 20,
marginBottom: 0,
letterSpacing: 0.3,
}}
>
&copy; SQL Audit Platform. All rights reserved.
</p>
</div>
</div>
</>
);
}
@@ -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>
</>
);
}
+94
View File
@@ -12,6 +12,11 @@ const LOGIN_PREVIEW_COLORS: Record<LoginStyle, { bg: string; accent: string; lab
sidepanel: { bg: 'linear-gradient(180deg, #020810 0%, #062030 100%)', accent: '#06b6d4', label: '深海鲸歌' }, 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' },
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: '渐变网格' },
}; };
export default function AppearanceSettings() { export default function AppearanceSettings() {
@@ -234,6 +239,95 @@ export default function AppearanceSettings() {
</div> </div>
</div> </div>
)} )}
{opt.id === 'aurora' && (
<div className="absolute inset-0" style={{ background: 'linear-gradient(180deg, #050810 0%, #0a0e1a 30%, #0d1425 60%, #080c14 100%)' }}>
<div className="absolute" style={{ top: '20%', left: '-10%', width: '120%', height: '25%', background: 'linear-gradient(90deg, transparent, rgba(124,58,237,0.25), rgba(16,185,129,0.2), transparent)', borderRadius: '50%', filter: 'blur(10px)', transform: 'rotate(-5deg)' }} />
<div className="absolute" style={{ top: '35%', left: '-5%', width: '110%', height: '20%', background: 'linear-gradient(90deg, transparent, rgba(6,182,212,0.2), rgba(168,85,247,0.15), transparent)', borderRadius: '50%', filter: 'blur(8px)', transform: 'rotate(3deg)' }} />
{[15, 30, 55, 75].map(t => (
<div key={t} className="absolute rounded-full" style={{ width: 2, height: 2, background: 'rgba(255,255,255,0.6)', top: `${t}%`, left: `${20 + t}%`, boxShadow: '0 0 3px rgba(255,255,255,0.4)' }} />
))}
<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(8,12,25,0.6)', border: '1px solid rgba(255,255,255,0.06)', backdropFilter: 'blur(4px)' }}>
<div className="w-3 h-1 rounded-sm" style={{ background: 'rgba(16,185,129,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, #7c3aed, #059669)', opacity: 0.5 }} />
</div>
</div>
</div>
)}
{opt.id === 'cosmos' && (
<div className="absolute inset-0" style={{ background: '#030508' }}>
<div className="absolute" style={{ top: '10%', left: '10%', width: '50%', height: '50%', background: 'radial-gradient(circle, rgba(59,130,246,0.15), transparent 70%)', filter: 'blur(8px)' }} />
<div className="absolute" style={{ bottom: '10%', right: '5%', width: '45%', height: '45%', background: 'radial-gradient(circle, rgba(139,92,246,0.12), transparent 70%)', filter: 'blur(8px)' }} />
{[10, 25, 40, 60, 80].map(t => (
<div key={t} className="absolute rounded-full" style={{ width: t % 3 === 0 ? 2.5 : 1.5, height: t % 3 === 0 ? 2.5 : 1.5, background: t % 2 === 0 ? 'rgba(147,197,253,0.8)' : '#fff', top: `${t}%`, left: `${15 + t * 0.8}%`, boxShadow: '0 0 3px rgba(255,255,255,0.3)' }} />
))}
<div className="absolute" style={{ top: '5%', right: '20%', width: '20%', height: 1, background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.6), transparent)', transform: 'rotate(-25deg)' }} />
<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(5,8,16,0.7)', border: '1px solid rgba(255,255,255,0.05)', backdropFilter: 'blur(4px)' }}>
<div className="w-3 h-1 rounded-sm" style={{ background: 'rgba(139,92,246,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, #4f46e5, #8b5cf6)', opacity: 0.5 }} />
</div>
</div>
</div>
)}
{opt.id === 'inkwash' && (
<div className="absolute inset-0" style={{ background: 'linear-gradient(180deg, #f5f0e8 0%, #ebe4d6 100%)' }}>
<div className="absolute bottom-0 left-0 right-0" style={{ height: '35%', background: 'linear-gradient(to top, rgba(180,175,165,0.5) 0%, transparent 60%)', borderRadius: '50% 50% 0 0 / 20% 20% 0 0' }} />
<div className="absolute bottom-0 left-0 right-0" style={{ height: '20%', background: 'linear-gradient(to top, rgba(120,115,108,0.5) 0%, transparent 50%)', borderRadius: '50% 50% 0 0 / 30% 30% 0 0' }} />
<div className="absolute" style={{ top: '5%', left: '5%', width: '30%', height: '25%', background: 'radial-gradient(circle, rgba(60,58,52,0.08), transparent 70%)', filter: 'blur(8px)' }} />
<div className="absolute bottom-[30%] left-[10%] right-[10%] h-px" style={{ background: 'linear-gradient(90deg, transparent, rgba(201,169,110,0.4), transparent)' }} />
<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,253,248,0.85)', border: '1px solid rgba(201,169,110,0.2)', backdropFilter: 'blur(4px)' }}>
<div className="w-3 h-1 rounded-sm" style={{ background: 'rgba(45,42,36,0.5)' }} />
<div className="w-5 h-0.5 rounded-sm" style={{ background: 'rgba(138,132,120,0.4)' }} />
<div className="w-full h-0.5 rounded-sm" style={{ background: 'rgba(45,42,36,0.06)' }} />
<div className="w-full h-0.5 rounded-sm" style={{ background: 'rgba(45,42,36,0.06)' }} />
<div className="w-full h-1 rounded-sm mt-0.5" style={{ background: 'linear-gradient(90deg, #2d2a24, #4a453d)', opacity: 0.5 }} />
</div>
</div>
</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 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>
</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 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>
</div>
</div>
)}
</div> </div>
{/* Active check mark */} {/* Active check mark */}
+17 -2
View File
@@ -4,8 +4,13 @@ import CenteredCard from '@/components/login/CenteredCard';
import SidePanel from '@/components/login/SidePanel'; import SidePanel from '@/components/login/SidePanel';
import FullImage from '@/components/login/FullImage'; import FullImage from '@/components/login/FullImage';
import MinimalDark from '@/components/login/MinimalDark'; import MinimalDark from '@/components/login/MinimalDark';
import AuroraView from '@/components/login/AuroraView';
import CosmosView from '@/components/login/CosmosView';
import InkWashView from '@/components/login/InkWashView';
import NeonCityView from '@/components/login/NeonCityView';
import GradientMeshView from '@/components/login/GradientMeshView';
export type LoginStyle = 'split' | 'centered' | 'sidepanel' | 'fullimage' | 'minimal'; export type LoginStyle = 'split' | 'centered' | 'sidepanel' | 'fullimage' | 'minimal' | 'aurora' | 'cosmos' | 'inkwash' | 'neoncity' | 'gradientmesh';
const LOGIN_STYLES: Record<LoginStyle, React.ComponentType> = { const LOGIN_STYLES: Record<LoginStyle, React.ComponentType> = {
split: SplitView, split: SplitView,
@@ -13,14 +18,24 @@ const LOGIN_STYLES: Record<LoginStyle, React.ComponentType> = {
sidepanel: SidePanel, sidepanel: SidePanel,
fullimage: FullImage, fullimage: FullImage,
minimal: MinimalDark, minimal: MinimalDark,
aurora: AuroraView,
cosmos: CosmosView,
inkwash: InkWashView,
neoncity: NeonCityView,
gradientmesh: GradientMeshView,
}; };
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: '发光网格晶格,Tron 科技感' }, { id: 'split', name: '经典分屏', desc: '左黑右白,晶格科技感' },
{ 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: '优雅暗黑,粉紫流动曲线' },
{ id: 'aurora', name: '极光之境', desc: '北极光动态渐变,紫绿流光' },
{ id: 'cosmos', name: '星际漫游', desc: '深邃星空星云,银白粒子' },
{ id: 'inkwash', name: '水墨山水', desc: '东方水墨意境,动态墨痕' },
{ id: 'neoncity', name: '霓虹都市', desc: '赛博朋克城市,彩色霓虹' },
{ id: 'gradientmesh', name: '渐变网格', desc: '柔和渐变网格,现代呼吸感' },
]; ];
export default function LoginPage() { export default function LoginPage() {