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.

129 lines
5.0 KiB

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