SliderVerify.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. <template>
  2. <div class="slider-verify-container w-320px my-3" v-loading="isResetting">
  3. <div class="relative">
  4. <img :src="captcha.canvasSrc" alt="验证图" draggable="false"/>
  5. <img
  6. ref="blockImage"
  7. :src="captcha.blockSrc"
  8. class="absolute left-0"
  9. :style="{top: captcha.blockY + 'px', transform: `translateX(${blockX}px)`}"
  10. alt="缺失的块"
  11. draggable="false"
  12. />
  13. </div>
  14. <div class="relative bg-white text-center line-height-50px h-50px shadow-inset shadow-black shadow-sm bg-opacity-50">
  15. <span v-show="!isDragging">向右拖动滑块来验证</span>
  16. <div
  17. ref="sliderBlock"
  18. class="absolute top-0 left-0 mt-5px ml-8px w-49px h-40px bg-blue cursor-pointer rounded-5px"
  19. :class="{'shadow-inset shadow-black shadow-sm': isDragging,'cursor-not-allowed':isResetting}"
  20. :style="{transform: `translateX(${currentX}px)`}"
  21. @mousedown="startDrag"
  22. @mousemove="onDrag"
  23. @mouseup="endDrag"
  24. ></div>
  25. </div>
  26. <div class="text-black text-5 rounded-50%">
  27. <el-icon class="cursor-pointer hover:text-cyan" @click="reset">
  28. <RefreshRight />
  29. </el-icon>
  30. </div>
  31. </div>
  32. </template>
  33. <script setup lang="ts">
  34. import request from '@/utils/request'
  35. import { ElMessage } from 'element-plus'
  36. import { ref } from 'vue'
  37. import { RefreshRight } from '@element-plus/icons-vue'
  38. interface Captcha {
  39. nonceStr: string,
  40. value: string,
  41. canvasSrc: string,
  42. canvasWidth: number,
  43. canvasHeight: number,
  44. blockSrc: string,
  45. blockWidth: number,
  46. blockHeight: number,
  47. blockRadius: number,
  48. blockX: number,
  49. blockY: number,
  50. place: number,
  51. }
  52. interface SliderVerifyResponse extends AxiosResponse{
  53. data: Captcha
  54. }
  55. const captcha = ref<Captcha>({
  56. blockHeight: 0,
  57. blockRadius: 0,
  58. blockSrc: '',
  59. blockWidth: 0,
  60. blockX: 0,
  61. blockY: 0,
  62. canvasHeight: 0,
  63. canvasSrc: '',
  64. canvasWidth: 0,
  65. nonceStr: '',
  66. place: 0,
  67. value: ''
  68. })
  69. const sliderBlock = ref<HTMLElement | null>(null)
  70. const blockImage = ref<HTMLElement | null>(null)
  71. const isDragging = ref(false);
  72. const startX = ref(0)
  73. const currentX = ref(0)
  74. const blockX = ref(0)
  75. const isResetting = ref(false);
  76. const startTime = ref(0);
  77. const emits = defineEmits(['verify','resetSlider']);
  78. const getImg = async () => {
  79. const res = await request.post<Captcha, SliderVerifyResponse>("/captchaSlider/getCaptcha", {})
  80. if (res.code !== 200) {
  81. ElMessage.error("当次获取验证图失败")
  82. }
  83. captcha.value = res.data
  84. blockX.value = captcha.value.blockX
  85. }
  86. const init = () => {
  87. getImg()
  88. }
  89. const startDrag = (event: MouseEvent) => {
  90. startTime.value = Date.now()
  91. if (sliderBlock.value && !isResetting.value) {
  92. isDragging.value = true
  93. startX.value = event.clientX - sliderBlock.value.offsetLeft
  94. }
  95. }
  96. const onDrag = (event: MouseEvent) => {
  97. event.preventDefault()
  98. if (isDragging.value && sliderBlock.value) {
  99. currentX.value = event.clientX - startX.value
  100. if (currentX.value < 0) currentX.value = 0
  101. if (currentX.value > 252) currentX.value = 252
  102. blockX.value = currentX.value
  103. }
  104. }
  105. const endDrag = () => {
  106. if(Date.now() - startTime.value > 10000){
  107. emits('resetSlider');
  108. return
  109. }
  110. if(Date.now() - startTime.value < 100){
  111. reset()
  112. return
  113. }
  114. if (isDragging.value) {
  115. emits('verify', captcha.value.nonceStr, currentX.value);
  116. isDragging.value = false
  117. isResetting.value = true
  118. }
  119. }
  120. const reset =async ()=>{
  121. await getImg();
  122. isResetting.value = false
  123. isDragging.value = false
  124. blockX.value = 0;
  125. currentX.value = 0;
  126. }
  127. defineExpose({reset});
  128. init()
  129. </script>