Reinit project

This commit is contained in:
Limo Saplf
2019-08-31 20:44:15 +08:00
commit b51ae824c7
115 changed files with 20955 additions and 0 deletions

47
src/pages/check/check.tsx Normal file
View File

@@ -0,0 +1,47 @@
import React from 'react';
import { Button, Input } from 'antd';
import { connect, DispatchProp } from 'dva';
import { formatMessage } from 'umi-plugin-locale';
import { CheckModelState } from './model';
import AreaSelection from '@/components/AreaSelection';
const CheckPage: React.FC<DispatchProp & CheckModelState> = ({
areacodes,
areacode,
phone,
submitting,
dispatch,
}) => {
return (
<>
<AreaSelection
className="item"
areacode={areacode}
areacodes={areacodes}
onChange={areacode => dispatch({ type: 'check/onUpdateState', areacode })}
size="large"
/>
<Input
className="item"
placeholder={formatMessage({ id: 'common.phoneHolder' })}
value={phone}
onChange={e => dispatch({ type: 'check/onUpdatePhone', payload: e.target.value })}
size="large"
type="tel"
/>
<Button
className="item gradient"
type="primary"
loading={submitting}
onClick={() => dispatch({ type: 'check/submitCheck' })}
size="large"
>{formatMessage({ id: 'common.next' })}</Button>
</>
);
}
export default connect(
({ check } : any) => ({ ...check })
)(CheckPage) as any;

70
src/pages/check/index.tsx Normal file
View File

@@ -0,0 +1,70 @@
/**
* Routes:
* - ./src/pages/routes
*/
import React, { useEffect } from 'react';
import { Spin } from 'antd';
import { connect, DispatchProp } from 'dva';
import FittedImage from 'react-fitted-image';
import { CheckModelState, PageStep } from './model';
import { PageState } from '@/types/common';
import Header from '@/components/Header';
import bgPic from '@/assets/bg_login.png';
import styles from './style.less';
import CheckPage from './check';
import LoginPage from './login';
import RegisterPage from './register';
import { formatMessage } from 'umi-plugin-locale';
const Page: React.FC<DispatchProp & CheckModelState> = ({
pageState,
pageStep,
dispatch,
}) => {
useEffect(() => {
dispatch({ type: 'check/load' });
}, []);
if (pageState === PageState.Pending) {
return (
<div className={styles.loading}><Spin /></div>
);
}
if (pageState === PageState.Failure) {
return (
<div onClick={() => dispatch({ type: 'check/load' })}></div>
);
}
return (
<div className={styles.check}>
{
pageStep !== PageStep.Check &&
<Header
className={styles.header}
onReturn={() => dispatch({ type: 'check/back' })}
/>
}
<div className="cover">
<div className="image">
<FittedImage fit="contain" src={bgPic} />
</div>
<span>{formatMessage({ id: 'app.name' })}</span>
</div>
{{
[PageStep.Check]: <CheckPage />,
[PageStep.Login]: <LoginPage />,
[PageStep.Register]: <RegisterPage />,
[PageStep.Reset]: null,
[PageStep.ResetSuccess]: null,
}[pageStep]}
</div>
);
}
export default connect(
({ check } : any) => ({ ...check })
)(Page);

36
src/pages/check/login.tsx Normal file
View File

@@ -0,0 +1,36 @@
import React from 'react';
import { Button, Input } from 'antd';
import { connect, DispatchProp } from 'dva';
import { formatMessage } from 'umi-plugin-locale';
import { CheckModelState } from './model';
const LoginPage: React.FC<DispatchProp & CheckModelState> = ({
password,
submitting,
dispatch,
}) => {
return (
<>
<Input
className="item"
placeholder={formatMessage({ id: 'common.pwdHolder' })}
value={password}
onChange={e => dispatch({ type: 'check/onUpdatePassword', payload: e.target.value })}
size="large"
type="password"
/>
<Button
className="item gradient"
type="primary"
loading={submitting}
onClick={() => dispatch({ type: 'check/submitLogin' })}
size="large"
>{formatMessage({ id: 'check.login' })}</Button>
</>
);
}
export default connect(
({ check } : any) => ({ ...check })
)(LoginPage) as any;

164
src/pages/check/model.ts Normal file
View File

@@ -0,0 +1,164 @@
import { Model } from "dva";
import _ from 'lodash';
import { message } from 'antd';
import { delay } from 'dva/saga';
import { PageState, AreaCode, AreaCodes } from '@/types/common';
import { getAreaCodes, getUserInfo } from '@/services/common';
import { validPhoneInput, validPwdInput, validPhone, validPwd } from '@/utils/validate';
import { checkExistence, CheckResult, signIn } from './service';
import { formatMessage } from 'umi-plugin-locale';
export enum PageStep {
Check = 'check',
Login = 'login',
Register = 'register',
Reset = 'reset',
ResetSuccess = 'resetSuccess',
}
export interface CheckModelState {
pageState: PageState,
pageStep: PageStep,
areacodes: AreaCodes,
areacode: AreaCode | undefined, // 选中的地区
phone: string;
password: string;
submitting: boolean;
}
const model: Model = {
namespace: 'check',
state: {
pageState: PageState.Pending,
pageStep: PageStep.Check,
areacodes: [],
areacode: undefined,
phone: '',
password: '',
submitting: false,
} as CheckModelState,
effects: {
*back(_, { select, put }) {
const { pageStep: prev } = yield select((state: any) => state.check);
let pageStep = PageStep.Check;
switch (prev) {
case PageStep.Login:
case PageStep.Register:
case PageStep.ResetSuccess:
pageStep = PageStep.Check;
break;
case PageStep.Reset:
pageStep = PageStep.Login;
break;
}
yield put({ type: 'onUpdateState', pageStep });
},
*load(_, { call, put, all }) {
yield put({ type: 'onUpdatePageState', payload: PageState.Pending });
yield delay(800);
try {
const areacodes = yield call(getAreaCodes);
yield all([
put({ type: 'onUpdatePageState', payload: PageState.Success }),
put({ type: 'onUpdateAreaCodes', payload: areacodes }),
]);
} catch(e) {
yield put({ type: 'onUpdatePageState', payload: PageState.Failure });
}
},
*submitCheck(_, { put, call, select }) {
const { areacode, phone } = yield select((state: any) => state.check);
if (!areacode) return message.warn(formatMessage({ id: 'check.msgArea' }));
if (!validPhone(phone)) return message.warn(formatMessage({ id: 'check.msgPhone' }));
yield put({ type: 'onUpdateState', submitting: true });
try {
const checkResult: CheckResult = yield call(checkExistence, areacode, phone);
yield put({
type: 'onUpdateState',
pageStep: {
[CheckResult.Login]: PageStep.Login,
[CheckResult.Register]: PageStep.Register,
}[checkResult],
});
} catch(e) {
message.error(e.message);
} finally {
yield put({ type: 'onUpdateState', submitting: false });
}
},
*submitLogin(_, { put, call, select }) {
const { areacode, phone, password } = yield select((state: any) => state.check);
if (!areacode) return message.warn(formatMessage({ id: 'check.msgArea' }));
if (!validPhone(phone)) return message.warn(formatMessage({ id: 'check.msgPhone' }));
if (!validPwd(password)) return message.warn(formatMessage({ id: 'check.msgPwd' }))
yield put({ type: 'onUpdateState', submitting: true });
try {
const token = yield call(signIn, areacode, phone, password);
yield put({ type: 'user/updateToken', payload: token });
const user = yield call(getUserInfo, true);
yield put({ type: 'user/updateUser', payload: user });
} catch(e) {
message.error(e.message);
} finally {
yield put({ type: 'onUpdateState', submitting: false });
}
},
*submitRegister(_, { put }) {
yield put({ type: 'onUpdateState', submitting: true });
yield delay(800);
yield put({ type: 'onUpdateState', submitting: false });
yield put({ type: 'onUpdateState', pageStep: PageStep.Check });
},
},
reducers: {
onUpdatePageState(state, { payload: pageState } : any) {
return {
...state,
pageState,
};
},
onUpdateAreaCodes(state, { payload: areacodes } : any) {
return {
...state,
areacodes,
areacode: areacodes[0],
};
},
onUpdatePhone(state, { payload } : any) {
const phone = validPhoneInput(payload) ? payload : state.phone;
return {
...state,
phone,
};
},
onUpdatePassword(state, { payload } : any) {
const password = validPwdInput(payload) ? payload : state.password;
return {
...state,
password,
};
},
onUpdateState(state, newState) {
return {
...state,
...newState,
};
},
},
};
export default model;

View File

@@ -0,0 +1,36 @@
import React from 'react';
import { Button, Input } from 'antd';
import { connect, DispatchProp } from 'dva';
import { formatMessage } from 'umi-plugin-locale';
import { CheckModelState } from './model';
const RegisterPage: React.FC<DispatchProp & CheckModelState> = ({
password,
submitting,
dispatch,
}) => {
return (
<>
<Input
className="item"
placeholder={formatMessage({ id: 'common.pwdHolder' })}
value={password}
onChange={e => dispatch({ type: 'check/onUpdatePassword', payload: e.target.value })}
size="large"
type="password"
/>
<Button
className="item gradient"
type="primary"
loading={submitting}
onClick={() => dispatch({ type: 'check/submitRegister' })}
size="large"
>{formatMessage({ id: 'check.register' })}</Button>
</>
);
}
export default connect(
({ check } : any) => ({ ...check })
)(RegisterPage) as any;

View File

@@ -0,0 +1,30 @@
import http from '@/utils/http';
import { AreaCode } from '@/types/common';
import { encodePhone } from '@/utils/transform';
export enum CheckResult {
Login = 0,
Register = 1,
}
export async function checkExistence(areacode: AreaCode, phone: string): Promise<CheckResult> {
const { code, msg } = await http.get('/sign/check', { phone: encodePhone(areacode, phone) });
if (code !== CheckResult.Login && code !== CheckResult.Register) {
throw Error(msg);
}
return code as CheckResult;
}
export async function signIn(areacode: AreaCode, phone: string, password: string): Promise<string> {
const { ok, msg, data } = await http.post(
'/sign/in',
{
phone: encodePhone(areacode, phone),
password,
},
);
if (!ok || !data || !data.token) {
throw Error(msg);
}
return data.token;
}

View File

@@ -0,0 +1,49 @@
.check {
position: relative;
display: flex;
flex-direction: column;
align-items: stretch;
:global {
.item {
margin: 10px 5% 0;
width: auto;
}
.ant-btn {
margin-top: 30px;
}
.cover {
height: 40vh;
transition: height .2s;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: height .2s;
}
.image {
width: 30%;
margin-bottom: 20px;
// width: 100%;
}
}
}
.header {
position: absolute;
top: 0;
left: 0;
width: 100%;
background-color: #fff0;
}
.loading {
height: 100%;
text-align: center;
line-height: 30;
line-height: 80vh;
}