mirror of
https://github.com/SunnyQjm/sunny-blog.git
synced 2026-10-05 01:15:39 +08:00
add: 添加登录功能
This commit is contained in:
1 parent
ab71d74755
commit
b220b2e3ca
13 files changed
+400
-10
No files matched your search
@@ -33,7 +33,14 @@ const config: IConfig = {
|
||||
}],
|
||||
],
|
||||
sass: {},
|
||||
disableCSSModules: true
|
||||
disableCSSModules: true,
|
||||
proxy: {
|
||||
"/api": {
|
||||
"target": "http://localhost:3000/",
|
||||
"changeOrigin": true,
|
||||
"pathRewrite": { "^/api" : "" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -9,9 +9,11 @@
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/axios": "^0.14.0",
|
||||
"@types/react-addons-shallow-compare": "^0.14.22",
|
||||
"@types/react-transition-group": "^4.2.3",
|
||||
"antd": "^3.19.5",
|
||||
"axios": "^0.19.0",
|
||||
"dva": "^2.6.0-beta.6",
|
||||
"for-editor": "^0.3.4",
|
||||
"highlight.js": "^9.15.10",
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
export interface Action<T> {
|
||||
type: string,
|
||||
data: T
|
||||
}
|
||||
|
||||
export interface Operators {
|
||||
call: any,
|
||||
put: any,
|
||||
select: any,
|
||||
}
|
||||
|
||||
|
||||
export interface DispatchProps {
|
||||
dispatch: any
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface BaseResponseBody<T> {
|
||||
statusCode: number;
|
||||
error: string;
|
||||
message: string;
|
||||
data: T;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import axios from 'axios';
|
||||
import {BaseResponseBody} from "@/data/interface/response.interface";
|
||||
import {StatusCode} from "@/data/network/status-code";
|
||||
import {User} from "@/data/user";
|
||||
|
||||
export const API = {
|
||||
INFO: {
|
||||
login: '/login',
|
||||
register: '/register'
|
||||
},
|
||||
global: {
|
||||
token: ''
|
||||
},
|
||||
easyRequest<T>(type: string, url: string, data: any = {}, config: any = {}): Promise<T> {
|
||||
// 统一添加相应的头部
|
||||
if (config.headers) {
|
||||
config.headers = {
|
||||
'Content-Type': 'application/json',
|
||||
// 'authorization': `Bearer ${API.global.token}`,
|
||||
...config.headers,
|
||||
}
|
||||
} else {
|
||||
config.headers = {
|
||||
'Content-Type': 'application/json',
|
||||
// 'authorization': `Bearer ${API.global.token}`
|
||||
}
|
||||
}
|
||||
let requestPromise;
|
||||
switch (type.trim().toLowerCase()) {
|
||||
case 'get':
|
||||
requestPromise = axios.get(url, {
|
||||
params: data,
|
||||
...config,
|
||||
});
|
||||
break;
|
||||
case 'post':
|
||||
requestPromise = axios.post(url, data, config);
|
||||
break;
|
||||
case 'put':
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (requestPromise) {
|
||||
return requestPromise
|
||||
.then((result: any) => {
|
||||
console.log(result);
|
||||
return result.data;
|
||||
})
|
||||
.then((responseData: BaseResponseBody<T>) => {
|
||||
console.log(responseData);
|
||||
// 同意处理状态码
|
||||
switch (responseData.statusCode) {
|
||||
case StatusCode.SUCCESS:
|
||||
break;
|
||||
case StatusCode.NEED_ADMIN_PERMISSION:
|
||||
break;
|
||||
case StatusCode.COMMENT_ERROR:
|
||||
break;
|
||||
case StatusCode.NEED_LOGIN:
|
||||
break;
|
||||
}
|
||||
if(responseData.statusCode === StatusCode.SUCCESS) {
|
||||
return responseData.data;
|
||||
} else {
|
||||
return Promise.reject(responseData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.reject('请求类型不对');
|
||||
},
|
||||
easyGet<T>(url: string, data: any, config: any = {}): Promise<T> {
|
||||
return API.easyRequest<T>('get', url, data, config);
|
||||
},
|
||||
eastPost<T>(url: string, data: any, config: any = {}): Promise<T> {
|
||||
return API.easyRequest<T>('post', url, data, config);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
///////// 用户相关接口
|
||||
////////////////////////////////////////////////////////////
|
||||
login: (username: string, password: string): Promise<User> => {
|
||||
return API.eastPost<User>('/api/login', {
|
||||
username,
|
||||
password
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export const StatusCode = {
|
||||
SUCCESS: 0, // 成功
|
||||
COMMENT_ERROR: -1, // 通用错误
|
||||
NEED_LOGIN: -2, // 需要登录
|
||||
NEED_ADMIN_PERMISSION: -3, // 需要管理员权限
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
export class User {
|
||||
id: number = -1;
|
||||
username: string = '';
|
||||
password: string = '';
|
||||
avatar: string = '';
|
||||
nickName: string = '';
|
||||
age: number = 0;
|
||||
}
|
||||
+16
-3
@@ -5,6 +5,8 @@ import {
|
||||
NavBarComponent
|
||||
} from '../components';
|
||||
import RouterTypes from "umi/routerTypes";
|
||||
import {LocalStorageKeys, LocalStorageManager} from "@/manager/storage.manager";
|
||||
import { connect } from 'dva';
|
||||
|
||||
const menu = [
|
||||
{
|
||||
@@ -26,7 +28,7 @@ const menu = [
|
||||
|
||||
|
||||
interface BasicLayoutProps extends RouterTypes {
|
||||
|
||||
dispatch: any
|
||||
}
|
||||
|
||||
interface BasicLayoutState {
|
||||
@@ -38,6 +40,13 @@ class BasicLayout extends React.Component<BasicLayoutProps, BasicLayoutState> {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
console.log('global layout mount');
|
||||
this.props.dispatch({
|
||||
type: 'admin/init'
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: any) {
|
||||
if (this.props.location !== prevProps.location) {
|
||||
window.scrollTo(0, 0);
|
||||
@@ -45,7 +54,7 @@ class BasicLayout extends React.Component<BasicLayoutProps, BasicLayoutState> {
|
||||
}
|
||||
|
||||
render(): React.ReactElement<any, string | React.JSXElementConstructor<any>> | string | number | {} | React.ReactNodeArray | React.ReactPortal | boolean | null | undefined {
|
||||
if (this.props.location.pathname.startsWith('/admin')) {
|
||||
if (this.props.location.pathname.startsWith('/admin') || this.props.location.pathname.startsWith('/login')) {
|
||||
return (
|
||||
<div>
|
||||
{this.props.children}
|
||||
@@ -71,4 +80,8 @@ class BasicLayout extends React.Component<BasicLayoutProps, BasicLayoutState> {
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(BasicLayout);
|
||||
function mapStateToProps(state: any) {
|
||||
return state;
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(withRouter(BasicLayout));
|
||||
@@ -0,0 +1,22 @@
|
||||
export const LocalStorageKeys = {
|
||||
USER: 'user'
|
||||
};
|
||||
|
||||
export const LocalStorageManager = {
|
||||
save<T>(key: string, value: T) {
|
||||
let insertValue = JSON.stringify(value);
|
||||
localStorage.setItem(key, insertValue);
|
||||
},
|
||||
|
||||
getString(key: string) {
|
||||
return localStorage.getItem(key);
|
||||
},
|
||||
|
||||
getObj(key: string) {
|
||||
const result = localStorage.getItem(key);
|
||||
if(result) {
|
||||
return JSON.parse(result);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import {Action, Operators} from "@/data/interface/dva.interface";
|
||||
import {API} from "@/data/network/api";
|
||||
import {User} from "@/data/user";
|
||||
import router from 'umi/router';
|
||||
import {} from 'antd'
|
||||
import {LocalStorageKeys, LocalStorageManager} from "@/manager/storage.manager";
|
||||
|
||||
export interface AdminModelState {
|
||||
user: User,
|
||||
loginIng: boolean
|
||||
}
|
||||
|
||||
export default {
|
||||
namespace: 'admin',
|
||||
state: {
|
||||
user: null,
|
||||
},
|
||||
reducers: {
|
||||
updateUser(state: any, action: Action<User>) {
|
||||
console.log('fuck?', action);
|
||||
return {
|
||||
...state,
|
||||
user: action.data
|
||||
}
|
||||
}
|
||||
},
|
||||
effects: {
|
||||
* init(action: Action<any>, operators: Operators) {
|
||||
const user = yield LocalStorageManager.getObj(LocalStorageKeys.USER);
|
||||
if(user) {
|
||||
yield operators.put({
|
||||
type: 'updateUser',
|
||||
data: user
|
||||
});
|
||||
} else { // 没有登录则跳转到登录页
|
||||
router.replace('/login');
|
||||
}
|
||||
yield console.log('init');
|
||||
},
|
||||
* login(action: Action<any>, operators: Operators) {
|
||||
const result: User = yield operators.call(API.login, action.data.username, action.data.password);
|
||||
yield operators.put({
|
||||
type: 'updateUser',
|
||||
data: result
|
||||
});
|
||||
LocalStorageManager.save(LocalStorageKeys.USER, result);
|
||||
router.replace('/admin');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,13 @@ import RouterTypes from "umi/routerTypes";
|
||||
import withRouter from 'umi/withRouter';
|
||||
import {SelectParam} from "antd/es/menu";
|
||||
import router from 'umi/router';
|
||||
import { connect } from 'dva';
|
||||
import {User} from "@/data/user";
|
||||
|
||||
const {Header, Content, Footer, Sider} = Layout;
|
||||
|
||||
interface AdminLayoutProps extends RouterTypes {
|
||||
|
||||
user: User,
|
||||
}
|
||||
|
||||
interface AdminLayoutState {
|
||||
@@ -50,9 +52,14 @@ class AdminLayout extends React.Component<AdminLayoutProps, AdminLayoutState> {
|
||||
const {
|
||||
collapsed
|
||||
} = this.state;
|
||||
const {
|
||||
user
|
||||
} = this.props;
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item>退出登录</Menu.Item>
|
||||
<Menu.Item onClick={() => {
|
||||
router.replace('/login')
|
||||
}}>退出登录</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
@@ -89,7 +96,6 @@ class AdminLayout extends React.Component<AdminLayoutProps, AdminLayoutState> {
|
||||
<Layout style={{marginLeft: collapsed ? 80 : 200}}>
|
||||
<Header
|
||||
className='admin-layout__header'
|
||||
// style={{width: collapsed ? 'calc(100% - 80px)' : 'calc(100% - 200px)'}}
|
||||
>
|
||||
<Dropdown overlay={menu}>
|
||||
<a>
|
||||
@@ -97,9 +103,8 @@ class AdminLayout extends React.Component<AdminLayoutProps, AdminLayoutState> {
|
||||
className='admin-layout__header_avatar'
|
||||
src='https://avatars2.githubusercontent.com/u/29176413?s=400&u=f78127887499acf06545f9cbe298ce388201dfed&v=4'
|
||||
/>
|
||||
<span className='admin-layout__header_username'>SunnyQjm</span>
|
||||
<span className='admin-layout__header_username'>{user && user.username}</span>
|
||||
</a>
|
||||
|
||||
</Dropdown>
|
||||
</Header>
|
||||
<Content className='admin-layout__content'>
|
||||
@@ -112,4 +117,10 @@ class AdminLayout extends React.Component<AdminLayoutProps, AdminLayoutState> {
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(AdminLayout);
|
||||
function mapStateToProps(state: any) {
|
||||
return {
|
||||
user: state.admin.user
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(withRouter(AdminLayout));
|
||||
@@ -0,0 +1,55 @@
|
||||
.login-page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
//justify-content: center;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
|
||||
&__title {
|
||||
position: relative;
|
||||
color: #222;
|
||||
margin-top: 100px;
|
||||
&::before {
|
||||
content: '';
|
||||
background: #222;
|
||||
height: 2px;
|
||||
width: 80%;
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: 10%;
|
||||
animation: width-show 1s;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background: #222;
|
||||
height: 2px;
|
||||
width: 80%;
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: 10%;
|
||||
animation: width-show 1s;
|
||||
}
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
animation: fadeInDown 1s;
|
||||
}
|
||||
|
||||
&__description {
|
||||
font-size: 14px;
|
||||
margin: 20px 0 40px;
|
||||
color: rgba(0, 0, 0, .45);
|
||||
}
|
||||
}
|
||||
|
||||
.login-form {
|
||||
max-width: 300px;
|
||||
}
|
||||
.login-form-forgot {
|
||||
float: right;
|
||||
}
|
||||
.login-form-button {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import React, {FormEvent} from "react";
|
||||
import './index.scss'
|
||||
import {Form, Icon, Input, Button, Checkbox} from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import {DispatchProps} from "@/data/interface/dva.interface";
|
||||
|
||||
interface LoginPageProps extends DispatchProps{
|
||||
form: any,
|
||||
loading: boolean
|
||||
}
|
||||
|
||||
interface LoginPageState {
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录页面
|
||||
*/
|
||||
class LoginPage extends React.Component<LoginPageProps, LoginPageState> {
|
||||
constructor(props: LoginPageProps) {
|
||||
super(props);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
handleSubmit(e: FormEvent) {
|
||||
e.preventDefault();
|
||||
this.props.form.validateFields((err: any, values: object) => {
|
||||
if (!err) {
|
||||
console.log('Received values of form: ', values);
|
||||
this.props.dispatch({
|
||||
type: 'admin/login',
|
||||
data: values
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
render(): React.ReactElement<any, string | React.JSXElementConstructor<any>> | string | number | {} | React.ReactNodeArray | React.ReactPortal | boolean | null | undefined {
|
||||
const {getFieldDecorator} = this.props.form;
|
||||
const {
|
||||
loading
|
||||
} = this.props;
|
||||
return (
|
||||
<div className='login-page'>
|
||||
<span className='login-page__title'>
|
||||
{'建明 | Ming.J'}
|
||||
</span>
|
||||
<span className='login-page__description'>每一个不曾起舞的日子都是对生命的辜负!</span>
|
||||
<Form onSubmit={this.handleSubmit} className="login-form">
|
||||
<Form.Item>
|
||||
{getFieldDecorator('username', {
|
||||
rules: [{required: true, message: '请输入用户名!'}],
|
||||
})(
|
||||
<Input
|
||||
prefix={<Icon type="user" style={{color: 'rgba(0,0,0,.25)'}}/>}
|
||||
placeholder="用户名"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('password', {
|
||||
rules: [{required: true, message: '请输入密码!'}],
|
||||
})(
|
||||
<Input
|
||||
prefix={<Icon type="lock" style={{color: 'rgba(0,0,0,.25)'}}/>}
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('remember', {
|
||||
valuePropName: 'checked',
|
||||
initialValue: true,
|
||||
})(<Checkbox>记住密码</Checkbox>)}
|
||||
<a className="login-form-forgot" href="">
|
||||
忘记密码
|
||||
</a>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
className="login-form-button"
|
||||
loading={loading}
|
||||
>
|
||||
登录
|
||||
</Button>
|
||||
Or <a href="">现在注册</a>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state: any) {
|
||||
return {
|
||||
loading: state.loading.effects['admin/login']
|
||||
};
|
||||
}
|
||||
|
||||
const WrappedNormalLoginForm = Form.create({name: 'normal_login'})(LoginPage);
|
||||
export default connect(mapStateToProps)(WrappedNormalLoginForm)
|
||||
Reference in new issue
Block a user