前端常见的【手机验证码】【图片验证码】功能实现-前后端配合实现

人们总喜欢”甜“,而忘记”先苦后甜“

一般在系统的注册、登录、反馈等页面需要验证用户个人身份信息,就需要用到【手机验证码】和【图片验证码】功能。

目录

一. 手机验证码功能 

1. 实现思路

2. 后端接口

3. 前端实现

二、图片验证码功能

1. 实现思路

2. 后端接口

3. 前端实现


一. 手机验证码功能 

1. 实现思路

页面需要实现找回密码功能,点击”获取验证码“,调用后端提供的接口,在60秒内提交找回密码请求,将数据表单和验证码发送给后端,后端校验是否为最新的验证码后返回接口调用成功信息。

2. 后端接口

3. 前端实现

(1)页面布局

<div className={styles.psw} style={
   
   { height: '540px' }}>
          <div className={styles.pswTitle}>找回密码</div>
          <div className={styles.line1}></div>
          <div className={styles.line2}></div>
          <div>
            <Form layout="inline">
              <div className={styles.inputW}>
                <div>
                  <div className={styles.iconPosition}>
                    <img
                      src={usericon}
                      alt=""
                      style={
   
   { width: '15px', height: '20px' }}
                    />
                    <span className={styles.inputText}>
                      <span style={
   
   { color: 'red' }}>*</span>用户名
                    </span>
                  </div>
                  <FormItem>
                    {getFieldDecorator('userName', {
                      rules: [
                        {
                          required: true,
                          message: '请输入用户名'
                        }
                      ]
                    })(<Input placeholder="请输入用户名" />)}
                  </FormItem>
                </div>
              </div>
              <div className={styles.inputW}>
                <div>
                  <div className={styles.iconPosition}>
                    <img
                      src={phoneIcon}
                      alt=""
                      style={
   
   { width: '15px', height: '20px' }}
                    />
                    <span className={styles.inputText}>
                      <span style={
   
   { color: 'red' }}>*</span>手机号
                    </span>
                  </div>
                  <FormItem>
                    {getFieldDecorator('mobile', {
                      rules: [
                        {
                          required: true,
                          pattern: /^1[34578]\d{9}$/,
                          message: '请输入正确的手机号码'
                        }
                      ]
                    })(<Input placeholder="请输入手机号" />)}
                  </FormItem>
                </div>
              </div>

              <div className={styles.inputW}>
                <div>
                  <div className={styles.iconPosition}>
                    <img
                      src={passwordIcon}
                      alt=""
                      style={
   
   { width: '15px', height: '20px' }}
                    />
                    <span
                      className={styles.inputText}
                      style={
   
   { width: '70px' }}
                    >
                      <span style={
   
   { color: 'red' }}>*</span>设置密码
                    </span>
                  </div>
                  <FormItem>
                    {getFieldDecorator('password', {
                      rules: [
                        {
                          required: true,
                          message: '请输入6-16位字符和数字组合,字符区分大小写',
                          // eslint-disable-next-line
                          pattern: /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$/
                        },
                        { validator: validateToNextPassword }
                      ]
                    })(<Input.Password placeholder="请输入密码" />)}
                  </FormItem>
                </div>
              </div>
              <div className={styles.inputW}>
                <div>
                  <div className={styles.iconPosition}>
                    <img
                      src={passwordIcon}
                      alt=""
                      style={
   
   { width: '15px', height: '20px' }}
                    />
                    <span
                      className={styles.inputText}
                      style={
   
   { width: '70px' }}
                    >
                      <span style={
   
   { color: 'red' }}>*</span>确认密码
                    </span>
                  </div>
                  <FormItem>
                    {getFieldDecorator('comfirm', {
                      rules: [
                        { required: true, message: '请输入确认密码' },
                        { validator: compareToFirstPassword }
                      ]
                    })(
                      <Input.Password
                        onBlur={handleConfirmBlur}
                        placeholder="请再次输入密码"
                      />
                    )}
                  </FormItem>
                </div>
              </div>
              <div className={styles.inputW1} style={
   
   { position: 'relative' }}>
                <div>
                  <div className={styles.iconPosition}>
                    <img
                      src={check}
                      alt=""
                      style={
   
   { width: '15px', height: '20px' }}
                    />
                    <span
                      className={styles.inputText}
                      style={
   
   { width: '70px' }}
                    >
                      <span style={
   
   { color: 'red' }}>*</span>短信验证
                    </span>
                  </div>
                  <YZMButton
                    onClick={onclick}
                    time={time}
                    className={styles.yzmc}
                  />
                  <FormItem>
                    {getFieldDecorator('smsCode', {
                      rules: [
                        {
                          required: true,
                          message: '请输入'
                        }
                      ]
                    })(<Input placeholder="请输入验证码" />)}
                  </FormItem>
                </div>
              </div>

              <div className={styles.button} style={
   
   { margin: '30px 50px' }}>
                <Button
                  onClick={() => {
                    window.history.back()
                  }}
                  className={styles.backButton}
                >
                  返回
                </Button>
                <Button onClick={handlOnsave} className={styles.findButton}>
                  找回密码
                </Button>
              </div>
            </Form>
          </div>
        </div>

(2)使用验证码组件YZMButton

import React, { useCallback, useState, useRef } from 'react'
import styles from './index.less'
import classnames from 'classnames'
import { Button } from 'antd'
interface YZMButtonProps {
  onClick: (e: any) => any
  time: number
  text?: string
  className: any
}

export default function YZMButton({
  onClick,
  time,
  text,
  className
}: YZMButtonProps) {
  const [yzime, setYzime] = useState(time)
  const _yzime = useRef(yzime)
  _yzime.current = yzime
  const flag = yzime === time
  const handleClick = useCallback(
    (e: any) => {
      if (flag) {
        if (!onClick(e)) {
          return
        }
        let timer = setInterval(() => {
          if (_yzime.current === 0) {
            clearInterval(timer)
            setYzime(time)
          } else {
            setYzime(value => value - 1)
          }
        }, 1000)
      }
    },
    [flag, onClick, time]
  )

  return (
    <Button
      className={classnames(styles.content, className)}
      onClick={handleClick}
      disabled={!flag}
    >
      {flag ? text || '获取验证码' : `还剩${yzime}s`}
    </Button>
  )
}

点击按钮之后,按钮变成不可用状态,与此同时60秒倒计时开始。

(3)调接口获取验证码

在click方法中,调用后端提供的方法获取短信验证码

const onclick = useCallback(() => {
    let callbackRes = true
    validateFields(['mobile'], (err: any, fieldsValue: any) => {
      if (err) {
        callbackRes = false
      }
      let { mobile } = fieldsValue
      let res = {
        mobile: mobile,
        type: 'forget-password'
      }
      dispatch({
        type: 'login/getyzm',
        params: [res]
      }).then((v: any) => {
        if (v.code === -1) {
          message.error(v.message)
          callbackRes = false
        } else if (v.code === 500) {
          message.error(v.message)
          callbackRes = false
        } else {
          callbackRes = true
        }
      })
    })
    return callbackRes
  }, [dispatch, validateFields])

 (4)提交表单 找回密码

  const handlOnsave = useCallback(() => {
    validateFields((err: any, fieldsValue: any) => {
      if (err) return
      let res = {
        userName: fieldsValue.userName,
        mobile: fieldsValue.mobile,
        // password: btoa(fieldsValue.password),
        newPassword: fieldsValue.password,
        code: fieldsValue.smsCode
      }
      dispatch({
        type: 'login/getback',
        params: [res]
      }).then((v: any) => {
        if (v.code === 200) {
          message.info(v.message)
          window.location.href = '/#/login/password'
        } else {
          message.info(v.message)
        }
      })
    })
  }, [dispatch, validateFields])

二、图片验证码功能

1. 实现思路

 进入页面,前端向后端发送请求(需要UUID),获取一张二维码,之后提交表单的同时提交uuid和验证码,后端请求验证。后端使用到阿里的获取短信验证码的服务。

2. 后端接口

后端需要拿到uuid借助阿里的生成图片验证码的工具进行生成,并将图片验证码返回给前端。最后,需要借助前端提交的uuid和验证码信息进行比对,即可实现提交功能。

3. 前端实现

(1)使用uuid

UUID 是 通用唯一识别码.

安装依赖:npm install uuid

引入依赖:import { v4 as uuidv4 } from "uuid

生成uuid使用方法:const id =uuidv4()
console.log(id)
e6a2f201-f720-4e98-8fea-7f473101acd3

(2)进入页面获取验证码图片

  const getCode = useCallback(() => {
    let id = uuidv4()
    setUuid(id)
    let result = dispatch({
      type: 'global/get1',
      params: ['/uaa/captcha.jpg', { uuid: id }]
    })
    console.log(result)
    return result
  }, [dispatch, setUuid])

  useEffect(() => {
    getCode().then((v: any) => {
      let a = window.URL.createObjectURL(v)
      setCode(a)
    })
  }, [getCode])

生成图片的请求需要向后端提供一个uuid.

注意 这里的请求就是/uaa/captcha.jpg  你没有看错。

(3)处理后端返回的图片并显示

请求之后后端返回的是一张图片,注意是一张图片,而不是图片的url.

可见在这个页面中。上传附件的请求,返回的是JSON数据

但是对于请求图片验证码的请求,返回的是图片格式。

 

 因此需要使用get1方法进行处理。


找到commonModal文件。可以看到里面封装使用了get 和 post方法。

get: async (
      {
        type,
        params: [url, remoteValue, options = {}]
      }: { type: string; params: [string, object, object | string] },
      { dispatch, getState }
    ) => {
      const { back = false, message: messageFlag = false } = options as any
      const res = await remote(url, remoteValue, 'get')
      const { success, message: _messagae } = res
      if (!success) {
        return
      }
      if (messageFlag) {
        message.success(_messagae || '操作成功!')
      }
      if (back) {
        history.go(-1)
      }
      return res
    },
get1: async (
      {
        type,
        params: [url, remoteValue, options = {}]
      }: { type: string; params: [string, object, object | string] },
      { dispatch, getState }
    ) => {
      const res = await remote(url, remoteValue, 'get1')
      return res
    },

并且需要使用let a = window.URL.createObjectURL(v)创建一个新的URL对象,并将其设置,并最终绑定在图片img的src中

 

 至此图片验证码就显示出来了。

 (4)提交操作

//提交反馈
  const submit = useCallback(
    (data: any) => {
      validateFields((err: any, fieldsValue: any) => {
        if (err) return
        let data = {
          ...fieldsValue,
          uuid: uuid,
          fileId: file[0] && file[0].id
        }
        debugger
        let result = dispatch({
          type: 'global/post',
          params: ['/api/problemFeedback/create', { data }]
        })
        return result
      }).then((v: any) => {
        if (v.code === 200) {
          //setDetailData(v.data)
          message.success(v.message)
          toggleForm()
        }
      })
    },
    [dispatch, file, toggleForm, uuid, validateFields]
  )

 提交功能平平无奇,注意加上刚才生成的唯一uuid即可交给后端处理。

猜你喜欢

转载自blog.csdn.net/Sabrina_cc/article/details/121098784