• 【React】AntD组件的使用--极客园--02.登录模块


    基本结构搭建

    在这里插入图片描述

    实现步骤

    1. Login/index.js 中创建登录页面基本结构
    2. 在 Login 目录中创建 index.scss 文件,指定组件样式
    3. logo.pnglogin.png 拷贝到 assets 目录中

    代码实现
    pages/Login/index.js

    import './index.scss'
    import { Card, Form, Input, Button } from 'antd'
    import logo from '@/assets/logo.png'
    
    const Login = () => {
      return (
        
    {/* 登录表单 */}
    ) } export default Login

    pages/Login/index.scss

    .login {
      width: 100%;
      height: 100%;
      position: absolute;
      left: 0;
      top: 0;
      background: center/cover url('~@/assets/login.png');
    
      .login-logo {
        width: 200px;
        height: 60px;
        display: block;
        margin: 0 auto 20px;
      }
    
      .login-container {
        width: 440px;
        height: 360px;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        box-shadow: 0 0 50px rgb(0 0 0 / 10%);
      }
    
      .login-checkbox-label {
        color: #1890ff;
      }
    }
    
    

    表单校验实现

    在这里插入图片描述

    实现步骤

    1. 为 Form 组件添加 validateTrigger 属性,指定校验触发时机的集合
    2. 为 Form.Item 组件添加 name 属性
    3. 为 Form.Item 组件添加 rules 属性,用来添加表单校验规则对象
      在这里插入图片描述

    代码实现
    page/Login/index.js

    import './index.scss'
    import { Card, Form, Input, Button, message } from 'antd'
    import logo from '@/assets/logo.png'
    import { useDispatch } from 'react-redux'
    import { fetchLogin } from '@/store/modules/user'
    import { useNavigate } from 'react-router-dom'
    
    const Login = () => {
      const dispatch = useDispatch()
      const navigate = useNavigate()
      const onFinish = async (values) => {
        console.log(values)
        // 触发异步action fetchLogin
        await dispatch(fetchLogin(values))
        // 1. 跳转到首页
        navigate('/')
        // 2. 提示一下用户
        message.success('登录成功')
      }
      return (
        <div className="login">
          <Card className="login-container">
            <img className="login-logo" src={logo} alt="" />
            {/* 登录表单 */}
            <Form onFinish={onFinish} validateTrigger="onBlur">
              <Form.Item
                name="mobile" //指定校验字段名
                // 多条校验逻辑 先校验第一条 第一条通过之后再校验第二条
                rules={[
                  {
                    required: true,
                    message: '请输入手机号',
                  },
                  {
                    pattern: /^1[3-9]\d{9}$/,
                    message: '请输入正确的手机号格式'
                  }
                ]}>
                <Input size="large" placeholder="请输入手机号" />
              </Form.Item>
              <Form.Item
                name="code"
                rules={[
                  {
                    required: true,
                    message: '请输入验证码',
                  },
                ]}>
                <Input size="large" placeholder="请输入验证码" />
              </Form.Item>
              <Form.Item>
                <Button type="primary" htmlType="submit" size="large" block>
                  登录
                </Button>
              </Form.Item>
            </Form>
          </Card>
        </div>
      )
    }
    
    export default Login
    

    获取登录表单数据

    实现步骤

    1. 为 Form 组件添加 onFinish 属性,该事件会在点击登录按钮时触发
    2. 创建 onFinish 函数,通过函数参数 values 拿到表单值
    3. Form 组件添加 initialValues 属性,来初始化表单值
      在这里插入图片描述

    代码实现
    pages/Login/index.js

    // 点击登录按钮时触发 参数values即是表单输入数据
    const onFinish = formValue => {
      console.log(formValue)
    }
    
    
    ...

    在这里插入图片描述

    封装request工具模块

    业务背景: 前端需要和后端拉取接口数据,axios是使用最广的工具插件,针对于项目中的使用,我们需要做一些简单的封装

    实现步骤

    1. 安装 axios 到项目
    2. 创建 utils/request.js 文件
    3. 创建 axios 实例,配置 baseURL,请求拦截器,响应拦截器
    4. 在 utils/index.js 中,统一导出request
      在这里插入图片描述
    npm i axios
    
    // axios的封装处理
    import axios from "axios"
    import { getToken, removeToken } from "./token"
    import router from "@/router"
    // 1. 根域名配置
    // 2. 超时时间
    // 3. 请求拦截器 / 响应拦截器
    
    const request = axios.create({
      baseURL: 'http://geek.itheima.net/v1_0',
      timeout: 5000
    })
    
    // 添加请求拦截器
    // 在请求发送之前 做拦截 插入一些自定义的配置 [参数的处理]
    request.interceptors.request.use((config) => {
      // 操作这个config 注入token数据
      // 1. 获取到token
      // 2. 按照后端的格式要求做token拼接
      const token = getToken()
      if (token) {
        config.headers.Authorization = `Bearer ${token}`
      }
      return config
    }, (error) => {
      return Promise.reject(error)
    })
    
    // 添加响应拦截器
    // 在响应返回到客户端之前 做拦截 重点处理返回的数据
    request.interceptors.response.use((response) => {
      // 2xx 范围内的状态码都会触发该函数。
      // 对响应数据做点什么
      return response.data
    }, (error) => {
      // 超出 2xx 范围的状态码都会触发该函数。
      // 对响应错误做点什么
      // 监控401 token失效
      console.dir(error)
      if (error.response.status === 401) {
        removeToken()
        router.navigate('/login')
        window.location.reload()
      }
      return Promise.reject(error)
    })
    
    export { request }
    
    

    Axios

    使用Redux管理token

    安装Redux相关工具包

    npm i react-redux @reduxjs/toolkit
    

    配置Redux

    import { createSlice } from '@reduxjs/toolkit'
    import { http } from '@/utils'
    const userStore = createSlice({
      name: 'user',
      // 数据状态
      initialState: {
        token:''
      },
      // 同步修改方法
      reducers: {
        setUserInfo (state, action) {
          state.userInfo = action.payload
        }
      }
    })
    
    // 解构出actionCreater
    const { setUserInfo } = userStore.actions
    
    // 获取reducer函数
    const userReducer = userStore.reducer
    
    // 异步方法封装
    const fetchLogin = (loginForm) => {
      return async (dispatch) => {
        const res = await http.post('/authorizations', loginForm)
        dispatch(setUserInfo(res.data.token))
      }
    }
    
    export { fetchLogin }
    
    export default userReducer
    
    import { configureStore } from '@reduxjs/toolkit'
    
    import userReducer from './modules/user'
    
    export default configureStore({
      reducer: {
        // 注册子模块
        user: userReducer
      }
    })
    

    实现登录逻辑

    业务逻辑

    1. 跳转到首页
    2. 提示用户登录成功
    import { message } from 'antd'
    import useStore from '@/store'
    import { fetchLogin } from '@/store/modules/user'
    import { useDispatch } from 'react-redux'
    
    const Login = () => {
      const dispatch = useDispatch()
      const navigate = useNavigate()
      const onFinish = async formValue => {
        await dispatch(fetchLogin(formValue))
        navigate('/')
        message.success('登录成功')
      }
      return (
        
    ) } export default Login

    token持久化

    业务背景: Token数据具有一定的时效时间,通常在几个小时,有效时间内无需重新获取,而基于Redux的存储方式又是基于内存的,刷新就会丢失,为了保持持久化,我们需要单独做处理

    封装存取方法

    // 封装存取方法
    
    const TOKENKEY = 'token_key'
    
    function setToken (token) {
      return localStorage.setItem(TOKENKEY, token)
    }
    
    function getToken () {
      return localStorage.getItem(TOKENKEY)
    }
    
    function clearToken () {
      return localStorage.removeItem(TOKENKEY)
    }
    
    export {
      setToken,
      getToken,
      clearToken
    }
    

    实现持久化逻辑

    import { getToken, setToken } from '@/utils'
    const userStore = createSlice({
      name: 'user',
      // 数据
      initialState: {
        token: getToken() || ''
      },
      // 同步修改方法
      reducers: {
        setUserInfo (state, action) {
          state.token = action.payload
          // 存入本地
          setToken(state.token)
        }
      }
    })
    

    刷新浏览器,通过Redux调试工具查看token数据

    在这里插入图片描述

    请求拦截器注入token

    业务背景: Token作为用户的数据标识,在接口层面起到了接口权限控制的作用,也就是说后端有很多接口都需要通过查看当前请求头信息中是否含有token数据,来决定是否正常返回数据
    在这里插入图片描述

    拼接方式:config.headers.Authorization = Bearer ${token}}

    utils/request.js

    // 添加请求拦截器
    request.interceptors.request.use(config => {
      // if not login add token
      const token = getToken()
      if (token) {
        config.headers.Authorization = `Bearer ${token}`
      }
      return config
    })
    

    路由鉴权实现

    业务背景:封装 AuthRoute 路由鉴权高阶组件,实现未登录拦截,并跳转到登录页面
    实现思路:判断本地是否有token,如果有,就返回子组件,否则就重定向到登录Login

    实现步骤

    1. 在 components 目录中,创建 AuthRoute/index.jsx 文件
    2. 登录时,直接渲染相应页面组件
    3. 未登录时,重定向到登录页面
    4. 将需要鉴权的页面路由配置,替换为 AuthRoute 组件渲染

    代码实现
    components/AuthRoute/index.jsx

    import { getToken } from '@/utils'
    import { Navigate } from 'react-router-dom'
    
    const AuthRoute = ({ children }) => {
      const isToken = getToken()
      if (isToken) {
        return <>{children}
      } else {
        return 
      }
    }
    
    export default AuthRoute
    

    src/router/index.jsx

    import { createBrowserRouter } from 'react-router-dom'
    
    import Login from '@/pages/Login'
    import Layout from '@/pages/Layout'
    import AuthRoute from '@/components/Auth'
    
    
    const router = createBrowserRouter([
      {
        path: '/',
        element: ,
      },
      {
        path: '/login',
        element: ,
      },
    ])
    
    export default router
    
  • 相关阅读:
    阿里P8级大师精品之作:Spring源码深度解读(IOC/AOP/MVC/事务)
    安全信息化管理平台——数据分析与可视化
    阿里P8架构师推荐的从Paxos到Zookeeper分布式一致性原理与实践
    10月韩国MogaFX外汇交易下降
    二叉树和二叉堆和优先队列
    3.3、Linux项目自动化构建工具make/makefile
    JS创建一个数组(字面量)
    React之Jsx如何转换成真实DOM
    HarmonyOS应用开发-首选项与后台通知管理
    南大通用数据库-Gbase-8a-学习-20-GBaseGMonitor
  • 原文地址:https://blog.csdn.net/weixin_40874076/article/details/139810498