You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
5.2 KiB

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