我们在项目开发过程中经常会遇到用户发送验证码后唤起图片滑块验证,验证成功后触发发送验证码事件(不处理发送验证码逻辑父组件去监听)进而读秒等流程,特此封装了该组件。

1、html部分

<template><div><div v-if="countDownNum < 1" @click="_sendCode" slot="suffix" class="modifyColor":class=" _disabled ? 'disabled' : 'hover7'">发送验证码</div><div v-else class="mt6 modifyColor">{{ countDownNum }}S</div><div :id="cacheKey"></div></div>
</template>

2、css部分

.modifyColor {color: #4C70DF;padding-right:15px;
}.mt6{margin-top: 6px;
}

3、js部分

(1)将以下内容放在public文件夹中取名为yidun.js

(function (global, factory) {'use strict'if (typeof module === 'object' && typeof module.exports === 'object') {module.exports = global.document ? factory(global) : function (w) {if (!w.document) {throw new Error('initNECaptchaWithFallback requires a window with a document')}return factory(w)}} else if (typeof define === 'function' && define.amd) {define('initNECaptchaWithFallback', [], function () {return factory(global)})} else {global.initNECaptchaWithFallback = factory(global)}
}(typeof window !== 'undefined' ? window : this, function (window) {'use strict'var errorCallbackCount = 0// 常量var DEFAULT_VALIDATE = 'QjGAuvoHrcpuxlbw7cp4WnIbbjzG4rtSlpc7EDovNHQS._ujzPZpeCInSxIT4WunuDDh8dRZYF2GbBGWyHlC6q5uEi9x-TXT9j7J705vSsBXyTar7aqFYyUltKYJ7f4Y2TXm_1Mn6HFkb4M7URQ_rWtpxQ5D6hCgNJYC0HpRE7.2sttqYKLoi7yP1KHzK-PptdHHkVwb77cwS2EJW7Mj_PsOtnPBubTmTZLpnRECJR99dWTVC11xYG0sx8dJNLUxUFxEyzTfX4nSmQz_T5sXATRKHtVAz7nmV0De5unmflfAlUwMGKlCT1khBtewlgN5nHvyxeD8Z1_fPVzi9oznl-sbegj6lKfCWezmLcwft8.4yaVh6SlzXJq-FnSK.euq9OBd5jYc82ge2_hEca1fGU--SkPRzgwkzew4O4qjdS2utdPwFONnhKAIMJRPUmCV4lPHG1OeRDvyNV8sCnuFMw7leasxIhPoycl4pm5bNy70Z1laozEGJgItVNr3' // 默认validatevar FALLBACK_LANG = {'zh-CN': '前方拥堵,已自动跳过验证','en': 'captcha error,Verified automatically'}var CACHE_MIN = 1000 * 60 // 缓存时长单位,1分钟var REQUEST_SCRIPT_ERROR = 502var RESOURCE_CACHE = {}// 工具函数function loadScript(src, cb) {var head = document.head || document.getElementsByTagName('head')[0]var script = document.createElement('script')cb = cb || function () {}script.type = 'text/javascript'script.charset = 'utf8'script.async = truescript.src = srcif (!('onload' in script)) {script.onreadystatechange = function () {if (this.readyState !== 'complete' && this.readyState !== 'loaded') returnthis.onreadystatechange = nullcb(null, script) // there is no way to catch loading errors in IE8}}script.onload = function () {this.onerror = this.onload = nullcb(null, script)}script.onerror = function () {// because even IE9 works not like othersthis.onerror = this.onload = nullcb(new Error('Failed to load ' + this.src), script)}head.appendChild(script)}function joinUrl(protocol, host, path) {protocol = protocol || ''host = host || ''path = path || ''if (protocol) {protocol = protocol.replace(/:?\/{0,2}$/, '://')}if (host) {var matched = host.match(/^([-0-9a-zA-Z.:]*)(\/.*)?/)host = matched[1]path = (matched[2] || '') + '/' + path}!host && (protocol = '')return protocol + host + path}function setDomText(el, value) {if (value === undefined) returnvar nodeType = el.nodeTypeif (nodeType === 1 || nodeType === 11 || nodeType === 9) {if (typeof el.textContent === 'string') {el.textContent = value} else {el.innerText = value}}}function queryAllByClassName(selector, node) {node = node || documentif (node.querySelectorAll) {return node.querySelectorAll(selector)}if (!/^\.[^.]+$/.test(selector)) return []if (node.getElementsByClassName) {return node.getElementsByClassName(selector)}var children = node.getElementsByTagName('*')var currentvar result = []var className = selector.slice(1)for (var i = 0, l = children.length; i < l; i++) {current = children[i]if (~(' ' + current.className + ' ').indexOf(' ' + className + ' ')) {result.push(current)}}return result}function assert(condition, msg) {if (!condition) throw new Error('[NECaptcha] ' + msg)}function isInteger(val) {if (Number.isInteger) {return Number.isInteger(val)}return typeof val === 'number' && isFinite(val) && Math.floor(val) === val}function isArray(val) {if (Array.isArray) return Array.isArray(val)return Object.prototype.toString.call(val) === '[object Array]'}function ObjectAssign() {if (Object.assign) {return Object.assign.apply(null, arguments)}var target = {}for (var index = 1; index < arguments.length; index++) {var source = arguments[index]if (source != null) {for (var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {target[key] = source[key]}}}}return target}function getTimestamp(msec) {msec = !msec && msec !== 0 ? msec : 1return parseInt((new Date()).valueOf() / msec, 10)}// 降级方案function normalizeFallbackConfig(customConfig) {var siteProtocol = window.location.protocol.replace(':', '')var defaultConf = {protocol: siteProtocol === 'http' ? 'http' : 'https',lang: 'zh-CN',errorFallbackCount: 3}var config = ObjectAssign({}, defaultConf, customConfig)var errorFallbackCount = config.errorFallbackCountassert(errorFallbackCount === undefined || (isInteger(errorFallbackCount) && errorFallbackCount >= 1),'errorFallbackCount must be an integer, and it\'s value greater than or equal one')return config}function loadResource(config, cb) {if (window.initNECaptcha) return cb(null)function genUrl(server) {var path = 'load.min.js'var urls = []if (isArray(server)) {for (var i = 0, len = server.length; i < len; i++) {urls.push(joinUrl(config.protocol, server[i], path))}} else {var url = joinUrl(config.protocol, server, path)urls = [url, url]}return urls}var urls = genUrl(config.staticServer || ['cstaticdun.126.net', 'cstaticdun1.126.net', 'cstatic.dun.163yun.com'])function step(i) {var url = urls[i] + '?v=' + getTimestamp(CACHE_MIN)loadScript(url, function (err) {if (err || !window.initNECaptcha) { // loadjs的全局变量i = i + 1if (i === urls.length) {return cb(new Error('Failed to load script(' + url + ').' + (err ? err.message : 'unreliable script')))}return step(i)}return cb(null)})}step(0)}/** entry: initNECaptchaWithFallback* options: *  errorFallbackCount: 触发降级的错误次数,默认第三次错误降级*  defaultFallback: 是否开启默认降级*  onFallback: 自定义降级方案,参数为默认validate*/function initNECaptchaWithFallback(options, onload, onerror) {var captchaIns = nullvar config = normalizeFallbackConfig(options)var defaultFallback = config.defaultFallback !== falsevar langPkg = FALLBACK_LANG[config.lang === 'zh-CN' ? config.lang : 'en']var storeKey = window.location.pathname + '_' + config.captchaId + '_NECAPTCHA_ERROR_COUNTS'try {errorCallbackCount = parseInt(localStorage.getItem(storeKey) || 0, 10)} catch (error) {}var fallbackFn = !defaultFallback ? config.onFallback || function () {} : function (validate) {function setFallbackTip(instance) {if (!instance) returnsetFallbackTip(instance._captchaIns)if (!instance.$el) returnvar tipEles = queryAllByClassName('.yidun-fallback__tip', instance.$el)if (!tipEles.length) return// 确保在队列的最后setTimeout(function () {for (var i = 0, l = tipEles.length; i < l; i++) {setDomText(tipEles[i], langPkg)}}, 0)}setFallbackTip(captchaIns)config.onVerify && config.onVerify(null, {validate: validate})}var noFallback = !defaultFallback && !config.onFallbackvar proxyOnError = function (error) {errorCallbackCount++if (errorCallbackCount < config.errorFallbackCount) {try {localStorage.setItem(storeKey, errorCallbackCount)} catch (err) {}onerror(error)} else {fallbackFn(DEFAULT_VALIDATE)proxyRefresh()noFallback && onerror(error)}}var proxyRefresh = function () {errorCallbackCount = 0try {localStorage.setItem(storeKey, 0)} catch (err) {}}var triggerInitError = function (error) {if (initialTimer && initialTimer.isError()) {initialTimer.resetError()return}initialTimer && initialTimer.resetTimer()noFallback ? onerror(error) : proxyOnError(error)}config.onError = function (error) {if (initialTimer && initialTimer.isError()) {initialTimer.resetError()}proxyOnError(error)}config.onDidRefresh = function () {if (initialTimer && initialTimer.isError()) {initialTimer.resetError()}proxyRefresh()}var initialTimer = options.initTimeoutError ? options.initTimeoutError(proxyOnError) : null // initialTimer is only for mobile.htmlvar loadResolve = function () {window.initNECaptcha(config, function (instance) {if (initialTimer && initialTimer.isError()) returninitialTimer && initialTimer.resetTimer()captchaIns = instanceonload && onload(instance)}, triggerInitError)}var cacheId = 'load-queue'if (!RESOURCE_CACHE[cacheId]) {RESOURCE_CACHE[cacheId] = {rejects: [],resolves: [],status: 'error'}}if (RESOURCE_CACHE[cacheId].status === 'error') {RESOURCE_CACHE[cacheId].status = 'pending'loadResource(config, function (error) {if (error) {var err = new Error()err.code = REQUEST_SCRIPT_ERRORerr.message = config.staticServer + '/load.min.js error'var rejects = RESOURCE_CACHE[cacheId].rejectsfor (var i = 0, iLen = rejects.length; i < iLen; i++) {rejects.pop()(err)}RESOURCE_CACHE[cacheId].status = 'error'} else {RESOURCE_CACHE[cacheId].status = 'done'var resolves = RESOURCE_CACHE[cacheId].resolvesfor (var j = 0, jLen = resolves.length; j < jLen; j++) {resolves.pop()()}}})} else if (RESOURCE_CACHE[cacheId].status === 'done') {loadResolve()}if (RESOURCE_CACHE[cacheId].status === 'pending') {RESOURCE_CACHE[cacheId].rejects.push(function loadReject(err) {triggerInitError(err)})RESOURCE_CACHE[cacheId].resolves.push(loadResolve)}}return initNECaptchaWithFallback
}))

(2)在index.html入口文件中引入yidun.js

<script src="<%= BASE_URL %>js/yidun.js"></script>

(3)组件js部分

<script lang="ts">
import {Component, Prop, Vue, Watch} from "vue-property-decorator";
import {getStorage, setStorage} from "@/modules/st-core/js_sdk/util/local-storage";
import apiConfig from "@/serve/api.config";
declare const initNECaptchaWithFallback:any@Component({components: {}
})
export default class GetVerificationCode extends Vue {//是否禁用@Prop({type : Boolean ,default:true}) disabled;//缓存的key@Prop({type : String ,default:"sendTime"}) cacheKey;@Prop({type : String ,default:"mt6"}) marginTop;@Prop({type : String ,default:"fz14"}) fontSize;private countDownNum: number = 0 //倒计时private timer: any = null  //计时器private captchaIns:any = nullprivate locale: any = getStorage("locale","en") ;created(){this._startTimer()}async mounted(){await this.$nextTick()await this._initNECaptcha()}@Watch('$i18n.locale')localeData(value){this.locale = value}//是否禁用get _disabled(){return this.disabled || this.countDownNum > 0 || !this.captchaIns ;}//读秒_countdown() {let total = 60;let sendTime = getStorage(this.cacheKey, 0);let now: any = Date.now();let mixSeconds: any = Math.ceil((now - sendTime) / 1000); //已过去了多少sthis.countDownNum = total - mixSeconds;if (this.countDownNum <= 0) {clearInterval(this.timer);}return this._countdown;}//验证码发送成功后调用该方法onSendSuccess() {setStorage(this.cacheKey, Date.now());this._startTimer();}_startTimer() {clearInterval(this.timer);this.timer = setInterval(this._countdown(), 1000);}//发送验证码_sendCode() {if (this._disabled) return;this.captchaIns.popUp();}_initNECaptcha(){return new Promise((resolve,reject)=>{// 若使用降级方案引用初始化js// initNECaptcha替换成initNECaptchaWithFallbackinitNECaptchaWithFallback({element: `#${this.cacheKey}`,captchaId: apiConfig.domainList.codes,mode:'popup',lang:this.locale == 'en' ? 'en' : 'zh-CN',width: '320px',onVerify: (err: any, ret: any) => {// 用户验证码验证成功后,进行实际的提交行为if (err) return console.log(err)let validate = ret.validate;this.$emit("send" , {validate});this._initNECaptcha()}}, (instance) => {this.captchaIns = instance ;resolve(instance);}, (err) => {// 初始化失败后触发该函数,err对象描述当前错误信息console.log(err)reject(err);})})}
}
</script>

总结:

1、图片校验全局函数initNECaptchaWithFallback()接收三个函数:配置项、初始化成功、初始失败,其中配置项中的captchaId要与服务端协商一致。图片校验onVerify失败后组件内部会重新唤起校验,校验成功后拿到一个字符串,触发send事件将这个字符串传给服务端校验,这个send事件在父级组件用来监听发送验证码。

2、概括为组件外部的其他不可发送验证码的情况,比如手机号格式错误等,可以传入disabled属性来控制,或者正在读秒,或者图片校验初始化失败。

//是否禁用get _disabled(){return this.disabled || this.countDownNum > 0 || !this.captchaIns ;}

3、组件内部提供onSendSuccess()方法方法,组件外部在验证码发送成功时调用该方法,该方法真正的作用是读秒。

4、读秒逻辑:
(1)验证码发送成功后将当前的时间存在缓存中

 onSendSuccess() {setStorage(this.cacheKey, Date.now());this._startTimer();}

(2)读秒逻辑:总时长为60秒,现在的时间减去从缓存中读取出来的发送时间就是过去了多少秒,总时长减去过去了多少秒就是剩余的时间,如果剩余的时间小于等于0的时候我们要把定时器清理掉。

   _countdown() {let total = 60;let sendTime = getStorage(this.cacheKey, 0);let now: any = Date.now();let mixSeconds: any = Math.ceil((now - sendTime) / 1000); //已过去了多少sthis.countDownNum = total - mixSeconds;if (this.countDownNum <= 0) {clearInterval(this.timer);}return this._countdown;}_startTimer() {clearInterval(this.timer);this.timer = setInterval(this._countdown(), 1000);}

(3)用户刷新页面时继续读秒处理

 created(){this._startTimer()
}

vue封装图片滑块验证+读秒组件相关推荐

  1. Vue实现图片滑动验证

    Vue实现图片滑动验证 使用vue完成 使用vue完成 实习一个星期小白对工作安排的图片滑动验证分享 1.安装依赖 附上:https://www.npmjs.com/package/vue-monop ...

  2. html滑动验证图片,js实现移动端图片滑块验证功能

    之前写过一篇uniapp框架的滑块验证,今天抽个空用原生js实现这个功能,pc端暂时不做,因为pc端只要把touch事件改成mouse事件就能实现,这里就不再重复写了. (最新完美版本,js+canv ...

  3. css3 滑动验证,Vue 实现拖动滑块验证功能(只有css+js没有后台验证步骤)

    vue验证滑块功能,在生活中很多地方都可以见到,那么使用起来非常方便,基于vue如何实现滑块验证呢?下面通过代码给大家讲解. 效果图如下所示: 拖动前 拖动后 代码引用的css与js都是线上的 将代码 ...

  4. vue实现图片滑动验证功能——功能实现

    图片滑动验证,是目前比较常见的验证方式,主要目的是防止用户利用机器人自动注册.登录.灌水. 目前vue技术日趋成熟,已经有专门针对图片滑动验证功能的插件了.具体使用方式如下: 1.安装插件--npm ...

  5. vue 封装图片预览组件

    因项目需要,自己封装了个vue图片预览组件 <template><div class="imgs_previews animated" @mousewheel.p ...

  6. Vue封装树形菜单组件

    csdn终于更新完成了哈,ok,步入正题 像这种树形结构的核心思想就是:递归思想,知道使用递归,其实这个例子函数的封装也就很简单喽 实现步骤: 设置的props:                    ...

  7. OpenCV(python版)识别滑块验证中的缺口

    前言 验证码往往是爬虫路上的一只拦路虎,而其花样也是层出不穷:图片验证.滑块验证.交互式验证.行为验证等.随着OCR技术的成熟,图片验证已经渐渐淡出主流,而「滑块验证」越来越多地出现在大众视野.&qu ...

  8. Python OpenCV 图片滑块验证码自动识别方案分析与自动化识别方案实现 图片相似度对比 OpenCV如何找到图片中的正方形并进行标记?

    目录 前言 一.图片滑块验证功能分析 1.图片滑块验证功能概述

  9. Python OpenCV 图片滑块验证码 滑块图片验证码 快速自动识别方案 代码简单 模板匹配识别 识别成功率达90%+

    前言 通过上一篇的文章大家已经对图片滑块验证码已经有了初步的了解,图片滑块验证码的核心关键在于图片识别接下来接入讲解.因为初版滑块图片识别虽然能识别验证码,通过一些策略调整也相对提高了一些图片识别率, ...

最新文章

  1. 机器学习、超参数、最优超参数、网格搜索、随机搜索、贝叶斯优化、Google Vizier、Adviser
  2. 设计模式复习-享元模式
  3. Linux PHP增加JSON支持及如何使用JSON
  4. python脚本创建拓扑_实验 1:Mininet --拓扑的命令脚本生成
  5. 分布式块存储QoS限速算法介绍与实践以及对上层应用的影响
  6. 软件测试基础课程学习笔记1--软件测试简介
  7. 为什么 GitHub 上的开发者比 iOS 上的要更值钱?
  8. 数值分析(7)-正交多项式
  9. 前端的小玩意(9.4)——做一个仿360工具箱的web页面(自动生成所有图标,对图标添加响应逻辑)
  10. 河北对口升学计算机VB知识点,vb对口升学试题.docx
  11. 光电传感器实验(红外对管/红外传感器实验)
  12. u盘win7纯净版_教你安装纯净版windows系统
  13. 论文阅读笔记《Optimal Image-Based Guidance of Mobile Manipulators Using Direct Visual Servoing》
  14. 使用RTT代替UART,把你的JLink变成串口调试助手~
  15. Java基础18-String类【String类的特点对象个数常用方法】【超详细讲解】
  16. 多表查询时,执行速度耗时太多
  17. 经常说这些话的人,不是情商高,而是城府深
  18. MySQL解决-Error:Your password does not satisfy the current policy requirements
  19. 洛基开放文化实验室---荐书”项目首批TOP5,
  20. ElasticSearch保姆级入门教程

热门文章

  1. Wide Deep Learning for Recommender Systems【论文记录】
  2. Counting Sheep_SAFIA
  3. python江红书后第六章实验答案_C#NET程序设计教程实验指导(清华大学江红,余青松)实验源码第六章...
  4. 东南大学计算机复试被刷概率,二战东大复试被刷恼羞成怒东大有内幕,今为黑东大之行为而后悔不已...
  5. i510400和i512400差距
  6. SPRING BOOT之三-Tests
  7. 这是一个谷歌抄腾讯的时代
  8. python能代替cad吗_免费且强大的CAD软件_DraftSight V1R5.1 可直接用来代替autoCAD
  9. 2019年清华大学827电路原理考研经验分享
  10. java 银行支付接口_cbcPayment 建设银行Java支付接口详细说明文档dsdsdds - 下载 - 搜珍网...