You've already forked DataMate
feat(auth): 添加登录功能和路由保护
- 在侧边栏添加退出登录按钮并实现登出逻辑 - 添加 ProtectedRoute 组件用于路由权限控制 - 创建 LoginPage 组件实现登录界面和逻辑 - 集成本地登录验证到 authSlice 状态管理 - 配置路由表添加登录页面和保护路由 - 实现自动跳转到登录页面的重定向逻辑
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
CloseOutlined,
|
||||
MenuOutlined,
|
||||
SettingOutlined,
|
||||
LogoutOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { ClipboardList, X } from "lucide-react";
|
||||
import { menuItems } from "@/pages/Layout/menu";
|
||||
@@ -12,6 +13,7 @@ import TaskUpload from "./TaskUpload";
|
||||
import SettingsPage from "../SettingsPage/SettingsPage";
|
||||
import { useAppSelector, useAppDispatch } from "@/store/hooks";
|
||||
import { showSettings, hideSettings } from "@/store/slices/settingsSlice";
|
||||
import { logout } from "@/store/slices/authSlice";
|
||||
|
||||
const isPathMatch = (currentPath: string, targetPath: string) =>
|
||||
currentPath === targetPath || currentPath.startsWith(`${targetPath}/`);
|
||||
@@ -67,6 +69,11 @@ const AsiderAndHeaderLayout = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleLogout = () => {
|
||||
dispatch(logout());
|
||||
navigate("/login");
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${
|
||||
@@ -148,6 +155,9 @@ const AsiderAndHeaderLayout = () => {
|
||||
>
|
||||
设置
|
||||
</Button>
|
||||
<Button block danger onClick={handleLogout}>
|
||||
退出登录
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
@@ -175,6 +185,7 @@ const AsiderAndHeaderLayout = () => {
|
||||
>
|
||||
<SettingOutlined />
|
||||
</Button>
|
||||
<Button block danger onClick={handleLogout} icon={<LogoutOutlined />} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
114
frontend/src/pages/Login/LoginPage.tsx
Normal file
114
frontend/src/pages/Login/LoginPage.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate, useLocation } from 'react-router';
|
||||
import { Form, Input, Button, Typography, message, Card } from 'antd';
|
||||
import { UserOutlined, LockOutlined } from '@ant-design/icons';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { loginLocal } from '@/store/slices/authSlice';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
const LoginPage: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { loading, error } = useAppSelector((state) => state.auth);
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
|
||||
const from = location.state?.from?.pathname || '/data';
|
||||
|
||||
const onFinish = (values: any) => {
|
||||
dispatch(loginLocal(values));
|
||||
// The reducer updates state synchronously.
|
||||
if (values.username === 'admin' && values.password === '123456') {
|
||||
messageApi.success('登录成功');
|
||||
navigate(from, { replace: true });
|
||||
} else {
|
||||
messageApi.error('账号或密码错误');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-[#050b14] relative overflow-hidden">
|
||||
{contextHolder}
|
||||
|
||||
{/* Background Effects */}
|
||||
<div className="absolute inset-0 z-0">
|
||||
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-blue-900/20 via-[#050b14] to-[#050b14]"></div>
|
||||
{/* Simple grid pattern if possible, or just gradient */}
|
||||
</div>
|
||||
|
||||
<div className="absolute top-1/4 left-1/4 w-72 h-72 bg-blue-500/10 rounded-full blur-3xl animate-pulse"></div>
|
||||
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-cyan-500/10 rounded-full blur-3xl animate-pulse delay-700"></div>
|
||||
|
||||
<div className="z-10 w-full max-w-md p-8 animate-[fadeIn_0.5s_ease-out_forwards]">
|
||||
<div className="backdrop-blur-xl bg-white/5 border border-white/10 rounded-2xl shadow-2xl p-8 relative overflow-hidden">
|
||||
{/* Decorative line */}
|
||||
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-blue-500 to-transparent"></div>
|
||||
|
||||
<div className="text-center mb-8">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-blue-500/20 mb-4 border border-blue-500/30">
|
||||
<svg className="w-8 h-8 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
||||
</svg>
|
||||
</div>
|
||||
<Title level={2} className="!text-white !mb-2 tracking-wide font-bold">
|
||||
DataBuilder
|
||||
</Title>
|
||||
<Text className="text-gray-400 text-sm tracking-wider">
|
||||
一站式数据工作平台
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<Form
|
||||
name="login"
|
||||
initialValues={{ remember: true, username: 'admin', password: '123456' }}
|
||||
onFinish={onFinish}
|
||||
layout="vertical"
|
||||
size="large"
|
||||
>
|
||||
<Form.Item
|
||||
name="username"
|
||||
rules={[{ required: true, message: '请输入账号!' }]}
|
||||
>
|
||||
<Input
|
||||
prefix={<UserOutlined className="text-blue-400" />}
|
||||
placeholder="账号"
|
||||
className="!bg-white/5 !border-white/10 !text-white placeholder:!text-gray-600 hover:!border-blue-500/50 focus:!border-blue-500 !rounded-lg"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="password"
|
||||
rules={[{ required: true, message: '请输入密码!' }]}
|
||||
>
|
||||
<Input.Password
|
||||
prefix={<LockOutlined className="text-blue-400" />}
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
className="!bg-white/5 !border-white/10 !text-white placeholder:!text-gray-600 hover:!border-blue-500/50 focus:!border-blue-500 !rounded-lg"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item className="mb-2">
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
className="w-full bg-gradient-to-r from-blue-600 to-cyan-600 hover:from-blue-500 hover:to-cyan-500 border-none h-12 rounded-lg font-semibold tracking-wide shadow-lg shadow-blue-900/20"
|
||||
loading={loading}
|
||||
>
|
||||
登录系统
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
||||
<div className="text-center mt-4">
|
||||
<Text className="text-gray-600 text-xs">
|
||||
企业级数据处理平台 · 安全接入
|
||||
</Text>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
Reference in New Issue
Block a user