完成强制跳转登录页面、完成用户信息存储

This commit is contained in:
wzhqwq 2021-07-30 13:08:37 +08:00
父節點 9b71c1f527
當前提交 1b66a5467a
共有 13 個檔案被更改,包括 178 行新增60 行删除

查看文件

@ -6,6 +6,7 @@
"axios": "^0.21.1", "axios": "^0.21.1",
"cra-template": "1.1.2", "cra-template": "1.1.2",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"qs": "^6.10.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-scripts": "4.0.3", "react-scripts": "4.0.3",

二進制
public/image/user.jpg Normal file

未顯示二進位檔案。

之後

寬度:  |  高度:  |  大小: 33 KiB

查看文件

@ -5,6 +5,7 @@ a.btn {
color: white !important; color: white !important;
border-radius: .3em; border-radius: .3em;
padding: .375em 0; padding: .375em 0;
line-height: 1.5em;
font-size: 1.1em; font-size: 1.1em;
cursor: pointer; cursor: pointer;
transition: filter .3s ease-out; transition: filter .3s ease-out;
@ -14,7 +15,7 @@ a.btn {
} }
.btn:disabled { .btn:disabled {
cursor: not-allowed; cursor: not-allowed;
filter: grayscale(100%); filter: grayscale(100%) opacity(.5);
} }
.btn:active { .btn:active {
color: white !important; color: white !important;
@ -58,3 +59,18 @@ button.btn {
border-radius: .15em; border-radius: .15em;
width: 6em; width: 6em;
} }
.sdu,
.user {
height: 100%;
display: flex;
align-items: center;
}
.sdu-logo,
.user-logo {
height: 100%;
}
.sdu-name {
height: 30px;
margin-left: 10px;
}

查看文件

@ -1,21 +1,43 @@
import './App.css'; import './App.css';
import { Route, SingleRouter } from './components/SingleRouter/SingleRouter'; import { Redirect, Route, SingleRouter } from './components/SingleRouter/SingleRouter';
import { AppContainer } from './index/index'; import { AppContainer } from './index/index';
import { UploadContainer } from './upload/upload'; import { UploadContainer } from './upload/upload';
import { LogInContainer } from './login/login'; import { LogInContainer } from './login/login';
import { ReviewContainer } from './review/review'; import { ReviewContainer } from './review/review';
import History from './helper/history'; import { Component } from 'react';
import { UserContext } from './helper/Context';
function App() { class App extends Component {
// TODO check login constructor(props) {
super(props);
this.state = {
name: '',
role: -1
};
this.setUserData = this.setUserData.bind(this);
}
setUserData({ name, role }) {
this.setState({ name, role });
}
render() {
return ( return (
<SingleRouter history={History}> <UserContext.Provider value={{ userData: this.state, setUserData: this.setUserData }}>
<SingleRouter>
<Route path="/" component={AppContainer} /> <Route path="/" component={AppContainer} />
<Route path="/upload" component={UploadContainer} /> <Route path="/upload" component={UploadContainer} />
<Route path="/login" component={LogInContainer} /> <Route path="/login" component={LogInContainer} />
<Route path="/review" component={ReviewContainer} /> <Route path="/review" component={ReviewContainer} />
<Route component={CheckLogIn} />
</SingleRouter> </SingleRouter>
</UserContext.Provider>
); );
}
}
function CheckLogIn() {
return localStorage.getItem('jwt') ? null : <Redirect from={/^\/.*/} to="/login" />;
} }
export default App; export default App;

查看文件

@ -1,6 +1,6 @@
.spinner { .spinner {
width: 20px; width: 1.5em;
height: 20px; height: 1.5em;
margin: 0 auto; margin: 0 auto;
} }
.spinner > svg { .spinner > svg {

查看文件

@ -0,0 +1,53 @@
import './userControl.css';
import { images } from '../../resources.json';
import { UserContext } from '../../helper/Context';
import Spinner from '../Spinner/Spinner';
import { get } from '../../helper/axios';
import { apis } from '../../helper/apis';
import { alert } from '../../helper/alert';
export default function UserControl(props) {
return (
<UserContext.Consumer>
{({ userData, setUserData }) => (
userData.role === 2
? (
<div className="user">
<button
className="btn btn-hollow btn-straight"
onClick={() => setUserData({ role: -1, name: '' })}
>退出审核</button>
</div>
) : (
<div className="user">
{
userData.role === -1
? (
<Spinner />
) : (
<div className="user-avatar">
<img src={images.avatar} alt="user avatar" />
</div>
)
}
<div className="user-name">{userData.name || "加载中"}</div>
{
(() => {
if (userData.role !== -1) return null;
get(apis.getProfile).then(({ data, status, networkStatus }) => {
if (networkStatus !== 200) return;
if (!status) return alert('获取用户信息失败:' + data + ',请稍候刷新再试');
setUserData({
name: data.realName,
role: data.role
});
});
return null;
})()
}
</div>
)
)}
</UserContext.Consumer>
);
}

查看文件

@ -0,0 +1,19 @@
.user-name {
height: 100%;
font-size: 18px;
height: 50px;
line-height: 50px;
color: white;
margin-left: 10px;
}
.user-avatar {
width: 40px;
height: 40px;
}
.user-avatar > img {
border-radius: 50%;
width: 100%;
height: 100%;
}

3
src/helper/Context.js Normal file
查看文件

@ -0,0 +1,3 @@
import { createContext } from "react";
export const UserContext = createContext({});

查看文件

@ -1,11 +1,13 @@
import axios from 'axios'; import axios from 'axios';
import { failed } from './alert'; import { failed } from './alert';
import qs from 'qs';
import History from './history';
export function get(url) { export function get(url) {
return send( return send(
axios.get(url, { axios.get(url, {
headers: { headers: {
Authorization: 'Bearer ' + localStorage.getItem('jwt'), Authorization: localStorage.getItem('jwt'),
"Allow-Control-Allow-Origin": "*" "Allow-Control-Allow-Origin": "*"
} }
}), }),
@ -14,9 +16,9 @@ export function get(url) {
} }
export function post(url, data) { export function post(url, data) {
return send( return send(
axios.post(url, data, { axios.post(url, qs.stringify(data), {
headers: { headers: {
Authorization: 'Bearer ' + localStorage.getItem('jwt'), Authorization: localStorage.getItem('jwt'),
"Allow-Control-Allow-Origin": "*" "Allow-Control-Allow-Origin": "*"
} }
}), }),
@ -33,6 +35,7 @@ async function send(xhr, retryFunc) {
} }
try { try {
const { data } = await xhr; const { data } = await xhr;
console.log("recv: ", data);
return { return {
...data, ...data,
networkStatus: 200 networkStatus: 200
@ -43,16 +46,16 @@ async function send(xhr, retryFunc) {
networkStatus: err?.response?.status ?? -1, networkStatus: err?.response?.status ?? -1,
status: false status: false
}; };
if (err?.response?.status === 401) {
History.force('/login');
return failData;
}
waitToSend.push(retryFunc); waitToSend.push(retryFunc);
if (err.message === 'Network Error') if (err.message === 'Network Error')
return await failed('您的设备似乎断网了,请检查网络后重试或刷新', flushWaitList) || failData; return await failed('您的设备似乎断网了,请检查网络后重试或刷新', flushWaitList) || failData;
else if (err?.response?.status === 401) if (err?.response?.status === 504)
History.force('/login');
else if (err?.response?.status === 504)
return await failed('请求超时,请耐心等待几秒后重试或刷新', flushWaitList) || failData; return await failed('请求超时,请耐心等待几秒后重试或刷新', flushWaitList) || failData;
else
return await failed('服务器出现问题,请稍后重试或刷新', flushWaitList) || failData; return await failed('服务器出现问题,请稍后重试或刷新', flushWaitList) || failData;
return failData;
} }
} }

查看文件

@ -1,4 +1,8 @@
import { Component } from 'react'; import { Component } from 'react';
import { apis } from '../helper/apis';
import { post } from '../helper/axios';
import History from '../helper/history';
import Spinner from '../components/Spinner/Spinner';
import './login.css'; import './login.css';
export class LogInContainer extends Component { export class LogInContainer extends Component {
@ -7,6 +11,7 @@ export class LogInContainer extends Component {
this.state = { this.state = {
username: '', username: '',
password: '', password: '',
processing: false,
error: false, error: false,
errorMessage: '', errorMessage: '',
}; };
@ -20,40 +25,57 @@ export class LogInContainer extends Component {
handleSubmit(e) { handleSubmit(e) {
e.preventDefault(); e.preventDefault();
const { username, password } = this.state; const { username, password } = this.state;
this.props.login(username, password); if (!username)
return this.setState({ error: true, errorMessage: '学号不能为空' });
if (!password)
return this.setState({ error: true, errorMessage: '密码不能为空' });
if (!username.match(/^\d*$/))
return this.setState({ error: true, errorMessage: '学号必须为数字' });
this.setState({ processing: true });
post(apis.login, { username, password }).then(({ data, status, networkStatus }) => {
this.setState({ processing: false });
if (networkStatus !== 200) return;
if (!status) return this.setState({ error: true, errorMessage: data });
localStorage.setItem('jwt', data);
History.replace('/');
});
} }
render() { render() {
const { error, errorMessage } = this.state; const { error, errorMessage, processing } = this.state;
return ( return (
<div className="login"> <div className="login">
<h1>Login</h1> <h1>登录</h1>
<form onSubmit={this.handleSubmit.bind(this)}> <form onSubmit={this.handleSubmit.bind(this)}>
<div className="form-group"> <div className="form-group">
<label htmlFor="username">Username</label>
<input <input
type="text" type="text"
name="username" name="username"
className="form-control" className="form-control"
placeholder="Username" placeholder="学号"
onChange={this.handleChange.bind(this)} onChange={this.handleChange.bind(this)}
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="password">Password</label>
<input <input
type="password" type="password"
name="password" name="password"
className="form-control" className="form-control"
placeholder="Password" placeholder="密码"
onChange={this.handleChange.bind(this)} onChange={this.handleChange.bind(this)}
/> />
</div> </div>
<button type="submit" className="btn btn-primary"> <button type="submit" className="btn btn-straight btn-sdu" disabled={processing || !this.state.username || !this.state.password}>
Login {
processing
? (
<Spinner />
)
: "登录"
}
</button> </button>
{error && {error &&
<div className="alert alert-danger"> <div className={"alert"}>
{errorMessage} {errorMessage}
</div>} </div>}
</form> </form>

查看文件

@ -2,6 +2,7 @@
"images": { "images": {
"poster": "image/poster.jpg", "poster": "image/poster.jpg",
"icon": "image/sdu_icon.png", "icon": "image/sdu_icon.png",
"name": "image/sdu_name.png" "name": "image/sdu_name.png",
"avatar": "image/user.jpg"
} }
} }

查看文件

@ -8,26 +8,6 @@
background-color: #9D0004; background-color: #9D0004;
} }
.sdu,
.user {
height: 100%;
display: flex;
align-items: center;
}
.sdu-logo,
.user-logo {
height: 100%;
}
.sdu-name {
height: 40px;
margin-left: 10px;
}
.user-name {
height: 100%;
line-height: 60px;
font-size: 24px;
}
.content { .content {
width: 800px; width: 800px;
margin: 0 auto; margin: 0 auto;

查看文件

@ -7,6 +7,7 @@ import { alert } from '../helper/alert';
import { images } from '../resources.json'; import { images } from '../resources.json';
import './upload.css'; import './upload.css';
import UserControl from '../components/UserControl/UserControl';
export class UploadContainer extends Component { export class UploadContainer extends Component {
constructor(props) { constructor(props) {
@ -60,10 +61,7 @@ export class UploadContainer extends Component {
<img src={images.icon} className="sdu-logo" alt="logo" /> <img src={images.icon} className="sdu-logo" alt="logo" />
<img src={images.name} className="sdu-name" alt="" /> <img src={images.name} className="sdu-name" alt="" />
</div> </div>
<div className="user"> <UserControl />
<img src="" className="user-avatar" alt="avatar" />
<div className="user-name"></div>
</div>
</div> </div>
<div className="content"> <div className="content">
<div className="message-box"> <div className="message-box">