前边登录页面也修改了。但细节还是需要去处理。
- Alert 用在登录面板里,觉得比较丑。还是用 message 组件来处理。
- Card 加上了 box-shadow 。
- 全部用多语言来控制。
继续完善 login 页面
src/user/login/index.tsx
import React, { useState } from 'react';
import { message, Card, Form, Input, Button, Checkbox } 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 [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') {
replaceGoto();
setTimeout(() => {
refresh();
}, 0);
return;
} else {
message.error(formatMessage({ id: 'login.formlogin.password-error' }));
}
} catch (error) {
message.error(formatMessage({ id: 'login.formlogin.login-fail' }));
}
setSubmitting(false);
};
return (
<div className={styles.container}>
<Card bordered={false} title={<FormattedMessage id='login.formlogin.login' />} className={styles['box-shadow']} style={{ width: 720 }}>
<Form {...itemLayout} initialValues={{ remember: true }} onFinish={handleSubmit} >
<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={styles['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;
src/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;
}
.other-way {
margin-bottom: 0;
}
.box-shadow {
box-shadow: 0 3px 6px -4px rgba(0,0,0,.12), 0 6px 16px 0 rgba(0,0,0,.08), 0 9px 28px 8px rgba(0,0,0,.05);
}
src/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': '如果没有账号,请',
'login.formlogin.login-success': '登录成功!',
'login.formlogin.login-fail': '登录失败,请重试!',
'login.formlogin.password-error': '账号密码错误!',
}
register 页面
register form 提交的数据继承父模板,这样好扩展数据。因为 password 和 repassword 只需要 password。
src/user/register/index.tsx
import React, { useState } from 'react';
import { message, Card, Form, Input, Button } from 'antd';
import { FormattedMessage, formatMessage, useModel } from 'umi';
import { getPageQuery } from '@/utils/utils';
import { RegisterParamsType, fakeAccountRegister } from '@/services/login';
import styles from './style.less';
interface RegisterParams extends RegisterParamsType{
repassword: string;
}
/**
* 此方法会跳转到 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 [submitting, setSubmitting] = useState(false);
const { refresh } = useModel('@@initialState');
const handleSubmit = async (values: RegisterParams) => {
const { password, repassword } = values;
if (password != repassword) {
message.error(formatMessage({ id: 'login.formlogin.password-not-same' }));
return;
}
delete values.repassword;
setSubmitting(true);
try {
// 注册
const msg = await fakeAccountRegister({ ...values });
if (msg.status === 'ok') {
replaceGoto();
setTimeout(() => {
refresh();
}, 0);
return;
} else {
}
} catch (error) {
message.error(formatMessage({ id: 'login.formlogin.register-fail' }));
}
setSubmitting(false);
};
return (
<div className={styles.container}>
<Card bordered={false} title={<FormattedMessage id='login.formlogin.register' />} className={styles['box-shadow']} style={{ width: 720 }}>
<Form {...itemLayout} initialValues={{ remember: true }} onFinish={handleSubmit} >
<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 name="repassword" label={<FormattedMessage id="login.formlogin.repassword" />} rules={[{ required: true, message: <FormattedMessage id='login.formlogin.repassword-rule' /> }]}>
<Input.Password placeholder={formatMessage({ id: 'login.formlogin.repassword-placeholder' })} />
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit" loading={submitting}>
<FormattedMessage id='login.formlogin.register' />
</Button>
</Form.Item>
<Form.Item {...tailLayout} className={styles['other-way']}>
<span><FormattedMessage id='login.formlogin.has-account' /></span>
<Button type="link" href="/user/login" size="small">
<FormattedMessage id='login.formlogin.login' />
</Button>
</Form.Item>
</Form>
</Card>
</div>
);
}
export default Login;
src/user/register/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;
}
.other-way {
margin-bottom: 0;
}
.box-shadow {
box-shadow: 0 3px 6px -4px rgba(0,0,0,.12), 0 6px 16px 0 rgba(0,0,0,.08), 0 9px 28px 8px rgba(0,0,0,.05);
}
src/user/register/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.repassword': '确认密码',
'login.formlogin.repassword-placeholder': '请输入确认密码',
'login.formlogin.password-rule': '密码不能为空',
'login.formlogin.repassword-rule': '确认密码不能为空',
'login.formlogin.has-account': '如果已注册账号,请',
'login.formlogin.password-not-same': '确认密码和密码不相同',
'login.formlogin.register-fail': '注册失败,请重试!',
}
到此,基本的页面和逻辑也完了。可这样,还远远不够。 request 需要用做拦截器,前后端分离这边会对接 jwt。发布的位置,项目前缀等都需要修改。对于后台,通常只需要登录就可以。不需要给它注册的入口。后边再完善吧。