VerifySlide.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. <template>
  2. <div style="position: relative">
  3. <div
  4. v-if="type === '2'"
  5. class="verify-img-out"
  6. :style="{ height: parseInt(setSize.imgHeight) + vSpace + 'px' }"
  7. >
  8. <div class="verify-img-panel" :style="{ width: setSize.imgWidth, height: setSize.imgHeight }">
  9. <img
  10. :src="'data:image/png;base64,' + backImgBase"
  11. alt=""
  12. style="width: 100%; height: 100%; display: block"
  13. />
  14. <div class="verify-refresh" @click="refresh" v-show="showRefresh"
  15. ><i class="iconfont icon-refresh"></i>
  16. </div>
  17. <transition name="tips">
  18. <span class="verify-tips" v-if="tipWords" :class="passFlag ? 'suc-bg' : 'err-bg'">{{
  19. tipWords
  20. }}</span>
  21. </transition>
  22. </div>
  23. </div>
  24. <!-- 公共部分 -->
  25. <div
  26. class="verify-bar-area"
  27. :style="{ width: setSize.imgWidth, height: barSize.height, 'line-height': barSize.height }"
  28. >
  29. <span class="verify-msg" v-text="text"></span>
  30. <div
  31. class="verify-left-bar"
  32. :style="{
  33. width: leftBarWidth !== undefined ? leftBarWidth : barSize.height,
  34. height: barSize.height,
  35. 'border-color': leftBarBorderColor,
  36. transaction: transitionWidth
  37. }"
  38. >
  39. <span class="verify-msg" v-text="finishText"></span>
  40. <div
  41. class="verify-move-block"
  42. @touchstart="start"
  43. @mousedown="start"
  44. :style="{
  45. width: barSize.height,
  46. height: barSize.height,
  47. 'background-color': moveBlockBackgroundColor,
  48. left: moveBlockLeft,
  49. transition: transitionLeft
  50. }"
  51. >
  52. <i :class="['verify-icon iconfont', iconClass]" :style="{ color: iconColor }"></i>
  53. <div
  54. v-if="type === '2'"
  55. class="verify-sub-block"
  56. :style="{
  57. width: Math.floor((parseInt(setSize.imgWidth) * 47) / 310) + 'px',
  58. height: setSize.imgHeight,
  59. top: '-' + (parseInt(setSize.imgHeight) + vSpace) + 'px',
  60. 'background-size': setSize.imgWidth + ' ' + setSize.imgHeight
  61. }"
  62. >
  63. <img
  64. :src="'data:image/png;base64,' + blockBackImgBase"
  65. alt=""
  66. style="width: 100%; height: 100%; display: block; -webkit-user-drag: none"
  67. />
  68. </div>
  69. </div>
  70. </div>
  71. </div>
  72. </div>
  73. </template>
  74. <script type="text/babel">
  75. /**
  76. * VerifySlide
  77. * @description 滑块
  78. * */
  79. import { aesEncrypt } from './../utils/ase'
  80. import { resetSize } from './../utils/util'
  81. import { reqGet, reqCheck } from './../api/index'
  82. import {
  83. computed,
  84. onMounted,
  85. reactive,
  86. ref,
  87. watch,
  88. nextTick,
  89. toRefs,
  90. getCurrentInstance
  91. } from 'vue'
  92. // "captchaType":"blockPuzzle",
  93. export default {
  94. name: 'VerifySlide',
  95. props: {
  96. captchaType: {
  97. type: String
  98. },
  99. type: {
  100. type: String,
  101. default: '1'
  102. },
  103. //弹出式pop,固定fixed
  104. mode: {
  105. type: String,
  106. default: 'fixed'
  107. },
  108. vSpace: {
  109. type: Number,
  110. default: 5
  111. },
  112. explain: {
  113. type: String,
  114. default: '向右滑动完成验证'
  115. },
  116. imgSize: {
  117. type: Object,
  118. default() {
  119. return {
  120. width: '310px',
  121. height: '155px'
  122. }
  123. }
  124. },
  125. blockSize: {
  126. type: Object,
  127. default() {
  128. return {
  129. width: '50px',
  130. height: '50px'
  131. }
  132. }
  133. },
  134. barSize: {
  135. type: Object,
  136. default() {
  137. return {
  138. width: '310px',
  139. height: '40px'
  140. }
  141. }
  142. }
  143. },
  144. setup(props) {
  145. const { mode, captchaType, type, blockSize, explain } = toRefs(props)
  146. const { proxy } = getCurrentInstance()
  147. let secretKey = ref(''), //后端返回的ase加密秘钥
  148. passFlag = ref(''), //是否通过的标识
  149. backImgBase = ref(''), //验证码背景图片
  150. blockBackImgBase = ref(''), //验证滑块的背景图片
  151. backToken = ref(''), //后端返回的唯一token值
  152. startMoveTime = ref(''), //移动开始的时间
  153. endMovetime = ref(''), //移动结束的时间
  154. tipsBackColor = ref(''), //提示词的背景颜色
  155. tipWords = ref(''),
  156. text = ref(''),
  157. finishText = ref(''),
  158. setSize = reactive({
  159. imgHeight: 0,
  160. imgWidth: 0,
  161. barHeight: 0,
  162. barWidth: 0
  163. }),
  164. top = ref(0),
  165. left = ref(0),
  166. moveBlockLeft = ref(undefined),
  167. leftBarWidth = ref(undefined),
  168. // 移动中样式
  169. moveBlockBackgroundColor = ref(undefined),
  170. leftBarBorderColor = ref('#ddd'),
  171. iconColor = ref(undefined),
  172. iconClass = ref('icon-right'),
  173. status = ref(false), //鼠标状态
  174. isEnd = ref(false), //是够验证完成
  175. showRefresh = ref(true),
  176. transitionLeft = ref(''),
  177. transitionWidth = ref(''),
  178. startLeft = ref(0)
  179. const barArea = computed(() => {
  180. return proxy.$el.querySelector('.verify-bar-area')
  181. })
  182. function init() {
  183. text.value = explain.value
  184. getPictrue()
  185. nextTick(() => {
  186. let { imgHeight, imgWidth, barHeight, barWidth } = resetSize(proxy)
  187. setSize.imgHeight = imgHeight
  188. setSize.imgWidth = imgWidth
  189. setSize.barHeight = barHeight
  190. setSize.barWidth = barWidth
  191. proxy.$parent.$emit('ready', proxy)
  192. })
  193. window.removeEventListener('touchmove', function (e) {
  194. move(e)
  195. })
  196. window.removeEventListener('mousemove', function (e) {
  197. move(e)
  198. })
  199. //鼠标松开
  200. window.removeEventListener('touchend', function () {
  201. end()
  202. })
  203. window.removeEventListener('mouseup', function () {
  204. end()
  205. })
  206. window.addEventListener('touchmove', function (e) {
  207. move(e)
  208. })
  209. window.addEventListener('mousemove', function (e) {
  210. move(e)
  211. })
  212. //鼠标松开
  213. window.addEventListener('touchend', function () {
  214. end()
  215. })
  216. window.addEventListener('mouseup', function () {
  217. end()
  218. })
  219. }
  220. watch(type, () => {
  221. init()
  222. })
  223. onMounted(() => {
  224. // 禁止拖拽
  225. init()
  226. proxy.$el.onselectstart = function () {
  227. return false
  228. }
  229. })
  230. //鼠标按下
  231. function start(e) {
  232. e = e || window.event
  233. if (!e.touches) {
  234. //兼容PC端
  235. var x = e.clientX
  236. } else {
  237. //兼容移动端
  238. var x = e.touches[0].pageX
  239. }
  240. startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left)
  241. startMoveTime.value = +new Date() //开始滑动的时间
  242. if (isEnd.value == false) {
  243. text.value = ''
  244. moveBlockBackgroundColor.value = '#337ab7'
  245. leftBarBorderColor.value = '#337AB7'
  246. iconColor.value = '#fff'
  247. e.stopPropagation()
  248. status.value = true
  249. }
  250. }
  251. //鼠标移动
  252. function move(e) {
  253. e = e || window.event
  254. if (status.value && isEnd.value == false) {
  255. if (!e.touches) {
  256. //兼容PC端
  257. var x = e.clientX
  258. } else {
  259. //兼容移动端
  260. var x = e.touches[0].pageX
  261. }
  262. var bar_area_left = barArea.value.getBoundingClientRect().left
  263. var move_block_left = x - bar_area_left //小方块相对于父元素的left值
  264. if (
  265. move_block_left >=
  266. barArea.value.offsetWidth - parseInt(parseInt(blockSize.value.width) / 2) - 2
  267. ) {
  268. move_block_left =
  269. barArea.value.offsetWidth - parseInt(parseInt(blockSize.value.width) / 2) - 2
  270. }
  271. if (move_block_left <= 0) {
  272. move_block_left = parseInt(parseInt(blockSize.value.width) / 2)
  273. }
  274. //拖动后小方块的left值
  275. moveBlockLeft.value = move_block_left - startLeft.value + 'px'
  276. leftBarWidth.value = move_block_left - startLeft.value + 'px'
  277. }
  278. }
  279. //鼠标松开
  280. function end() {
  281. endMovetime.value = +new Date()
  282. //判断是否重合
  283. if (status.value && isEnd.value == false) {
  284. var moveLeftDistance = parseInt((moveBlockLeft.value || '').replace('px', ''))
  285. moveLeftDistance = (moveLeftDistance * 310) / parseInt(setSize.imgWidth)
  286. let data = {
  287. captchaType: captchaType.value,
  288. pointJson: secretKey.value
  289. ? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), secretKey.value)
  290. : JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
  291. token: backToken.value
  292. }
  293. reqCheck(data).then((res) => {
  294. if (res.repCode == '0000') {
  295. moveBlockBackgroundColor.value = '#5cb85c'
  296. leftBarBorderColor.value = '#5cb85c'
  297. iconColor.value = '#fff'
  298. iconClass.value = 'icon-check'
  299. showRefresh.value = false
  300. isEnd.value = true
  301. if (mode.value == 'pop') {
  302. setTimeout(() => {
  303. proxy.$parent.clickShow = false
  304. refresh()
  305. }, 1500)
  306. }
  307. passFlag.value = true
  308. tipWords.value = `${((endMovetime.value - startMoveTime.value) / 1000).toFixed(
  309. 2
  310. )}s验证成功`
  311. var captchaVerification = secretKey.value
  312. ? aesEncrypt(
  313. backToken.value + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
  314. secretKey.value
  315. )
  316. : backToken.value + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 })
  317. setTimeout(() => {
  318. tipWords.value = ''
  319. proxy.$parent.closeBox()
  320. proxy.$parent.$emit('success', { captchaVerification })
  321. }, 1000)
  322. } else {
  323. moveBlockBackgroundColor.value = '#d9534f'
  324. leftBarBorderColor.value = '#d9534f'
  325. iconColor.value = '#fff'
  326. iconClass.value = 'icon-close'
  327. passFlag.value = false
  328. setTimeout(function () {
  329. refresh()
  330. }, 1000)
  331. proxy.$parent.$emit('error', proxy)
  332. tipWords.value = '验证失败'
  333. setTimeout(() => {
  334. tipWords.value = ''
  335. }, 1000)
  336. }
  337. })
  338. status.value = false
  339. }
  340. }
  341. const refresh = () => {
  342. showRefresh.value = true
  343. finishText.value = ''
  344. transitionLeft.value = 'left .3s'
  345. moveBlockLeft.value = 0
  346. leftBarWidth.value = undefined
  347. transitionWidth.value = 'width .3s'
  348. leftBarBorderColor.value = '#ddd'
  349. moveBlockBackgroundColor.value = '#fff'
  350. iconColor.value = '#000'
  351. iconClass.value = 'icon-right'
  352. isEnd.value = false
  353. getPictrue()
  354. setTimeout(() => {
  355. transitionWidth.value = ''
  356. transitionLeft.value = ''
  357. text.value = explain.value
  358. }, 300)
  359. }
  360. // 请求背景图片和验证图片
  361. function getPictrue() {
  362. let data = {
  363. captchaType: captchaType.value
  364. }
  365. reqGet(data).then((res) => {
  366. if (res.repCode == '0000') {
  367. backImgBase.value = res.repData.originalImageBase64
  368. blockBackImgBase.value = res.repData.jigsawImageBase64
  369. backToken.value = res.repData.token
  370. secretKey.value = res.repData.secretKey
  371. } else {
  372. tipWords.value = res.repMsg
  373. }
  374. })
  375. }
  376. return {
  377. secretKey, //后端返回的ase加密秘钥
  378. passFlag, //是否通过的标识
  379. backImgBase, //验证码背景图片
  380. blockBackImgBase, //验证滑块的背景图片
  381. backToken, //后端返回的唯一token值
  382. startMoveTime, //移动开始的时间
  383. endMovetime, //移动结束的时间
  384. tipsBackColor, //提示词的背景颜色
  385. tipWords,
  386. text,
  387. finishText,
  388. setSize,
  389. top,
  390. left,
  391. moveBlockLeft,
  392. leftBarWidth,
  393. // 移动中样式
  394. moveBlockBackgroundColor,
  395. leftBarBorderColor,
  396. iconColor,
  397. iconClass,
  398. status, //鼠标状态
  399. isEnd, //是够验证完成
  400. showRefresh,
  401. transitionLeft,
  402. transitionWidth,
  403. barArea,
  404. refresh,
  405. start
  406. }
  407. }
  408. }
  409. </script>