From bf5c077b5b6eaafeee5170694cfadcb5a54c753e Mon Sep 17 00:00:00 2001 From: logan Date: Wed, 27 May 2026 17:52:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=99=BB=E5=BD=95=E9=A1=B5=E6=89=A9?= =?UTF-8?q?=E5=B1=95=E8=87=B310=E7=A7=8D=E9=A3=8E=E6=A0=BC=20=E2=80=94=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=9E=81=E5=85=89=E4=B9=8B=E5=A2=83/?= =?UTF-8?q?=E6=98=9F=E9=99=85=E6=BC=AB=E6=B8=B8/=E6=B0=B4=E5=A2=A8?= =?UTF-8?q?=E5=B1=B1=E6=B0=B4/=E9=9C=93=E8=99=B9=E9=83=BD=E5=B8=82/?= =?UTF-8?q?=E6=B8=90=E5=8F=98=E7=BD=91=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 AuroraView.tsx: 4条极光光带+15星光+毛玻璃卡片 - 新增 CosmosView.tsx: 3星云+45颗星星+2流星+行星装饰 - 新增 InkWashView.tsx: 三层远山+水墨晕染+金色点缀+亮色表单 - 新增 NeonCityView.tsx: 城市剪影+霓虹灯管+雨滴+赛博朋克风格 - 新增 GradientMeshView.tsx: 5渐变光晕+点阵纹理+呼吸动画 - LoginPage.tsx: LoginStyle类型扩展10种+映射+选项 - AppearanceSettings.tsx: 预览颜色+缩略图10种 --- frontend/src/components/login/AuroraView.tsx | 429 ++++++++++++++ frontend/src/components/login/CosmosView.tsx | 547 ++++++++++++++++++ .../src/components/login/GradientMeshView.tsx | 362 ++++++++++++ frontend/src/components/login/InkWashView.tsx | 479 +++++++++++++++ .../src/components/login/NeonCityView.tsx | 505 ++++++++++++++++ frontend/src/pages/AppearanceSettings.tsx | 94 +++ frontend/src/pages/LoginPage.tsx | 19 +- 7 files changed, 2433 insertions(+), 2 deletions(-) create mode 100644 frontend/src/components/login/AuroraView.tsx create mode 100644 frontend/src/components/login/CosmosView.tsx create mode 100644 frontend/src/components/login/GradientMeshView.tsx create mode 100644 frontend/src/components/login/InkWashView.tsx create mode 100644 frontend/src/components/login/NeonCityView.tsx diff --git a/frontend/src/components/login/AuroraView.tsx b/frontend/src/components/login/AuroraView.tsx new file mode 100644 index 0000000..8c53ad6 --- /dev/null +++ b/frontend/src/components/login/AuroraView.tsx @@ -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 ( + <> + + + {/* ═══════════════ 全屏背景容器 ═══════════════ */} +
+ {/* ────────── 极光光带 1:紫罗兰色 ────────── */} +
+ + {/* ────────── 极光光带 2:翠绿色 ────────── */} +
+ + {/* ────────── 极光光带 3:青蓝色 ────────── */} +
+ + {/* ────────── 极光光带 4:紫绿混合 ────────── */} +
+ + {/* ────────── 星光点缀 ────────── */} + {STARS.map((star, i) => ( +
+ ))} + + {/* ═══════════════ 登录卡片 ═══════════════ */} +
+ {/* ──────── 品牌区域 ──────── */} +
+ {/* Logo */} +
+ + + + + +
+ + {/* 标题 */} +

+ SQL审核平台 +

+ + {/* 副标题 */} +

+ 企业级数据库审核与合规管理 +

+
+ + {/* ──────── 表单区域 ──────── */} +
+ {/* 错误提示 */} + {error && ( +
+ {error} +
+ )} + + {/* 用户名输入框 */} +
+ 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'; + }} + /> +
+ + {/* 密码输入框 */} +
+ 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'; + }} + /> +
+ + {/* 登录按钮 */} + +
+ + {/* ──────── 版权信息 ──────── */} +
+ © 2025 SQL审核平台 +
+
+ + {/* ──────── 底部辉光装饰 ──────── */} +
+
+ + {/* 全局旋转动画 */} + + + ); +} diff --git a/frontend/src/components/login/CosmosView.tsx b/frontend/src/components/login/CosmosView.tsx new file mode 100644 index 0000000..09329f6 --- /dev/null +++ b/frontend/src/components/login/CosmosView.tsx @@ -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(() => { + 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(() => { + 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 ( +
+ + + {/* ── 星云色块 ── */} +
+
+
+ + {/* ── 星星层 ── */} + {stars.map((star) => ( +
+ ))} + + {/* ── 流星 ── */} +
+
+ + {/* ── 漂浮粒子星尘 ── */} + {particles.map((p) => ( +
+ ))} + + {/* ── 行星装饰(左下角) ── */} +
+ {/* 行星本体 */} +
+ {/* 行星光环 */} +
+ {/* 内环 */} +
+
+ + {/* ── 小行星(右上角装饰) ── */} +
+ + {/* ── 登录卡片 ── */} +
+ {/* 品牌区域 */} +
+ {/* Logo */} +
+ + + + + +
+

+ SQL审核平台 +

+

+ 企业级数据库审核与合规管理 +

+
+ + {/* 表单区域 */} +
+ {/* 用户名 */} +
+ + 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)'; + }} + /> +
+ + {/* 密码 */} +
+ + 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)'; + }} + /> +
+ + {/* 登录按钮 */} + + + {/* 错误提示 */} + {error && ( +
+ {error} +
+ )} +
+ + {/* 底部版权 */} +

+ © 2025 SQL审核平台 — 企业级数据库安全合规 +

+
+ + {/* 全局旋转动画补充 */} + +
+ ); +} diff --git a/frontend/src/components/login/GradientMeshView.tsx b/frontend/src/components/login/GradientMeshView.tsx new file mode 100644 index 0000000..96779a9 --- /dev/null +++ b/frontend/src/components/login/GradientMeshView.tsx @@ -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(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 ( + <> + + + {/* ===================== ROOT CONTAINER ===================== */} +
+ {/* ---- gradient mesh blobs ---- */} + {blobs.map((b, i) => ( +
+ ))} + + {/* ---- subtle dot-grid texture ---- */} +
+ + {/* ===================== LOGIN CARD ===================== */} +
+ {/* top accent line */} +
+ + {/* inner glow */} +
+ + {/* ---- brand ---- */} +
+ {/* Logo */} +
+ + + + + +
+ +

+ SQL审核平台 +

+

+ 企业级数据库审核与合规管理 +

+
+ + {/* ---- form ---- */} +
+ {/* username */} +
+ setUsername(e.target.value)} + onFocus={() => setFocusedField('username')} + onBlur={() => setFocusedField(null)} + style={getInputStyle('username')} + autoComplete="username" + /> + {focusedField === 'username' && ( +
+ )} +
+ + {/* password */} +
+ setPassword(e.target.value)} + onFocus={() => setFocusedField('password')} + onBlur={() => setFocusedField(null)} + style={getInputStyle('password')} + autoComplete="current-password" + /> + {focusedField === 'password' && ( +
+ )} +
+ + {/* error */} + {error && ( +
+ {error} +
+ )} + + {/* submit */} + + + + {/* ---- copyright ---- */} +

+ © 2025 SQL审核平台 +

+
+
+ + ); +} diff --git a/frontend/src/components/login/InkWashView.tsx b/frontend/src/components/login/InkWashView.tsx new file mode 100644 index 0000000..0f9a84b --- /dev/null +++ b/frontend/src/components/login/InkWashView.tsx @@ -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 ( + <> + + + {/* ===================== 全屏容器 ===================== */} +
+ {/* ────────── 水墨晕染装饰 ────────── */} + {/* 左上角淡墨点 */} +
+ {/* 右下角浓墨点 */} +
+ {/* 中央偏上淡淡墨痕 */} +
+ + {/* ────────── 远山轮廓 ────────── */} + {/* 最远山 */} +
+ {/* 中山 */} +
+ {/* 近山 */} +
+ + {/* ────────── 雾气效果 ────────── */} +
+ + {/* ────────── 金色点缀线(水面微光) ────────── */} +
+
+ + {/* ────────── 竖排 SQL 背景装饰 ────────── */} +
+ SQL +
+
+ AUDIT +
+ + {/* ===================== 登录卡片 ===================== */} +
+ {/* ──── 品牌区域 ──── */} +
+ {/* Logo */} +
+ + + + + +
+ + {/* 标题 */} +

+ SQL审核平台 +

+ + {/* 副标题 */} +

+ 企业级数据库审核与合规管理 +

+
+ + {/* ──── 中国风细线装饰(上方) ──── */} +
+
+
+
+
+ + {/* ──── 表单 ──── */} +
+ {/* 用户名 */} +
+ + 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'; + }} + /> +
+ + {/* 密码 */} +
+ + 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'; + }} + /> +
+ + {/* 错误提示 */} + {error && ( +
+ {error} +
+ )} + + {/* 登录按钮 */} + +
+ + {/* ──── 中国风细线装饰(下方) ──── */} +
+
+
+
+
+ + {/* ──── 版权信息 ──── */} +

+ © SQL Audit Platform. All rights reserved. +

+
+
+ + ); +} diff --git a/frontend/src/components/login/NeonCityView.tsx b/frontend/src/components/login/NeonCityView.tsx new file mode 100644 index 0000000..754f22a --- /dev/null +++ b/frontend/src/components/login/NeonCityView.tsx @@ -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 ( + <> + +
+ {/* Grid floor background */} +
+ + {/* Neon tubes */} + {NEON_TUBES.map(([top, color, delay, duration], i) => ( +
+ ))} + + {/* City skyline buildings */} +
+ {BUILDINGS.map(([width, heightPct, leftPct, glowColor], i) => ( +
+ ))} +
+ + {/* Rain drops */} + {RAIN_DROPS.map(([left, delay, duration], i) => ( +
+ ))} + + {/* Fog layer */} +
+ + {/* Top glow */} +
+ + {/* Login card */} +
+ {/* Top neon line decoration */} +
+ + {/* Brand area */} +
+ {/* Logo */} +
+ + + + + +
+ + {/* Title */} +

+ SQL审核平台 +

+ + {/* Subtitle */} +

+ 企业级数据库审核与合规管理 +

+
+ + {/* Login form */} +
+ {/* Username input */} +
+ + 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'; + }} + /> +
+ + {/* Password input */} +
+ + 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'; + }} + /> +
+ + {/* Error message */} + {error && ( +
+ {error} +
+ )} + + {/* Login button */} + +
+ + {/* Copyright */} +

+ © SQL审核平台 · 安全合规的数据库管理 +

+
+
+ + ); +} diff --git a/frontend/src/pages/AppearanceSettings.tsx b/frontend/src/pages/AppearanceSettings.tsx index f79a511..80b11b9 100644 --- a/frontend/src/pages/AppearanceSettings.tsx +++ b/frontend/src/pages/AppearanceSettings.tsx @@ -12,6 +12,11 @@ const LOGIN_PREVIEW_COLORS: Record
)} + {opt.id === 'aurora' && ( +
+
+
+ {[15, 30, 55, 75].map(t => ( +
+ ))} +
+
+
+
+
+
+
+
+
+
+ )} + {opt.id === 'cosmos' && ( +
+
+
+ {[10, 25, 40, 60, 80].map(t => ( +
+ ))} +
+
+
+
+
+
+
+
+
+
+
+ )} + {opt.id === 'inkwash' && ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )} + {opt.id === 'neoncity' && ( +
+
+
+ {[10, 25, 40, 55, 70, 85].map((l, i) => ( +
+ ))} +
+
+
+
+
+
+
+
+
+
+ )} + {opt.id === 'gradientmesh' && ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
{/* Active check mark */} diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index 515bf46..3b08226 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -4,8 +4,13 @@ import CenteredCard from '@/components/login/CenteredCard'; import SidePanel from '@/components/login/SidePanel'; import FullImage from '@/components/login/FullImage'; 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 = { split: SplitView, @@ -13,14 +18,24 @@ const LOGIN_STYLES: Record = { sidepanel: SidePanel, fullimage: FullImage, minimal: MinimalDark, + aurora: AuroraView, + cosmos: CosmosView, + inkwash: InkWashView, + neoncity: NeonCityView, + gradientmesh: GradientMeshView, }; 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: 'sidepanel', name: '侧边面板', desc: '深海波浪,生物发光粒子' }, { id: 'fullimage', 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() {