index.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. <template>
  2. <div class="w100">
  3. <el-input text maxlength="4" placeholder="请输入验证码" autocomplete="off" v-bind="$attrs">
  4. <template #prefix>
  5. <el-icon class="el-input__icon"><ele-Message /></el-icon>
  6. </template>
  7. <template #suffix>
  8. <el-link type="primary" :underline="false" v-show="state.status !== 'countdown'" :disabled="state.status === 'countdown'" @click="onClick">{{
  9. state.status === 'ready' ? state.startText : state.endText
  10. }}</el-link>
  11. <el-countdown
  12. v-show="state.status === 'countdown'"
  13. :format="state.changeText"
  14. :value="state.countdown"
  15. value-style="font-size:var(--el-font-size-base);color:var(--el-color-primary)"
  16. @change="onChange"
  17. />
  18. </template>
  19. </el-input>
  20. <el-dialog ref="dialogRef" v-model="state.showDialog" class="my-captcha" title="请完成安全验证" draggable append-to-body width="380px">
  21. <MySlideCaptcha
  22. ref="slideCaptchaRef"
  23. :fail-tip="state.failTip"
  24. :success-tip="state.successTip"
  25. width="100%"
  26. height="auto"
  27. @refresh="onRefresh"
  28. @finish="onFinish"
  29. />
  30. </el-dialog>
  31. </div>
  32. </template>
  33. <script lang="ts" setup name="my-input-code">
  34. import { ref, reactive, defineAsyncComponent } from 'vue'
  35. import { CaptchaApi } from '/@/api/admin/Captcha'
  36. import { isMobile } from '/@/utils/test'
  37. import { ElMessage } from 'element-plus'
  38. const MySlideCaptcha = defineAsyncComponent(() => import('/@/components/my-slide-captcha/index.vue'))
  39. const props = defineProps({
  40. seconds: {
  41. type: Number,
  42. default: 60,
  43. },
  44. startText: {
  45. type: String,
  46. default: '获取验证码',
  47. },
  48. changeText: {
  49. type: String,
  50. default: 's秒重新获取',
  51. },
  52. endText: {
  53. type: String,
  54. default: '重新获取',
  55. },
  56. mobile: {
  57. type: String,
  58. default: '',
  59. },
  60. validate: {
  61. type: Function,
  62. default: null,
  63. },
  64. })
  65. const slideCaptchaRef = ref()
  66. const dialogRef = ref()
  67. const state = reactive({
  68. status: 'ready',
  69. startText: props.startText,
  70. changeText: props.changeText,
  71. endText: props.endText,
  72. countdown: Date.now(),
  73. showDialog: false,
  74. requestId: '',
  75. failTip: '',
  76. successTip: '',
  77. })
  78. const startCountdown = () => {
  79. state.status = 'countdown'
  80. state.countdown = Date.now() + (props.seconds + 1) * 1000
  81. }
  82. const onClick = () => {
  83. if (state.status !== 'countdown') {
  84. if (props.validate) {
  85. props.validate(onGetCode)
  86. } else {
  87. onGetCode()
  88. }
  89. }
  90. }
  91. const onChange = (value: number) => {
  92. if (value < 1000) state.status = 'finish'
  93. }
  94. //刷新滑块验证码
  95. const onRefresh = async () => {
  96. slideCaptchaRef.value.startRequestGenerate()
  97. const res = await new CaptchaApi().generate().catch(() => {
  98. slideCaptchaRef.value.endRequestGenerate(null, null)
  99. })
  100. if (res?.success && res.data) {
  101. state.requestId = res.data.id || ''
  102. slideCaptchaRef.value.endRequestGenerate(res.data.backgroundImage, res.data.sliderImage)
  103. }
  104. }
  105. //验证滑块验证码
  106. const onFinish = async (data: any) => {
  107. slideCaptchaRef.value.startRequestVerify()
  108. const res = await new CaptchaApi().check(data, { id: state.requestId }).catch(() => {
  109. state.failTip = '服务异常,请稍后重试'
  110. slideCaptchaRef.value.endRequestVerify(false)
  111. })
  112. if (res?.success && res.data) {
  113. let success = res.data.result === 0
  114. state.failTip = res.data.result == 1 ? '验证未通过,拖动滑块将悬浮图像正确合并' : '验证超时, 请重新操作'
  115. state.successTip = '验证通过'
  116. slideCaptchaRef.value.endRequestVerify(success)
  117. if (success) {
  118. state.showDialog = false
  119. startCountdown()
  120. //发送短信验证码
  121. } else {
  122. setTimeout(() => {
  123. onRefresh()
  124. }, 1000)
  125. }
  126. }
  127. }
  128. //获得验证码
  129. const onGetCode = () => {
  130. //验证手机号
  131. if (!isMobile(props.mobile)) {
  132. ElMessage.warning({ message: '请输入正确的手机号码', grouping: true })
  133. return
  134. }
  135. state.showDialog = true
  136. //刷新验证码
  137. slideCaptchaRef.value?.handleRefresh()
  138. }
  139. </script>
  140. <style scoped lang="scss">
  141. :deep(.el-statistic__content) {
  142. font-size: var(--el-font-size-base);
  143. }
  144. </style>
  145. <style lang="scss">
  146. .my-captcha .el-dialog__body {
  147. padding-top: 10px;
  148. }
  149. .my-captcha .captcha__bar {
  150. border-color: var(--el-border-color) !important;
  151. }
  152. </style>