基于umi4+antdpro+ts 登录带token

一.思路:

1.首先要有登录页面 

2.app.tsx 里面,如果没有登录,要跳转到登录页

3.utils里面:request.tsx(异常处理,请求拦截) ,index.ts(storage,存储在loaclstorage里面)


二.具体步骤:

1.登录页面

import { LockOutlined, UserOutlined } from '@ant-design/icons';
import { Alert, message } from 'antd';
import React, { useState } from 'react';
import ProForm, { ProFormCheckbox, ProFormText } from '@ant-design/pro-form';
import { useIntl, Link, history, FormattedMessage, SelectLang, useModel } from 'umi';
import { storage } from '@/utils';
import { login } from '@/services/user';
import authRouter from '@/utils/authRouter';
import styles from './index.less';


const LoginMessage: React.FC<{
  content: string;
}> = ({ content }) => (
  <Alert
    style={{
      marginBottom: 24,
    }}
    message={content}
    type="error"
    showIcon
  />
);

/** 此方法会跳转到 redirect 参数所在的位置 */
const goto = () => {
  if (!history) return;
  setTimeout(() => {
    const { query } = history.location; //如果ts报错,找不到query,就删掉行,直接跳转
    const { redirect } = query as { redirect: string };
    history.push(redirect || '/');
  }, 10);
};

const Login: React.FC = () => {
  const [submitting, setSubmitting] = useState(false);
  const [userLoginState, setUserLoginState] = useState<API.LoginResult>({});

  const { initialState, setInitialState } = useModel('@@initialState');

  const intl = useIntl();

  const handleSubmit = async (values: API.LoginParams) => {
    setSubmitting(true);
    try {
      // 登录
      const { results } = await login({ ...values });
      if (results.token) {
        const defaultloginSuccessMessage = intl.formatMessage({
          id: 'pages.login.success',
          defaultMessage: '登录成功!',
        });

        console.log(results);
        message.success(defaultloginSuccessMessage);
        setInitialState({ //初始信息
          ...initialState,
          currentUser: results,
          token: results.token,
          hasRouter: authRouter(results.access_data), //不同权限,侧边栏把不同
        });
        storage.set({ name: 'token', data: results.token }); //存储token
        storage.set({ name: 'userInfo', data: JSON.stringify(results) });
        storage.set({ name: 'hasRouter', data: JSON.stringify(authRouter(results.access_data)) });
        goto();
        return;
      }
      // 如果失败去设置用户错误信息
      setUserLoginState({ status: 'error' });
    } catch (error) {
      const defaultloginFailureMessage = intl.formatMessage({
        id: 'pages.login.failure',
        defaultMessage: '登录失败,请重试!',
      });
      message.error(defaultloginFailureMessage);
    }
    setSubmitting(false);
  };
  const { status, type: loginType } = userLoginState;

  return (
    <div className={styles.container}>
      <div className={styles.content}>
        <div className={styles.main}>
          <div className={styles.header}>
          </div>
          <div className={styles.form}>
            <ProForm
              initialValues={{
                autoLogin: true,
              }}
              submitter={{
                searchConfig: {
                  submitText: intl.formatMessage({
                    id: 'pages.login.submit',
                    defaultMessage: '登录',
                  }),
                },
                render: (_, dom) => dom.pop(),
                submitButtonProps: {
                  loading: submitting,
                  size: 'large',
                  style: {
                    width: '100%',
                  },
                },
              }}
              onFinish={async (values) => {
                handleSubmit(values as API.LoginParams);
              }}
            >
              {status === 'error' && loginType === 'account' && (
                <LoginMessage
                  content={intl.formatMessage({
                    id: 'pages.login.accountLogin.errorMessage',
                    defaultMessage: '账户或密码错误(admin/ant.design)',
                  })}
                />
              )}

              <>
                <ProFormText
                  name="username"
                  fieldProps={{
                    size: 'large',
                    prefix: <UserOutlined className={styles.prefixIcon} />,
                  }}
                  placeholder={intl.formatMessage({
                    id: 'pages.login.username.placeholder',
                  })}
                  rules={[
                    {
                      required: true,
                      message: <FormattedMessage id="pages.login.username.required" />,
                    },
                  ]}
                />
                <ProFormText.Password
                  name="password"
                  fieldProps={{
                    size: 'large',
                    prefix: <LockOutlined className={styles.prefixIcon} />,
                  }}
                  placeholder={intl.formatMessage({
                    id: 'pages.login.password.placeholder',
                  })}
                  rules={[
                    {
                      required: true,
                      message: <FormattedMessage id="pages.login.password.required" />,
                    },
                  ]}
                  extra={
                    <div style={{ textAlign: 'center', paddingTop: 6 }}>
                      <FormattedMessage
                        id="pages.login.forgotPassword"
                        defaultMessage="如忘记密码,请联系您的管理员进行重置"
                      />
                    </div>
                  }
                />
              </>

              {status === 'error' && loginType === 'mobile' && (
                <LoginMessage content="验证码错误" />
              )}

              <div className={styles.loginButtons}>
                <ProFormCheckbox noStyle>
                  <FormattedMessage id="pages.login.rememberUser" defaultMessage="记住账号" />
                </ProFormCheckbox>
                <ProFormCheckbox noStyle>
                  <FormattedMessage id="pages.login.rememberPassword" defaultMessage="记住密码" />
                </ProFormCheckbox>
                <ProFormCheckbox noStyle>
                  <FormattedMessage id="pages.login.rememberMe" defaultMessage="自动登录" />
                </ProFormCheckbox>
              </div>
            </ProForm>
          </div>
        </div>
      </div>
    </div>
  );
};

export default Login;

2.request.ts

这是umi3的处理。umi4官网已经改版,具体可以查看官网。我这里是提供另外一种改法。

import { notification, message, Modal } from 'antd';
import { storage } from '@/utils';
import config from './config';
import { history } from 'umi';
import { RequestOptionsInit } from 'umi-request';

const codeMessage = {
  200: '服务器成功返回请求的数据。',
  201: '新建或修改数据成功。',
  202: '一个请求已经进入后台排队(异步任务)。',
  204: '删除数据成功。',
  400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
  401: '用户没有权限(令牌、用户名、密码错误)。',
  403: '用户得到授权,但是访问是被禁止的。',
  404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
  405: '请求方法不被允许。',
  406: '请求的格式不可得。',
  410: '请求的资源被永久删除,且不会再得到的。',
  422: '当创建一个对象时,发生一个验证错误。',
  500: '服务器发生错误,请检查服务器。',
  502: '网关错误。',
  503: '服务不可用,服务器暂时过载或维护。',
  504: '网关超时。',
};

let isTokenOverdue = false;

export default {
  prefix: config.host,
  timeout: 60000,
  headers: {
    'Content-Type': 'application/json; charset=utf-8',
  },
  errorHandler: (error: { response: Response }): Response => {
    const { response } = error;
    if (response && response.status) {
      const errorText = codeMessage[response.status] || response.statusText;
      const { status, url } = response;

      notification.error({
        message: `请求错误 ${status}: ${url}`,
        description: errorText,
      });
    } else if (!response) {
      notification.error({
        description: '您的网络发生异常,无法连接服务器',
        message: '网络异常',
      });
    }
    return response;
  },
  errorConfig: {
    adaptor: (resData: any) => {
      return {
        ...resData,
        code: resData.status_code,
      };
    },
  },
  middlewares: [
    async function middlewareA(ctx: any, next: any) {
      await next();
      const { res } = ctx;

      if (res.status_code !== 200) {
        switch (res.status_code) {
          case 401:
            if (!isTokenOverdue) {
              Modal.error({
                title: '警告',
                content: '您的登录信息已过期,请重新登录!!!',
                onOk: () => {
                  history.replace({ pathname: '/welcome', query: { key: 'logout' } });
                },
              });
              isTokenOverdue = true;
            }

            break;

          default:
            message.error(res.msg);
            return false;
        }
      }
      return ctx;
    },
  ],
  requestInterceptors: [
    async function authHeaderInterceptor(url: string, options: RequestOptionsInit) {
      const authHeader = {
        Authorization: storage.get('token') ? `Token ${storage.get('token')}` : '',
        'YT-LANGUAGE': JSON.parse(storage.get('userInfo') as any)?.user_language || '',
      };
      return {
        url: `${url}`,
        options: { ...options, interceptors: true, headers: { ...options.headers, ...authHeader } },
      };
    },
  ],
};

新改法:

import { message, notification } from 'antd'
import { extend } from 'umi-request'

const codeMessage = {
    200: '服务器成功返回请求的数据。',
    201: '新建或修改数据成功。',
    202: '一个请求已经进入后台排队(异步任务)。',
    204: '删除数据成功。',
    400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
    401: '登录过期,请重新登陆。',
    403: '用户得到授权,但是访问是被禁止的。',
    404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
    406: '请求的格式不可得。',
    410: '请求的资源被永久删除,且不会再得到的。',
    422: '当创建一个对象时,发生一个验证错误。',
    500: '服务器发生错误,请检查服务器。',
    502: '网关错误。',
    503: '服务不可用,服务器暂时过载或维护。',
    504: '网关超时。',
}

/**
 * 异常处理程序
 */
const errorHandler = (error: { response: Response }): Response => {
    const { response } = error
    if (response && response.status) {
        const errorText = codeMessage[response.status] || response.statusText
        const { status, url } = response
        message.error(errorText)
        notification.error({
            message: `请求错误 ${status}: ${url}`,
            description: errorText,
        })
    } else if (!response) {
        // message.error('您的网络发生异常,无法连接服务器')
        notification.error({
            description: '您的网络发生异常,无法连接服务器',
            message: '网络异常',
        })
    }
    return response
}
/**
 * 配置request请求时的默认参数
 */
const request = extend({
    errorHandler, // 默认错误处理
    credentials: 'include', // 默认请求是否带上cookie
})
export default request

utils.ts

import moment from 'moment';

export const storage = {
  get: (name: string) => {
    return localStorage.getItem(name);
  },
  set: ({ name, data }: { name: string; data: string }) => {
    localStorage.setItem(name, data);
  },
  clearItem: (name: string) => {
    localStorage.removeItem(name);
  },
  clear: () => {
    localStorage.clear();
  },
};

export const formatTime = (time: string) => {
  return moment(time).format('YYYY-MM-DD HH:mm:ss');
};

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值