微信小程序倒计时验证码弹框

效果图
在这里插入图片描述
目录结构
微信截图_20201230172254.png
关闭图标
close.png
dialog-vccode组件
dialog-vccode.js
Component({
  properties: {
    // 输入框的数量
    inputLength: {
      type: Number,
      value: 4
    },
    // 单个输入框的宽度
    inputWidth: {
      type: String,
      value: '58rpx'
    },
    inputHeight: {
      type: String,
      value: '58rpx'
    },
    // 是否显示输入的值,默认隐藏
    showValue: {
      type: Boolean,
      value: false
    },
    //提示文字
    tipText: {
      type: String,
      value: '请输入验证码'
    },
    //手机号码
    phone: {
      type: String,
      value: ''
    },
    //手机号码提示
    phoneTip: {
      type: String,
      value: '已发送到:'
    },
    //是否显示倒计时
    showMask:{
      type:Boolean,
      value:false
    },
    //倒计时
    countDown:{
      type:Number,
      value:5
    },
    //是否已发送验证码
    alreadySend:{
      type:Boolean,
      value:false
    }
  },
  data: {
    // input是否获取焦点
    inputFocus: true,
    // 初始input值为空
    currentValue: '',
    
  },
  methods: {
    //请稍后再试
    pleaseWait:function(){
      wx.showToast({
        title: '请稍后再试',
      })
    },
    //重新发送验证码
    reSendCode:function(){
      this.triggerEvent('reSendCode',{value:true})
    },
    closeMask: function () {
      this.setData({ showMask: false,currentValue:'' })
      this.triggerEvent('closeMask',{value:false},{bubbles:true,composed:true})
    },
    showView({ phone }) {
      var mPhone = phone.substr(0, 3) + '****' + phone.substr(7);
      this.setData({
        phone: mPhone,
      })
    },
    // 设置当前的值
    _setCurrentValue(e) {
      // 在此处判断满6(inputLength)位,把值返回给上级父组件或页面
      let currentValue = e.detail.value
      // 改变时,派发一个事件,如果父组件或页面中需要实时获取改变后的值,可以监听这个事件。
      this.triggerEvent('change', e.detail.value)

      this.setData({
        currentValue
      })
      if (currentValue.length >= this.data.inputLength) {
        this._complate()
        return
      }
    },
    // 点击伪装的input时,让隐藏的input获得焦点
    _focusInput() {
      this.setData({
        inputFocus: true
      })
      console.log(this.data.inputFocus)
    },
    _complate() {
      this.triggerEvent('inputComplate', this.data.currentValue)
    },
    // 提供给外部调用的方法,显示/隐藏密码。接收一个参数,可以显性修改展示的状态。
    toggleValue(state) {
      this.setData({
        showValue: state != undefined ? state : !this.data.showValue
      })
    },
    // 清除input当前的值
    clearCurrentValue() {
      this.setData({
        currentValue: ''
      })
    },
    //确认按钮
    tapConfirm() {
      let value = this.data.currentValue
      if (value.length < this.data.inputLength) {
        return
      }
      this.triggerEvent('confirm', value)
      this.closeMask()
    },

  },
  ready: function (e) {
    var phone = this.data.phone;
    this.setData({
      phone: phone.substr(0, 3) + '****' + phone.substr(7)
    })
  }
})
dialog-vccode.json
{
  "component": true
}
dialog-vccode.wxml
<view wx:if="{{showMask}}" class="mask">
  <view class="set-password">
    <view class="close_view_reverse" catchtap="closeMask">
       <view class="close_view"><image src='./image/close.png' style="height: 34rpx;width: 34rpx;"></image></view>
    </view>
    <view class="tip">{{tipText}}</view>
    <view class='verify-content'>{{phoneTip}}{{phone}}</view>
    <!-- 密码框 -->
    <view style="position: relative;top: -65rpx;">
      <view class="password-box">
        <view class='password-wrapper'>
          <!-- 伪装的input -->
          <block wx:for="{{inputLength}}" wx:key="item">
            <!-- 宽高可以由外部指定 -->
            <view class="password-item" style="width: {{inputWidth}}; height: {{inputHeight}}" catchtap='_focusInput'>
              <!-- 隐藏密码时显示的小圆点【自定义】 -->
              <!-- <view wx:if="{{!showValue && currentValue.length>=index+1}}" class="hidden"></view> -->
              <!-- 显示密码时显示对应的值 -->
              <view class="show">{{currentValue.length>=index+1?currentValue[index]:''}}</view>
            </view>
          </block>
        </view>
        <!-- 隐藏的输入框 -->
        <input type="text" value="{{currentValue}}" class='hidden-input' maxlength="{{inputLength}}" adjust-position="{{true}}"
          focus="{{inputFocus}}" catchinput="_setCurrentValue"></input>
      </view>

    </view>

    <view wx:if="{{alreadySend}}" class="text_countDown c_flex" bindtap="pleaseWait">重新发送({{countDown}}s)</view>
    <view wx:else class="text_countDown c_flex" bindtap='reSendCode'>重新发送</view>

    <!-- 分割线 -->
    <view class="divider_line"></view>
    <view  class="btn-next {{currentValue.length==4?'btn-next-active':''}}" bindtap="tapConfirm">确定</view>
  </view>
</view>
dialog-vccode.wxss
.password-box .password-wrapper {
  margin-top: 20rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}
.password-box .password-item {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
}
.password-box .password-item::after {
  display: block;
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  border: 1px solid #D8D8D8;
  box-sizing: border-box;
  width: 200%;
  height: 200%;
  border-radius: 4rpx;
  transform: scale(0.5);
  transform-origin: left top;
}
.password-box .password-item + .password-item {
  margin-left: 44rpx;
}
.password-box .password-wrapper .hidden {
  width: 14rpx;
  height: 14rpx;
  border-radius: 50%;
  background: #999;
}
.password-box .password-wrapper .show {
  color: #1a88f9;
}
.password-box .hidden-input {
  width: 1px;
  height: 0;
  min-height: 0;
}

.mask{
  position: fixed;
  top: 0;
  left: 0;
  display: flex;
  justify-content: center;
  background: rgba(0, 0, 0, 0.4);
  height: 100vh;
  width: 100vw;
  z-index: 1000;
}

.set-password {
  position: absolute;
  top:35vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 540rpx;
  padding-bottom: 20rpx;
  height: auto;
  background: #ffffff;
  border-radius: 10rpx;
}
.tip {
  position: relative;
  top: -80rpx;
  text-align: center;
  height: 43rpx;
  font-size: 32rpx;
  font-family: MicrosoftYaHeiSemibold;
  font-weight: 800;
  text-align: center;
  color: #333333;
  line-height: 43rpx;
}
.close_view_reverse{
  width:100%;
  display: flex;
  flex-direction: row-reverse;
}
.close_view{
  display: flex;
  flex-direction: row-reverse;
  height: 100rpx;
  width: 100rpx;
  padding:10rpx 10rpx 0 0;
}

.divider_line{
  position: relative;
  top:-20rpx;
  width: 540rpx;
  height: 1px;
  background-color: #f0f1f1;
}

.btn-next {
  background: #1a88f9;
  border-radius: 110rpx;
  box-shadow: 0px 4rpx 10rpx 0rpx rgba(0,0,0,0.1); 
  box-sizing: border-box;
  margin: 0 auto 0;
  width: 260rpx;
  height: 70rpx;
  line-height: 70rpx;
  text-align: center;
  color: #fff;
  background: #d8d8d8;
}
.btn-next-active {
  background: #1A88F9;
}

.verify-content{
  position: relative;
  top: -70rpx;
  text-align: center;
  height: 30rpx;
  font-size: 24rpx;
  color: #C5C5C5;
}
.text_countDown{
  position: relative;
  top: -50rpx;
  height: 30rpx;
  font-size: 22rpx;
  text-decoration: underline;
  text-align: center;
  color: #999999;
  line-height: 30rpx;
}
.c_flex{
  display: flex;
  align-items:center;
}
index中调用组件
index.wxml

<view class="c_flex_center">
  <button bindtap="getVCode">点击获取验证码</button>
</view>


<dialog-vccode id='dialogVCode' showMask="{{showMask}}" countDown="{{countDown}}" alreadySend="{{alreadySend}}" bind:confirm="verifyCodeConfirm"  bind:reSendCode='reSendCode' bind:closeMask="closeMask" />

index.json
{
  "usingComponents": {
    "dialog-vccode":"/component/dialog-vccode/dialog-vccode"
  }
}
index.js
//index.js
//获取应用实例
const app = getApp()

Page({
  data: {
    showMask: false,
    countDown: 60,//倒计时时间s
    alreadySend: false,//验证码是否发送
    clearInterval: false,//释放interval重置倒计时
    currentTimeStamp: 0,
    isDown: true,
  },
  onLoad: function () {

  },
  onShow: function () {
    this.dialogVCode = this.selectComponent('#dialogVCode')
  },
  //显示验证码提示框
  getVCode: function () {
    //防止快速点击
    let intervalTimeStamp=Date.now() - this.data.currentTimeStamp;
    if (this.data.currentTimeStamp&&intervalTimeStamp<=1000) {
      console.log('点击获取验证码按钮时当前时间戳', this.data.currentTimeStamp)
      console.log('点击间隔时间:', intervalTimeStamp+'毫秒')
      wx.showToast({
        title: '点击的太快了',
      })
      return
    } 
    this.setData({
      currentTimeStamp: Date.now()
    })
    console.log('点击获取验证码按钮时当前时间戳', this.data.currentTimeStamp)
    console.log('点击间隔时间:', intervalTimeStamp+'毫秒')
    //显示验证码框
    this.setData({
      showMask: true,
      countDown: 60,
      alreadySend: true,
      clearInterval: false
    })
    //开启倒计时
    this.timer()
    //传入手机号码
    this.dialogVCode.showView({ phone: '18855556666' })
  },
  //验证码倒计时
  timer: function () {
    if (!this.data.showMask) return
    let promise = new Promise((resolve, reject) => {
      let setTimer = setInterval(
        () => {
          this.setData({
            countDown: --this.data.countDown
          })
          if (this.data.countDown <= 0) {
            this.setData({
              countDown: 60,
              alreadySend: false
            })
            resolve(setTimer)
          } else {
            //关闭验证码框后重新点击按钮时,释放上一个正在执行的setInterval
            if (this.data.clearInterval) {
              resolve(setTimer)
            }
          }
        }, 1000)
    })
    promise.then((setTimer) => {
      clearInterval(setTimer)
    })
  },
  //重新发送
  reSendCode: function (e) {
    console.log('重新发送验证码', e.detail.value)
    wx.showToast({
      title: '验证码已发送',
    })
    this.setData({
      alreadySend: e.detail.value,
      countDown: 60
    })
    this.timer()
    //调用发送验证码接口

  },
  //自定义验证码弹框的确定
  verifyCodeConfirm(e) {
    console.log('验证码弹框的确定', e.detail)
    //确定后重新计时
    this.setData({
      countDown: 60,
      clearInterval: true,
      alreadySend: false
    })
  },
  //关闭验证码提示框
  closeMask: function (e) {
    console.log('验证码关闭按钮', e.detail.value)
    this.setData({
      showMask: e.detail.value,
      clearInterval: true
    })

  },
})

index.wxss
.c_flex_center{
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100vw;
    height: 100vh;
}
如果本篇文章能帮到您,那真是非常荣幸!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容