人们总喜欢”甜“,而忘记”先苦后甜“
一般在系统的注册、登录、反馈等页面需要验证用户个人身份信息,就需要用到【手机验证码】和【图片验证码】功能。
目录
一. 手机验证码功能
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即可交给后端处理。