Reinit project
This commit is contained in:
47
src/pages/check/check.tsx
Normal file
47
src/pages/check/check.tsx
Normal 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
70
src/pages/check/index.tsx
Normal 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
36
src/pages/check/login.tsx
Normal 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
164
src/pages/check/model.ts
Normal 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;
|
||||
36
src/pages/check/register.tsx
Normal file
36
src/pages/check/register.tsx
Normal 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;
|
||||
30
src/pages/check/service.ts
Normal file
30
src/pages/check/service.ts
Normal 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;
|
||||
}
|
||||
49
src/pages/check/style.less
Normal file
49
src/pages/check/style.less
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user