diff --git a/README.md b/README.md
index c6276b1..147aa02 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,9 @@
-# inMyHeartFrontEnd
+# 新双心 - 前端
-# 开发或编译前
-请根据`env.example.json`的格式按照自己的需求编写`env.json`,或者直接运行`cp env.example.json env.json`,否则无法通过编译!
\ No newline at end of file
+## 维护者必读
+
+### 开发或编译前
+请根据`env.example.json`的格式按照自己的需求编写`env.json`,或者直接运行`cp env.example.json env.json`,**否则无法正确处理网络请求!**
+
+### 网络请求的用法
+引入./src/helper/axios.js(请以相对路径引入),尽情使用其中的post、get方法,它们会返回一个Promise(或可被视作Promise的AsyncFunction),且不会以抛出异常的方式通知网络错误,它会在服务器返回内容中插入属性`networkStatus`,**当值为200时才可以视作正确的服务器响应**,当值为-1时说明这是用户的网络错误,**你不需要处理200以外的情况,代码能够发出弹窗并建议用户重试**
\ No newline at end of file
diff --git a/package.json b/package.json
index bc50585..0c717a6 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/App.js b/src/App.js
index 9b8c300..cbf1861 100644
--- a/src/App.js
+++ b/src/App.js
@@ -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 (
-
-
+
+
-
+
);
}
diff --git a/src/components/SingleRouter/Frame.css b/src/components/SingleRouter/Frame.css
new file mode 100644
index 0000000..57df597
--- /dev/null
+++ b/src/components/SingleRouter/Frame.css
@@ -0,0 +1,9 @@
+.full-window {
+ width: 100%;
+ height: 100%;
+}
+
+.full-window > div {
+ background-size: cover !important;
+ background-position: center !important;
+}
\ No newline at end of file
diff --git a/src/components/SingleRouter/SingleRouter.js b/src/components/SingleRouter/SingleRouter.js
new file mode 100644
index 0000000..08a8fcf
--- /dev/null
+++ b/src/components/SingleRouter/SingleRouter.js
@@ -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 (
+
+ {this.state.page}
+
+ );
+ }
+}
+
+export function Route(props) {
+ return (
+
+ );
+}
+
+export function Redirect(props) {
+ if (History.getHref().match(props.from))
+ History.force(props.to);
+ return null;
+}
+
+export function Link(props) {
+ return ({props.children});
+}
\ No newline at end of file
diff --git a/src/helper/alert.js b/src/helper/alert.js
new file mode 100644
index 0000000..5a5a2e9
--- /dev/null
+++ b/src/helper/alert.js
@@ -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'
+ });
+}
diff --git a/src/helper/axios.js b/src/helper/axios.js
index 883fdb8..919867d 100644
--- a/src/helper/axios.js
+++ b/src/helper/axios.js
@@ -1,18 +1,62 @@
import axios from 'axios';
+import { failed } from './alert';
export function get(url) {
- return axios.get(url, {
- headers: {
- Authorization: 'Bearer ' + localStorage.getItem('jwt'),
- "Allow-Control-Allow-Origin": "*"
- }
- });
+ 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, {
- headers: {
- Authorization: 'Bearer ' + localStorage.getItem('jwt'),
- "Allow-Control-Allow-Origin": "*"
- }
- });
+ 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);
}
\ No newline at end of file
diff --git a/src/helper/history.js b/src/helper/history.js
new file mode 100644
index 0000000..ef919d3
--- /dev/null
+++ b/src/helper/history.js
@@ -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()
+ }
+ });
+ }
+};
\ No newline at end of file
diff --git a/src/upload/upload.js b/src/upload/upload.js
index c5df44e..a29fe21 100644
--- a/src/upload/upload.js
+++ b/src/upload/upload.js
@@ -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();
+ });
});
}
}