Write the Code. Change the World.

7月 26

前边登录页面也修改了。但细节还是需要去处理。

  1. Alert 用在登录面板里,觉得比较丑。还是用 message 组件来处理。
  2. Card 加上了 box-shadow 。
  3. 全部用多语言来控制。

继续完善 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。发布的位置,项目前缀等都需要修改。对于后台,通常只需要登录就可以。不需要给它注册的入口。后边再完善吧。

发表评论

电子邮件地址不会被公开。 必填项已用*标注