- Redis DB0~DB15 下拉选择,默认DB0 - 查询历史粘贴按钮(一键填入SQL编辑器) - 去掉导出历史按钮 - SchemaSidebar 按目标数据库筛选 - SQL自动补全 schema 双层注册(db→tables, table→columns) - 后端 db-schema 接口(INFORMATION_SCHEMA.COLUMNS 一次加载) - autocompletion 扩展激活 - Redis key 树形结构(冒号分隔层级) - Redis 查询模板 GET/HGETALL/HGET - guest 角色彻底移除,改为 auditor - 登录页优化:去掉演示账号、placeholder 中文提示
135 lines
6.6 KiB
TypeScript
135 lines
6.6 KiB
TypeScript
import { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { Shield, Database, Code, GitBranch, Bell, History, UserCog, Sparkles, Sun, Moon } from 'lucide-react';
|
|
import { useTheme } from '@/hooks/useTheme';
|
|
import { useAuth } from '@/hooks/useAuth';
|
|
|
|
export default function LoginPage() {
|
|
const navigate = useNavigate();
|
|
const { theme, toggleTheme } = useTheme();
|
|
const { login } = useAuth();
|
|
const [username, setUsername] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState('');
|
|
|
|
const handleLogin = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (!username || !password) return;
|
|
setLoading(true);
|
|
setError('');
|
|
const ok = await login(username, password);
|
|
setLoading(false);
|
|
if (ok) {
|
|
navigate('/dashboard');
|
|
} else {
|
|
setError('用户名或密码错误');
|
|
}
|
|
};
|
|
|
|
const features = [
|
|
{ icon: Database, title: '多数据库支持', desc: 'MySQL、PostgreSQL、Redis、MongoDB' },
|
|
{ icon: Code, title: '智能 SQL 审核', desc: '语法检查、规则校验、影响分析、自动评分' },
|
|
{ icon: GitBranch, title: '多级审批流程', desc: '会签、或签、条件审批灵活配置' },
|
|
{ icon: Bell, title: '多渠道通知', desc: '飞书、钉钉、企微、TG 即时推送' },
|
|
{ icon: History, title: '完整审计追踪', desc: '所有操作可追溯,支持 SQL 回滚' },
|
|
{ icon: UserCog, title: '细粒度权限', desc: 'RBAC 角色权限,字段级/行级访问控制' },
|
|
];
|
|
|
|
return (
|
|
<div className="min-h-screen flex bg-[var(--dg-bg-primary)] relative overflow-hidden">
|
|
{/* 背景装饰 */}
|
|
<div className="absolute inset-0 pointer-events-none">
|
|
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(ellipse_at_20%_20%,rgba(88,166,255,0.08)_0%,transparent_50%),radial-gradient(ellipse_at_80%_80%,rgba(137,87,229,0.08)_0%,transparent_50%)]" />
|
|
</div>
|
|
{/* 主题切换 */}
|
|
<button
|
|
onClick={toggleTheme}
|
|
className="absolute top-5 right-5 z-20 w-10 h-10 rounded-xl bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] flex items-center justify-center text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)] hover:bg-[var(--dg-bg-hover)] transition-colors"
|
|
title={theme === 'dark' ? '切换到浅色模式' : '切换到深色模式'}
|
|
>
|
|
{theme === 'dark' ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
|
|
</button>
|
|
|
|
{/* 左侧 */}
|
|
<div className="flex-1 flex flex-col justify-center px-16 relative z-10">
|
|
<div className="mb-10">
|
|
<div className="flex items-center gap-3 mb-6">
|
|
<div className="w-12 h-12 rounded-xl dg-gradient-br flex items-center justify-center">
|
|
<Shield className="w-7 h-7 text-white" />
|
|
</div>
|
|
<h1 className="text-5xl font-bold dg-gradient-text">
|
|
SQL-Platform
|
|
</h1>
|
|
</div>
|
|
<p className="text-lg text-[var(--dg-text-secondary)] leading-relaxed">
|
|
企业级数据审核与变更管理平台<br />
|
|
支持多数据库类型,提供完善的 SQL 审核、审批流程和审计追踪
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
{features.map((f, i) => (
|
|
<div key={i} className="flex items-start gap-3 p-4 rounded-xl bg-[var(--dg-bg-secondary)]/60 border border-[var(--dg-border)] hover:border-blue-500/50 hover:bg-[var(--dg-bg-secondary)] transition-all group">
|
|
<f.icon className="w-6 h-6 text-blue-400 mt-0.5 flex-shrink-0 group-hover:scale-110 transition-transform" />
|
|
<div>
|
|
<h4 className="text-sm font-semibold mb-1">{f.title}</h4>
|
|
<p className="text-xs text-[var(--dg-text-secondary)]">{f.desc}</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 右侧 */}
|
|
<div className="w-[480px] flex flex-col justify-center px-16 bg-[var(--dg-bg-secondary)]/80 backdrop-blur-xl border-l border-[var(--dg-border)] relative z-10">
|
|
<div className="w-full max-w-sm mx-auto">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<Sparkles className="w-5 h-5 text-blue-400" />
|
|
<h2 className="text-2xl font-bold">欢迎回来</h2>
|
|
</div>
|
|
<p className="text-sm text-[var(--dg-text-secondary)] mb-8">请登录您的账户以继续</p>
|
|
|
|
<form onSubmit={handleLogin} className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium mb-2">用户名</label>
|
|
<div className="relative">
|
|
<UserCog className="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--dg-text-muted)]" />
|
|
<input
|
|
className="w-full pl-10 pr-4 py-3 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400/20 transition-all"
|
|
placeholder="请输入用户名"
|
|
value={username}
|
|
onChange={e => setUsername(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium mb-2">密码</label>
|
|
<div className="relative">
|
|
<Shield className="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--dg-text-muted)]" />
|
|
<input
|
|
type="password"
|
|
className="w-full pl-10 pr-4 py-3 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400/20 transition-all"
|
|
placeholder="请输入密码"
|
|
value={password}
|
|
onChange={e => setPassword(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<button
|
|
type="submit"
|
|
disabled={loading || !username || !password}
|
|
className="w-full py-3 dg-gradient-bg rounded-lg text-sm font-semibold text-white hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed transition-all flex items-center justify-center gap-2"
|
|
>
|
|
{loading ? (
|
|
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
|
) : '登录'}
|
|
</button>
|
|
{error && <p className="text-xs text-red-400 text-center mt-2">{error}</p>}
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|