VerifySlide.vue 11 KB

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