Write the Code. Change the World.

7月 24

还是那句话,喜欢简洁,有强迫症,就是想弄。那么,从路由和注册登录开始。先去掉其他路由,仅仅保存注册登录和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,
  };
}

发表回复

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