当前位置: 首页 > news >正文

react项目中没有路由守卫,需要拦截的话,只能在路径上拦截,可以自己去封装 Route

项目 1 我们直接上代码
App 文件
在这里插入图片描述

import React from 'react';
import { Provider } from 'mobx-react';
import store from '@/store/index';
import ElLayout from '@/layout';
import MultiTabMobx from '@/store/multiTab';
import './style/index.less';
import {
  BrowserRouter as Router,
  Route,
  Redirect,
  Switch
} from 'react-router-dom';
import { isLogin } from '@/utils/utils';
import Login from '@/page/Login/index';
import Err404 from '@/page/errPage/Err404';
class App extends React.Component<any> {
  componentWillMount() {
    store.getPrincipal();
  }
  componentDidMount() {
    //在页面刷新时将mobx里的信息保存到sessionStorage里
    window.addEventListener('beforeunload', () => {
      store.setPrincipal();
    });
  }
  render() {
    return (
      <Router>
        <Provider store={store} multiTabMobx={MultiTabMobx}>
          {/* <ElLayout /> */}
          <Switch>
            <Route path='/login' component={Login} /> // 登录页面
            <Route path='/404' component={Err404} />
            <Route
              path='/'
              render={() =>
                !!isLogin() ? <ElLayout /> : <Redirect to='/login' />
              }
            />
            {/* <Route path='*' component={Err404} /> */}
          </Switch>
        </Provider>
      </Router>
    );
  }
}

export default App;

在这里插入图片描述
那么我们的isLogin在 utils文件下

import { message } from 'antd';
import { pathToRegexp } from 'path-to-regexp';
import route from '@/config'; // 路由配置指向文件引入,自己写的路由地址文件

/**
 * @name isIE
 * @description 判断是否是IE
 */
export function isIE() {
  const bw = window.navigator.userAgent;
  const compare = (s) => bw.indexOf(s) >= 0;
  const ie11 = (() => 'ActiveXObject' in window)();
  return compare('MSIE') || ie11;
}
	
	
/**
 * @name isLogin
 * @description 判断是否登录
 */
export function isLogin() {
  return localStorage.getItem('Authorization');
}


/**
 * @interface exportParam
 * @description 导出
 */
interface exportParam {
  url: string;
  params?: Object;
  fileName: string;
  timeOut?: Number;
  method?: 'POST' | 'GET';
}



/**
 * @name requestExport
 * @description 发送请求
 * @param url
 * @param params
 * @param method
 * @param signal
 * @param controller
 * @returns
 */
function requestExport(url, params, method, signal, controller) {
  function getRouteMap() {
    const storageItemName = `el_routemaps_${process.env.REACT_APP_DOMAIN_NAME}`
    let routemap = JSON.parse(sessionStorage.getItem(storageItemName));
    if (routemap) {
      return routemap;
    } else {
      routemap = new Object();
      function generateRoute(route) {
        route.map((v) => {
          if (v.path) {
            routemap[v.name] = v.path;
          }
          if (v.routes) {
            generateRoute(v.routes);
          }
        });
      }
      generateRoute(route);
      sessionStorage.setItem(storageItemName, JSON.stringify(routemap));
      return routemap;
    }
  }

  return new Promise((resolve) => {
    const routemap = getRouteMap();
    let routeKey = '';
    for (let i in routemap) {
      if (pathToRegexp(routemap[i]).test(window.location.pathname)) {
        routeKey = i;
      }
    }
    let options: any = {
      method: method,
      headers: new Headers({
        'Content-Type': 'application/json;application/octet-stream',
        Authorization: `${window.localStorage.getItem('Authorization')}`,
        RouteKey: routeKey
      }),
      signal: signal
    };
    if (method === 'POST') {
      options.body = JSON.stringify(params);
    }
    console.log('options', options);
    fetch(url, { ...options })
      .then((response) => {
        const { ok } = response;
        if (!ok) {
          message.error('网络错误');
          controller.abort();
          throw '网络错误';
        }
        return response;
      })
      .then((res) => res.blob())
      .catch((error) => {
        throw error;
      })
      .then((response) => {
        resolve(response);
      });
  });
}



/**
 * @name timeoutPromise
 * @description 超时处理
 * @param timeout
 * @param controller
 * @returns
 */
export function timeoutPromise(timeout, controller) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Response('timeout', { status: 521, statusText: 'timeout ' }));
      //超时自动关闭当前请求
      controller.abort();
    }, timeout);
  });
}



/**
 * @name download
 * @description 下载导出文件
 * @param blobs
 * @param fileName
 */
function download(blobs, fileName) {
  const defaultName = '未命名的导出文件';
  const blob = new Blob([blobs]);
  const name = `${fileName ? fileName : defaultName}.xlsx`;
  if ('download' in document.createElement('a')) {
    // 非IE下载
    const elink = document.createElement('a');
    elink.download = name;
    elink.style.display = 'none';
    elink.href = URL.createObjectURL(blob);
    document.body.appendChild(elink);
    elink.click();
    URL.revokeObjectURL(elink.href); // 释放URL 对象
    document.body.removeChild(elink);
  } else {
    // IE10+下载
    navigator.msSaveBlob(blob, fileName);
  }
}



/**
 * @name commonExport
 * @description 公共导出
 * @param param0
 * @returns
 */
export function commonExport({
  url,
  params,
  method = 'POST',
  fileName,
  timeOut
}: exportParam) {
  let controller = new AbortController();
  let signal = controller.signal;
  let time = timeOut ? timeOut : 60000;
  return Promise.race([
    requestExport(url, params, method, signal, controller),
    timeoutPromise(time, controller)
  ])
    .then((resp: any) => {
      download(resp, fileName);
    })
    .catch((err) => {
      throw err + '请求超时';
    });
}



/**
 * @name enCodeStr
 * @description 前端字符串加密
 * @param str
 * @returns
 */
export const enCodeStr = (str: string): string => {
  return btoa(str);
};




/**	
 * @name enCodeStr
 * @description 前端字符串解密
 * @param str
 * @returns
 */
export const deCodeStr = (str: string): string => {
  return atob(str);
};

在这里插入图片描述

那么我们登录页面 Login

import React from 'react';
import { Tabs, Form, Input, Button } from 'antd';
import { LockBlack, UserBlack, LoadingBlack } from '@/components/el/ElIcon';
import { observer, inject } from 'mobx-react';
import * as service from './service';
import './style.less';
import { ElNotification } from '@/components/el';
import { enCodeStr } from '@/utils/utils'; //  前端字符串加密

const { TabPane } = Tabs;

interface State {
  loading: boolean;
  captchaLoading: boolean;
  isShow: boolean;
  answer: string;
  captcha: { [props: string]: any };
  redirectUrl: string;
  openEnvironmentDesc: boolean;
  environmentDesc: string;
}

@inject('store') // 注入
@observer // 观察者
class Login extends React.Component<{ [props: string]: any }, State> {
  constructor(props) {
    super(props);
    this.state = {
      loading: false,
      captchaLoading: false,
      captcha: {},
      isShow: true,
      answer: '',
      redirectUrl: '',
      openEnvironmentDesc: true,
      environmentDesc: ''
    };
  }

// 模块渲染后 立即执行的
  componentDidMount() {
    localStorage.removeItem('Authorization');
    this.getCaptcha(); //  获取验证码
    this.getRedirectUrl();
    this.initEnvironment(); // 初始化环境
  }
  initEnvironment = () => {
    const href = window.location.href;
    if (href.indexOf('nrp-dev') > -1) {
      this.setState({
        environmentDesc: '开发环境'
      });
    }
    if (href.indexOf('nrp-test') > -1) {
      this.setState({
        environmentDesc: '测试环境'
      });
    }
    if (href.indexOf('nrp-uat') > -1) {
      this.setState({
        environmentDesc: 'Uat环境'
      });
    }
    if (href.indexOf('localhost') > -1) {
      this.setState({
        environmentDesc: '本地环境'
      });
    }
  };
  getRedirectUrl = () => {
    if (window.location.search && window.location.search.indexOf('=') > -1) {
      const redirectUrl = window.location.search.split('=')[1];
      this.setState({
        redirectUrl
      });
    }
  };
  
  //调用接口
  getCaptcha = async () => {
    this.setState({ captchaLoading: true });
    const res: any = await service.getCaptcha();
    if (res.success) {
      this.setState({ captcha: res.data });
    } else {
      ElNotification({
        type: 'error',
        message: res.msg
      });
    }
    this.setState({ captchaLoading: false });
  };

// 登录,下边有调用
  login = async (params) => {
    this.setState({ loading: true });
    const res: any = await service.login({
      ...params,
      capuid: this.state.captcha.uuid
    });
    this.setState({ loading: false });
    if (res.success) {
      // 浏览器本地缓存token
      localStorage.setItem(
        'Authorization',
        `${res.data.token_type} ${res.data.access_token}`
      );

      // 全局数据栈缓存用户信息
      const userRes = await service.getCurrent();

      if (userRes.success) {
        const orgRes = await service.getOrgInfo();
        if (orgRes.success) {
          this.props.store.updatePrincipal({
            ...userRes.data,
            buData: orgRes.data
          });
        }
      }
      const redirectUrl = this.state.redirectUrl;
      this.setState({
        redirectUrl: ''
      });
      // 跳转至首页
      this.props.history.push(redirectUrl ? redirectUrl : '/dashboard', {
        replace: true
      });
    } else {
      // 重新获取验证码
      this.getCaptcha();
      ElNotification({
        type: 'error',
        message: res.msg
      });
    }
  };

  // 页面交互事件处理
  handleRefreshCaptcha = () => {
    this.getCaptcha();
  };
  handleLogin = (values) => {
    values.password = enCodeStr(values.password);
    this.login(values);
  };
  handleValidateFail = ({ errorFields }) => {
    ElNotification({
      type: 'error',
      message: errorFields[0].errors[0]
    });
  };

  render() {
    return (
      <div className={'container'}>
        <div className={'content'}>
          <i className={'logo'} />

          <i className={'img'}>
            <span style={{ fontSize: '80px', color: 'red' }}>
              {this.state.environmentDesc}
            </span>
          </i>

          <Tabs className={'tabs'} tabBarGutter={72}>
            <TabPane tab='账号密码登录' key='1'>
              <Form
                className={'form'}
                onFinish={this.handleLogin}
                onFinishFailed={this.handleValidateFail}
                initialValues={{
                  username: '',
                  password: ''
                }}
              >
                <Form.Item
                  name='username'
                  required
                  rules={[{ required: true, message: '请输入账户名' }]}
                >
                  <Input
                    prefix={<UserBlack className={'icon'} />}
                    className={'input'}
                    placeholder='账户名'
                    autoComplete='off'
                  />
                </Form.Item>
                <Form.Item
                  name='password'
                  required
                  rules={[{ required: true, message: '请输入密码' }]}
                >
                  <Input.Password
                    prefix={<LockBlack className={'icon'} />}
                    className={'input'}
                    placeholder='密码'
                    autoComplete='off'
                  />
                </Form.Item>
                <Form.Item
                  name='captcha'
                  required
                  rules={[{ required: true, message: '请输入验证码' }]}
                >
                  <Input
                    className={'captchaInput'}
                    placeholder='验证码'
                    autoComplete='off'
                    addonAfter={
                      <div
                        onClick={this.handleRefreshCaptcha}
                        style={{ width: 80 }}
                      >
                        {this.state.captchaLoading ? (
                          <LoadingBlack />
                        ) : (
                          <img
                            src={this.state.captcha && this.state.captcha.img}
                            alt='captcha'
                            className={'captcha'}
                          />
                        )}
                      </div>
                    }
                  />
                </Form.Item>
                {/* <Form.Item>
                  <Form.Item name='remember' valuePropName='checked' noStyle>
                    <Checkbox disabled={true} className={'checkbox'}>
                      自动登录
                    </Checkbox>
                  </Form.Item>
                  <a
                    style={{ opacity: 0.2 }}
                    className={'forget'}
                    href='javascript:return false;'
                  >
                    忘记密码?
                  </a>
                </Form.Item> */}
                <Form.Item>
                  <Button
                    type='primary'
                    htmlType='submit'
                    className={'input'}
                    loading={this.state.loading}
                  >
                    登录
                  </Button>
                  {this.state.isShow ? (
                    <div id='answer'>{this.state.captcha.captext}</div>
                  ) : null}
                </Form.Item>
              </Form>
            </TabPane>
            {/* <TabPane tab='手机号登录' key='2' /> */}
          </Tabs>
          <span className={'footer'}>
            Copyright© 2021 云时通产品研发技术部出品{' '}
            <a href='https://beian.miit.gov.cn/#/Integrated/recordQuery'>
              沪ICP备18031690号-1
            </a>
          </span>
        </div>
      </div>
    );
  }
}
export default Login;

在这里插入图片描述
这样就结束了。

====================
项目2 中我们直接上代码

import React from "react";
import ReactDOM from 'react-dom'
import {
  HashRouter as Router,
  Route,
  Link,
  Redirect,
  withRouter
} from "react-router-dom";

解说

 1.  点击公共页面
 2. 单击受保护页面
 3. 登陆
 4. 单击后退按钮,每次注意URL

const AuthExample = () => (
  <Router>
    <div>
      <AuthButton />
      <ul>
        <li>
          <Link to="/public">公共页面</Link>
        </li>
        <li>
          <Link to="/protected">需要登录验证的页面</Link>
        </li>
        // 或者
        {/* <li>
          <Link to={{ pathname: '/login', state: { from: { pathname: '/abc' } } }}>登录页面</Link>
        </li> */}
      </ul>

      <Route path="/public" component={Public} />
      <Route path="/login" component={Login} />
      
      {/* PrivateRoute 自定义 的路由组件实现路由拦截功能  通过将props,path="/protected" component={Protected}传过去*/}
      
      <PrivateRoute path="/protected" component={Protected} />
    </div>
  </Router>
);


const fakeAuth = {
    // fakeAuth登录的对象集合
  isAuthenticated: false, // 登录状态

  // 登录方法
  authenticate(cb) {
    this.isAuthenticated = true;
    setTimeout(cb, 100); // fake async
  },

  // 退出登录方法
  signout(cb) {
    this.isAuthenticated = false;
    setTimeout(cb, 100);
  }
};
// withRouter 方法重新包装router
const AuthButton = withRouter(
  ({ history }) =>
  // 登录状态 登录了就显示退出, 没有登录就显示没有登录
    fakeAuth.isAuthenticated ? (
      <p>
        Welcome!{" "}
        <button
          onClick={() => {
              //  函数式路由跳转
            fakeAuth.signout(() => history.push("/"));
          }}
        >
          退出登录
        </button>
      </p>
    ) : (
      <p>您没有登录</p>
    )
);

自己对 Route 组件做的一个封装。(高阶组件)
// props.path
// props.component
// rest = { path: ‘/protected’ }
// 自己定义的一个Router组件,来实现路由拦截功能
// component: Component,将props中的component解构赋值出来并且实施重命名,应为下面是要作为组件使用
// …rest 是接受剩下的元素

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route
  // 这是在路由中将剩余的props元素展开
    {...rest} // 里面有路径 和路由页面的三props属性
    render={
        //这里的props是路由三prop
        props =>
        // 登录对象集合中的登录状态
      fakeAuth.isAuthenticated ? (
          // 为true 就显示该组件
        <Component {...props} />
      ) : (
        <Redirect
        // 登录状态为false时重定向到登录页面
          to={{
            pathname: "/login",
            state: { from: props.location }
          }}
        />
      )
    }
  />
);

const Public = () => <h3>公共页面</h3>;
const Protected = () => <h3>需要登录的页面</h3>;
//登录页面
class Login extends React.Component {
  state = {
      // 登录状态
    redirectToReferrer: false
  };

  login = () => {
      // 点击登录时触发登录方法
    fakeAuth.authenticate(() => {
        // 将redirectToReferrer登录状态转变为true
      this.setState({ redirectToReferrer: true });
    });
  };

  render() {
      // 解构赋值到 from  有传递过来想去的页面就去这个页面   没有的话就去 /
    const { from } = this.props.location.state || { from: { pathname: "/" } };
    const { redirectToReferrer } = this.state;

    // 如果登录状态为true 就重定向到from页面
    if (redirectToReferrer) {
      return <Redirect to={from} />;
    }

    return (
      <div>
        <p>你还没有权限需要先登录 {from.pathname}</p>
        <button onClick={this.login}>登录</button>
      </div>
    );
  }
}

// export default AuthExample;
ReactDOM.render(<AuthExample />, document.getElementById('root'))

相关文章:

  • 跟锦数学160823-190322, 共 942 题
  • JavaScript新鲜事·第5期
  • vue 项目实战 递归
  • react 项目 tab列表 把返回的一个字段数组,全部 展示在一个字段里
  • Python语言学习 (六)1.2
  • js语法中 ?. 和 ?? 的含义以及用法说明
  • 工作中MySql常用操作
  • 命令模式-对象行为型
  • git 分支 合并 具体执行过程 详细
  • 2017届校招提前批面试回顾
  • 前端 通过id 查询详情,让form表单中某地址展示成超链接,点击跳转并查看情况
  • 运用Keil uVision新建一个工程
  • 前端 react 项目 常见的 面试题
  • Jquery 异步提交表单(post)
  • vue 项目 对 echarts 组件 的封装使用
  • 《深入 React 技术栈》
  • eclipse(luna)创建web工程
  • emacs初体验
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • Javascript基础之Array数组API
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • Redis在Web项目中的应用与实践
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • spring security oauth2 password授权模式
  • vue中实现单选
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 构建工具 - 收藏集 - 掘金
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 网络应用优化——时延与带宽
  • 智能网联汽车信息安全
  • 《天龙八部3D》Unity技术方案揭秘
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • 通过调用文摘列表API获取文摘
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • #define,static,const,三种常量的区别
  • #etcd#安装时出错
  • #传输# #传输数据判断#
  • (Java数据结构)ArrayList
  • (windows2012共享文件夹和防火墙设置
  • (多级缓存)多级缓存
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (南京观海微电子)——I3C协议介绍
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (四) 虚拟摄像头vivi体验
  • (转) ns2/nam与nam实现相关的文件
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .NET Core Web APi类库如何内嵌运行?
  • @Builder用法
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法