u-code.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. <template>
  2. <view class="u-code">
  3. <!-- 此组件功能由js完成,无需写html逻辑 -->
  4. </view>
  5. </template>
  6. <script>
  7. import props from './props.js';
  8. import mixin from '../../libs/mixin/mixin'
  9. import mpMixin from '../../libs/mixin/mpMixin';
  10. /**
  11. * Code 验证码输入框
  12. * @description 考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件 不提供界面显示,只提供提示语,由用户将提示语嵌入到具体的场景
  13. * @tutorial https://uview.d3u.cn/components/code.html
  14. * @property {String | Number} seconds 倒计时所需的秒数(默认 60 )
  15. * @property {String} startText 开始前的提示语,见官网说明(默认 '获取验证码' )
  16. * @property {String} changeText 倒计时期间的提示语,必须带有字母"x",见官网说明(默认 'X秒重新获取' )
  17. * @property {String} endText 倒计结束的提示语,见官网说明(默认 '重新获取' )
  18. * @property {Boolean} keepRunning 是否在H5刷新或各端返回再进入时继续倒计时( 默认false )
  19. * @property {String} uniqueKey 为了区分多个页面,或者一个页面多个倒计时组件本地存储的继续倒计时变了
  20. *
  21. * @event {Function} change 倒计时期间,每秒触发一次
  22. * @event {Function} start 开始倒计时触发
  23. * @event {Function} end 结束倒计时触发
  24. * @example <u-code ref="uCode" @change="codeChange" seconds="20"></u-code>
  25. */
  26. export default {
  27. name: "u-code",
  28. mixins: [mpMixin, mixin, props],
  29. data() {
  30. return {
  31. secNum: this.seconds,
  32. timer: null,
  33. canGetCode: true, // 是否可以执行验证码操作
  34. }
  35. },
  36. mounted() {
  37. this.checkKeepRunning()
  38. },
  39. watch: {
  40. seconds: {
  41. immediate: true,
  42. handler(n) {
  43. this.secNum = n
  44. }
  45. }
  46. },
  47. // #ifdef VUE3
  48. emits: ["change", "start", "end"],
  49. // #endif
  50. methods: {
  51. checkKeepRunning() {
  52. // 获取上一次退出页面(H5还包括刷新)时的时间戳,如果没有上次的保存,此值可能为空
  53. let lastTimestamp = Number(uni.getStorageSync(this.uniqueKey + '_$uCountDownTimestamp'))
  54. if(!lastTimestamp) return this.changeEvent(this.startText)
  55. // 当前秒的时间戳
  56. let nowTimestamp = Math.floor((+ new Date()) / 1000)
  57. // 判断当前的时间戳,是否小于上一次的本该按设定结束,却提前结束的时间戳
  58. if(this.keepRunning && lastTimestamp && lastTimestamp > nowTimestamp) {
  59. // 剩余尚未执行完的倒计秒数
  60. this.secNum = lastTimestamp - nowTimestamp
  61. // 清除本地保存的变量
  62. uni.removeStorageSync(this.uniqueKey + '_$uCountDownTimestamp')
  63. // 开始倒计时
  64. this.start()
  65. } else {
  66. // 如果不存在需要继续上一次的倒计时,执行正常的逻辑
  67. this.changeEvent(this.startText)
  68. }
  69. },
  70. // 开始倒计时
  71. start() {
  72. // 防止快速点击获取验证码的按钮而导致内部产生多个定时器导致混乱
  73. if(this.timer) {
  74. clearInterval(this.timer)
  75. this.timer = null
  76. }
  77. this.$emit('start')
  78. this.canGetCode = false
  79. // 这里放这句,是为了一开始时就提示,否则要等setInterval的1秒后才会有提示
  80. this.changeEvent(this.changeText.replace(/x|X/, this.secNum))
  81. this.timer = setInterval(() => {
  82. if (--this.secNum) {
  83. // 用当前倒计时的秒数替换提示字符串中的"x"字母
  84. this.changeEvent(this.changeText.replace(/x|X/, this.secNum))
  85. } else {
  86. clearInterval(this.timer)
  87. this.timer = null
  88. this.changeEvent(this.endText)
  89. this.secNum = this.seconds
  90. this.$emit('end')
  91. this.canGetCode = true
  92. }
  93. }, 1000)
  94. this.setTimeToStorage()
  95. },
  96. // 重置,可以让用户再次获取验证码
  97. reset() {
  98. this.canGetCode = true
  99. clearInterval(this.timer)
  100. this.secNum = this.seconds
  101. this.changeEvent(this.endText)
  102. },
  103. changeEvent(text) {
  104. this.$emit('change', text)
  105. },
  106. // 保存时间戳,为了防止倒计时尚未结束,H5刷新或者各端的右上角返回上一页再进来
  107. setTimeToStorage() {
  108. if(!this.keepRunning || !this.timer) return
  109. // 记录当前的时间戳,为了下次进入页面,如果还在倒计时内的话,继续倒计时
  110. // 倒计时尚未结束,结果大于0;倒计时已经开始,就会小于初始值,如果等于初始值,说明没有开始倒计时,无需处理
  111. if(this.secNum > 0 && this.secNum < this.seconds) {
  112. // 获取当前时间戳(+ new Date()为特殊写法),除以1000变成秒,再去除小数部分
  113. let nowTimestamp = Math.floor((+ new Date()) / 1000)
  114. // 将本该结束时候的时间戳保存起来 => 当前时间戳 + 剩余的秒数
  115. uni.setStorage({
  116. key: this.uniqueKey + '_$uCountDownTimestamp',
  117. data: nowTimestamp + Number(this.secNum)
  118. })
  119. }
  120. }
  121. },
  122. // 组件销毁的时候,清除定时器,否则定时器会继续存在,系统不会自动清除
  123. // #ifdef VUE2
  124. beforeDestroy() {
  125. this.setTimeToStorage()
  126. clearTimeout(this.timer)
  127. this.timer = null
  128. }
  129. // #endif
  130. // #ifdef VUE3
  131. beforeUnmount() {
  132. this.setTimeToStorage()
  133. clearTimeout(this.timer)
  134. this.timer = null
  135. }
  136. // #endif
  137. }
  138. </script>
  139. <style lang="scss" scoped>
  140. @import "../../libs/css/components.scss";
  141. </style>