还是那句话,喜欢简洁,有强迫症,就是想弄。那么,从路由和注册登录开始。先去掉其他路由,仅仅保存注册登录和welcome。去掉其他页面,从零开始。
修改路由
编辑 /config/config.ts
,修改路由配置如下:
routes: [
{
path: '/user',
layout: false,
routes: [
{
name: 'login',
path: '/user/login',
component: './user/auth',
},
{
name: 'register',
path: '/user/register',
component: './user/auth',
}
],
},
{
path: '/welcome',
name: 'welcome',
icon: 'smile',
component: './Welcome',
},
{
path: '/',
redirect: '/welcome',
},
{
component: './404',
},
],
注册和路由都进同一个 component
, 然后根据路由进行不同的渲染。
pages 目录文件
Welcome.less
Welcome.tsx
404.tsx
document.ejs
------
user/login/index.tsx
user/register/index.tsx
------
user/login/locales/
user/register/locales/
zh-CN.ts
-----
user/login/style
user/register/style
index.less
先这么多吧。
user/login/index.tsx
的内容如下。其他文件都是空的。
import React from 'react';
const Login: React.FC<{}> = () => {
return (<div>登录</div>);
}
export default Login;
但是这样,当路由是 register 的时候,会跳转到 login 路由。得修改 /src/app.tsx
文件。
修改如下:
export async function getInitialState(): Promise<{
currentUser?: API.CurrentUser;
settings?: LayoutSettings;
}> {
// 如果是登录页面,不执行
if (history.location.pathname !== '/user/login' && history.location.pathname !== '/user/register') {
try {
const currentUser = await queryCurrent();
return {
currentUser,
settings: defaultSettings,
};
} catch (error) {
history.push('/user/login');
}
}
return {
settings: defaultSettings,
};
}
到此,注册登录路由貌似好了。通过测试,还有一点,如果用户已经登录过了,然后访问 login 路由,还是可以访问的。这样体验就不好了。
开始怼登录功能了
/user/login/index.tsx
import React, { useState } from 'react';
import { message, Card, Form, Input, Button, Checkbox, Alert } from 'antd';
import { FormattedMessage, formatMessage, useModel } from 'umi';
import { getPageQuery } from '@/utils/utils';
import { LoginParamsType, fakeAccountLogin } from '@/services/login';
import styles from './style.less';
/**
* 此方法会跳转到 redirect 参数所在的位置
*/
const replaceGoto = () => {
const urlParams = new URL(window.location.href);
const params = getPageQuery();
let { redirect } = params as { redirect: string };
if (redirect) {
const redirectUrlParams = new URL(redirect);
if (redirectUrlParams.origin === urlParams.origin) {
redirect = redirect.substr(urlParams.origin.length);
if (redirect.match(/^\/.*#/)) {
redirect = redirect.substr(redirect.indexOf('#') + 1);
}
} else {
window.location.href = '/';
return;
}
}
window.location.href = urlParams.href.split(urlParams.pathname)[0] + (redirect || '/');
};
const itemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
},
};
const tailLayout = {
wrapperCol: {
xs: { span: 24 },
sm: { offset: 6, span: 12 }
},
};
const Login: React.FC<{}> = () => {
const [userLoginState, setUserLoginState] = useState<API.LoginStateType>({});
const [submitting, setSubmitting] = useState(false);
const { refresh } = useModel('@@initialState');
const [autoLogin, setAutoLogin] = useState(true);
const handleSubmit = async (values: LoginParamsType) => {
setSubmitting(true);
try {
// 登录
const msg = await fakeAccountLogin({ ...values });
if (msg.status === 'ok') {
message.success('登录成功!');
replaceGoto();
setTimeout(() => {
refresh();
}, 0);
return;
}
// 如果失败去设置用户错误信息
setUserLoginState(msg);
} catch (error) {
message.error('登录失败,请重试!');
}
setSubmitting(false);
};
const { status } = userLoginState;
return (
<div className={styles.container}>
<Card bordered={false} title={<FormattedMessage id='login.formlogin.login' />} style={{ width: 720 }}>
<Form {...itemLayout} initialValues={{ remember: true }} onFinish={handleSubmit} >
{ status === 'error' && (<Alert className={styles.error} message="账号密码错误" type="error" />) }
<Form.Item name="account" label={<FormattedMessage id='login.formlogin.account' />} rules={[{ required: true, pattern: /^1[3456789]\d{9}$/, message: <FormattedMessage id='login.formlogin.account-rule' /> }]}>
<Input placeholder={formatMessage({ id: 'login.formlogin.account-placeholder' })} />
</Form.Item>
<Form.Item name="password" label={<FormattedMessage id="login.formlogin.password" />} rules={[{ required: true, message: <FormattedMessage id='login.formlogin.password-rule' /> }]}>
<Input.Password placeholder={formatMessage({ id: 'login.formlogin.password-placeholder' })} />
</Form.Item>
<Form.Item {...tailLayout} name="remember" valuePropName="checked">
<Checkbox checked={autoLogin} onChange={(e) => setAutoLogin(e.target.checked)}>
<FormattedMessage id="login.formlogin.remember-password" />
</Checkbox>
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit" loading={submitting}>
<FormattedMessage id='login.formlogin.login' />
</Button>
<Button type="link" href="#" target="_blank">
<FormattedMessage id='login.formlogin.forget-password' />
</Button>
</Form.Item>
<Form.Item {...tailLayout} className="other-way">
<span><FormattedMessage id='login.formlogin.none-account' /></span>
<Button type="link" href="/user/register" size="small">
<FormattedMessage id='login.formlogin.register' />
</Button>
</Form.Item>
</Form>
</Card>
</div>
);
}
export default Login;
****/user/login/styles.less****
@import '~antd/es/style/themes/default.less';
@login-bg: '../../../assets/user/login/login-bg.jpg';
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 100vh;
margin: -24px;
overflow: auto;
background-image: url(@login-bg);
background-repeat: no-repeat;
background-size: cover;
background-position: center;
}
.error {
margin-bottom: 20px;
}
user/login/locales/zh-CN.ts
export default {
'login.formlogin.login': '登录',
'login.formlogin.register': '注册',
'login.formlogin.account': '账号',
'login.formlogin.account-placeholder': '请输入账号',
'login.formlogin.account-rule': '请输入正确的账号格式(手机号码)',
'login.formlogin.password': '密码',
'login.formlogin.password-placeholder': '请输入密码',
'login.formlogin.password-rule': '密码不能为空',
'login.formlogin.remember-password': '记住密码',
'login.formlogin.forget-password': '忘记密码',
'login.formlogin.none-account': '如果没有账号,请',
}
登录用户名,我喜欢用 account 账号,所以 username 要换掉。更改 services/login.ts
import { request } from 'umi';
export interface LoginParamsType {
account: string;
password: string;
}
export async function fakeAccountLogin(params: LoginParamsType) {
return request<API.LoginStateType>('/api/login/account', {
method: 'POST',
data: params,
});
}
export async function getFakeCaptcha(mobile: string) {
return request(`/api/login/captcha?mobile=${mobile}`);
}
export async function outLogin() {
return request('/api/login/outLogin');
}
mock 也得改 mock/user.ts
'POST /api/login/account': (req: Request, res: Response) => {
const { password, account, type } = req.body;
if (password === '111111' && account === '13671638524') {
res.send({
status: 'ok',
type,
currentAuthority: 'admin',
});
access = 'admin';
return;
}
if (password === '111111' && account === '13682501124') {
res.send({
status: 'ok',
type,
currentAuthority: 'user',
});
access = 'user';
return;
}
res.send({
status: 'error',
type,
currentAuthority: 'guest',
});
access = 'guest';
},
到此,登录已经 ok 了。不过之前说过了。登录成功后,再访问 /user/login 路由,一样可以到登录页面。这样体验就不好了。下边,优化掉这个问题。
优化掉已经登录的页面再访问登录注册页面,就不需要再登录注册了
修改 app.tsx
import { getPageQuery } from '@/utils/utils';
/**
* 此方法会跳转到 redirect 参数所在的位置
*/
const replaceGoto = () => {
const urlParams = new URL(window.location.href);
const params = getPageQuery();
let { redirect } = params as { redirect: string };
if (redirect) {
const redirectUrlParams = new URL(redirect);
if (redirectUrlParams.origin === urlParams.origin) {
redirect = redirect.substr(urlParams.origin.length);
if (redirect.match(/^\/.*#/)) {
redirect = redirect.substr(redirect.indexOf('#') + 1);
}
} else {
window.location.href = '/';
return;
}
}
window.location.href = urlParams.href.split(urlParams.pathname)[0] + (redirect || '/');
};
export async function getInitialState(): Promise<{
currentUser?: API.CurrentUser;
settings?: LayoutSettings;
}> {
// 如果是登录页面,不执行
let currentUser = null;
try {
currentUser = await queryCurrent();
} catch (error) {
}
if (history.location.pathname !== '/user/login' && history.location.pathname !== '/user/register') {
if (currentUser) {
return {
currentUser,
settings: defaultSettings,
};
}
history.push('/user/login');
}
if (currentUser) {
replaceGoto();
}
return {
settings: defaultSettings,
};
}