改成自己的router,基本完成网络通信

This commit is contained in:
wzhqwq 2021-07-26 23:51:30 +08:00
父節點 d8c40cfe59
當前提交 41f5239ae6
共有 9 個檔案被更改,包括 294 行新增23 行删除

查看文件

@ -1,4 +1,9 @@
# inMyHeartFrontEnd
# 新双心 - 前端
# 开发或编译前
请根据`env.example.json`的格式按照自己的需求编写`env.json`,或者直接运行`cp env.example.json env.json`,否则无法通过编译!
## 维护者必读
### 开发或编译前
请根据`env.example.json`的格式按照自己的需求编写`env.json`,或者直接运行`cp env.example.json env.json`,**否则无法正确处理网络请求!**
### 网络请求的用法
引入./src/helper/axios.js(请以相对路径引入),尽情使用其中的post、get方法,它们会返回一个Promise(或可被视作Promise的AsyncFunction),且不会以抛出异常的方式通知网络错误,它会在服务器返回内容中插入属性`networkStatus`,**当值为200时才可以视作正确的服务器响应**,当值为-1时说明这是用户的网络错误,**你不需要处理200以外的情况,代码能够发出弹窗并建议用户重试**

查看文件

@ -9,8 +9,8 @@
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-dropzone": "^11.3.4",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3"
"react-scripts": "4.0.3",
"sweetalert2": "^11.0.20"
},
"scripts": {
"start": "react-scripts start",

查看文件

@ -1,18 +1,19 @@
import './App.css';
import { HashRouter, Route } from 'react-router-dom';
import { Route, SingleRouter } from './components/SingleRouter/SingleRouter';
import { AppContainer } from './index/index';
import { UploadContainer } from './upload/upload';
import { LogInContainer } from './login/login';
import { ReviewContainer } from './review/review';
import History from './helper/history';
function App() {
return (
<HashRouter>
<Route path="/" exact component={AppContainer} />
<SingleRouter history={History}>
<Route path="/" component={AppContainer} />
<Route path="/upload" component={UploadContainer} />
<Route path="/login" component={LogInContainer} />
<Route path="/review" component={ReviewContainer} />
</HashRouter>
</SingleRouter>
);
}

查看文件

@ -0,0 +1,9 @@
.full-window {
width: 100%;
height: 100%;
}
.full-window > div {
background-size: cover !important;
background-position: center !important;
}

查看文件

@ -0,0 +1,63 @@
import './Frame.css';
import { Component } from 'react';
import History from '../../helper/history';
export class SingleRouter extends Component {
historyRemover = null;
constructor(props) {
super(props);
this.state = {
page: null
};
}
componentDidMount() {
this.historyRemover = History.init(() => {
this.setState({
page: this.getPage()
});
});
this.setState({
page: this.getPage()
});
}
componentWillUnmount() {
this.historyRemover?.();
}
getPage() {
const path = History.getHref();
return this.props.children.filter(component =>
component && (!component.props.path ||
path === component.props.path ||
path.replace(/[\d]*$/, ':id') === component.props.path)
);
}
render() {
return (
<div className="full-window">
{this.state.page}
</div>
);
}
}
export function Route(props) {
return (
<div path={props.path}>
{props.children ?? <props.component />}
</div>
);
}
export function Redirect(props) {
if (History.getHref().match(props.from))
History.force(props.to);
return null;
}
export function Link(props) {
return (<a href={`/#${props.to}`} {...props}>{props.children}</a>);
}

45
src/helper/alert.js Normal file
查看文件

@ -0,0 +1,45 @@
import Swal from 'sweetalert2';
const SelfSwal = Swal.mixin({
customClass: {
confirmButton: 'btn-round-full-single',
cancelButton: 'btn-round-full-single',
},
buttonsStyling: false,
heightAuto: false
});
// ajax
export const success = content => {
return SelfSwal.fire({
html: content,
showConfirmButton: false,
timer: 1000,
icon: 'success'
});
}
export const failed = (content, afterRetry) => {
return afterRetry ?
SelfSwal.fire({
html: content,
showCancelButton: true,
confirmButtonText: '重试',
cancelButtonText: '放弃'
})
.then(result => result.isConfirmed ? afterRetry() : null) :
SelfSwal.fire({
html: content,
showConfirmButton: false,
timer: 1000,
icon: 'error'
})
.then(() => null);
}
export const alert = (content) => {
return SelfSwal.fire({
html: content,
confirmButtonText: '了解',
icon: 'warning'
});
}

查看文件

@ -1,18 +1,62 @@
import axios from 'axios';
import { failed } from './alert';
export function get(url) {
return axios.get(url, {
return send(
axios.get(url, {
headers: {
Authorization: 'Bearer ' + localStorage.getItem('jwt'),
"Allow-Control-Allow-Origin": "*"
}
});
}),
() => get(url)
);
}
export function post(url, data) {
return axios.post(url, data, {
return send(
axios.post(url, data, {
headers: {
Authorization: 'Bearer ' + localStorage.getItem('jwt'),
"Allow-Control-Allow-Origin": "*"
}
});
}),
() => post(url, data)
);
}
const waitToSend = [];
async function send(xhr, retryFunc) {
if (waitToSend.length) {
waitToSend.push(retryFunc);
return;
}
try {
const { data } = await xhr;
return {
...data,
networkStatus: 200
};
}
catch (err) {
const failData = {
networkStatus: err?.response?.status ?? -1,
status: false
};
waitToSend.push(retryFunc);
if (err.message === 'Network Error')
return await failed('您的设备似乎断网了,请检查网络后重试或刷新', flushWaitList) || failData;
else if (err?.response?.status === 401)
window.history.replaceState({}, '', '#/login');
else if (err?.response?.status === 504)
return await failed('请求超时,请耐心等待几秒后重试或刷新', flushWaitList) || failData;
else
return await failed('服务器出现问题,请稍后重试或刷新', flushWaitList) || failData;
return failData;
}
}
function flushWaitList() {
waitToSend.forEach(retryFunc => retryFunc());
waitToSend.splice(0, waitToSend.length);
}

98
src/helper/history.js Normal file
查看文件

@ -0,0 +1,98 @@
const listeners = [];
const statics = {
nowDepth: null,
forceURL: null
};
export default class History {
static push(url, state) {
// console.log('href: push', url, 'over', this.getHref());
if (url === this.getHref() || this.getHref() === '/forceback') {
// console.log('href: push ignored.');
return;
}
window.history.pushState({state, depth: ++statics.nowDepth}, null, '#' + url);
listeners.forEach(fn => fn());
}
static replace(url, state) {
// console.log('href: substitute', url, 'for', this.getHref());
if (url === this.getHref() || this.getHref() === '/forceback') {
// console.log('href: replace ignored.');
return;
}
window.history.replaceState({state, depth: statics.nowDepth}, null, '#' + url);
listeners.forEach(fn => fn());
}
static force(url) {
// console.log('href: force', this.getHref(), 'to', url);
if (url === this.getHref()) return;
const depth = window.history.state?.depth || 0;
if (depth) {
this.go(-depth);
statics.forceURL = url;
}
else
this.replace(url);
}
static listen(listener) {
listeners.push(listener);
return () => {
for (let i in listeners)
if (listeners[i] === listener)
listeners.splice(i, 1);
};
}
static go(d) {
window.history.go(d);
// console.log("go", d);
}
static back() {
if (statics.nowDepth === 0)
this.replace('/');
else
window.history.back();
// console.log("goback")
}
static forward() {
window.history.forward();
// console.log("goforward")
}
static getLocation() {
return document.location;
}
static getHref() {
return document.location.href.match(/#[^#]*$/)?.[0]?.replace('#', '') ?? '/';
}
static getId() {
return this.getHref().match(/[\d]*$/)?.[0] ?? null;
}
static init(pageChangeHandler) {
if (statics.nowDepth != null) return;
statics.nowDepth = window.history.state?.depth || 0;
window.addEventListener('hashchange', () => {
// console.log('href: hash changed to:', this.getHref());
listeners.forEach(fn => fn());
});
return this.listen(() => {
const depth = window.history.state?.depth || 0;
if (depth > statics.nowDepth) {
this.go(statics.nowDepth - depth);
return;
}
else
statics.nowDepth = depth;
if (statics.forceURL) {
const replaceURL = statics.forceURL;
statics.forceURL = null;
this.replace(replaceURL);
}
else {
// 触发换页
pageChangeHandler()
}
});
}
};

查看文件

@ -4,6 +4,7 @@ import Spinner from '../components/Spinner/Spinner';
import UploadUnit from '../components/UploadUnit/UploadUnit';
import { post } from '../helper/axios';
import { apis } from '../helper/apis';
import { alert } from '../helper/alert';
import './upload.css';
@ -39,8 +40,13 @@ export class UploadContainer extends Component {
if (this.state.submitting && (!this.state.file || this.state.url !== "")) {
// upload using axios
post(apis.submitMessage, { content: this.state.msg, image: this.state.url })
.then(res => {
this.setState({ submitting: false, msg: "", url: "" });
.then(({ data, status, networkStatus }) => {
if (networkStatus !== 200) return;
if (!status) return alert('提交内容失败:' + data);
this.setState({ submitting: false, msg: "", url: "", file: null });
alert('内容提交成功啦').then(({ isConfirmed }) => {
if (isConfirmed) window.close();
});
});
}
}