verifyPoint.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. <template>
  2. <view style="position: relative">
  3. <view class="verify-image-out" v-show="showImage">
  4. <view class="verify-image-panel" :style="{'width': imgSize.width,
  5. 'height': imgSize.height,
  6. 'margin-bottom': vSpace + 'px'}">
  7. <view class="verify-refresh" style="z-index:3" @click="refresh" v-show="showRefresh">
  8. <text class="iconfont icon-refresh"></text>
  9. </view>
  10. <image :src="pointBackImgBase?('data:image/png;base64,'+pointBackImgBase):defaultImg" id="image"
  11. ref="canvas" style="width:100%;height:100%;display:block"
  12. @click=" bindingClick? canvasClick($event): undefined"></image>
  13. <view v-for="(tempPoint, index) in tempPoints" :key="index" class="point-area" :style="{
  14. 'background-color':'#1abd6c',
  15. color:'#fff',
  16. 'z-index':9999,
  17. width:'20px',
  18. height:'20px',
  19. 'text-align':'center',
  20. 'line-height':'20px',
  21. 'border-radius': '50%',
  22. position:'absolute',
  23. top:parseInt(tempPoint.y-10) + 'px',
  24. left:parseInt(tempPoint.x-10) + 'px'
  25. }">
  26. {{index + 1}}
  27. </view>
  28. </view>
  29. </view>
  30. <!-- 'height': this.barSize.height, -->
  31. <view class="verify-bar-area" :style="{'width': imgSize.width,
  32. 'color': barAreaColor,
  33. 'border-color': barAreaBorderColor,
  34. 'line-height':'40px'}">
  35. <text class="verify-msg">{{text}}</text>
  36. </view>
  37. </view>
  38. </template>
  39. <script type="text/babel">
  40. // +----------------------------------------------------------------------
  41. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  42. // +----------------------------------------------------------------------
  43. // | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
  44. // +----------------------------------------------------------------------
  45. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  46. // +----------------------------------------------------------------------
  47. // | Author: CRMEB Team <admin@crmeb.com>
  48. // +----------------------------------------------------------------------
  49. /**
  50. * VerifyPoints
  51. * @description 点选
  52. * */
  53. import {aesEncrypt} from "./../utils/ase.js"
  54. import {getAjcaptcha,ajcaptchaCheck} from '@/api/api.js';
  55. export default {
  56. name: 'VerifyPoints',
  57. props: {
  58. //弹出式pop,固定fixed
  59. mode: {
  60. type: String,
  61. default: 'fixed'
  62. },
  63. captchaType:{
  64. type:String,
  65. },
  66. //间隔
  67. vSpace: {
  68. type: Number,
  69. default: 5
  70. },
  71. imgSize: {
  72. type: Object,
  73. default() {
  74. return {
  75. width: '310px',
  76. height: '155px'
  77. }
  78. }
  79. },
  80. barSize: {
  81. type: Object,
  82. default() {
  83. return {
  84. width: '310px',
  85. height: '40px'
  86. }
  87. }
  88. },
  89. defaultImg: {
  90. type: String,
  91. default: ''
  92. }
  93. },
  94. data() {
  95. return {
  96. secretKey:'', //后端返回的加密秘钥 字段
  97. checkNum:3, //
  98. fontPos: [], // 选中的坐标信息
  99. checkPosArr: [], //用户点击的坐标
  100. num: 1, //点击的记数
  101. pointBackImgBase:'', //后端获取到的背景图片
  102. poinTextList:[], //后端返回的点击字体顺序
  103. backToken:'', //后端返回的token值
  104. imgRand: 0, //随机的背景图片
  105. setSize: {
  106. imgHeight: 0,
  107. imgWidth: 0,
  108. barHeight: 0,
  109. barWidth: 0
  110. },
  111. showImage: true,
  112. tempPoints: [],
  113. text: '',
  114. barAreaColor: '#fff',
  115. barAreaBorderColor: "#fff",
  116. showRefresh: true,
  117. bindingClick: true,
  118. imgLeft:'' ,
  119. imgTop:'',
  120. }
  121. },
  122. methods: {
  123. init() {
  124. //加载页面
  125. this.fontPos.splice(0, this.fontPos.length)
  126. this.checkPosArr.splice(0, this.checkPosArr.length)
  127. this.num = 1
  128. this.$nextTick(() => {
  129. this.refresh();
  130. this.$parent.$emit('ready', this)
  131. })
  132. },
  133. canvasClick(e) {
  134. const query = uni.createSelectorQuery().in(this);
  135. query.select('#image').boundingClientRect(data => {
  136. this.imgLeft =Math.ceil(data.left)
  137. this.imgTop =Math.ceil(data.top)
  138. this.checkPosArr.push(this.getMousePos(this.$refs.canvas, e));
  139. if (this.num == this.checkNum) {
  140. this.num = this.createPoint(this.getMousePos(this.$refs.canvas, e));
  141. //按比例转换坐标值
  142. this.checkPosArr = this.pointTransfrom(this.checkPosArr,this.imgSize);
  143. //等创建坐标执行完
  144. setTimeout(() => {
  145. //发送后端请求
  146. var captchaVerification =this.secretKey? aesEncrypt(this.backToken+'---'+JSON.stringify(this.checkPosArr),this.secretKey):this.backToken+'---'+JSON.stringify(this.checkPosArr)
  147. let data = {
  148. captchaType:this.captchaType,
  149. "pointJson":this.secretKey? aesEncrypt(JSON.stringify(this.checkPosArr),this.secretKey):JSON.stringify(this.checkPosArr),
  150. "token":this.backToken
  151. }
  152. ajcaptchaCheck(data).then(result => {
  153. let res = result.data
  154. this.barAreaColor = '#4cae4c'
  155. this.barAreaBorderColor = '#5cb85c'
  156. this.text = '验证成功'
  157. this.bindingClick = false
  158. setTimeout(()=>{
  159. if (this.mode=='pop') {
  160. this.$parent.clickShow = false;
  161. }
  162. this.refresh();
  163. },1500)
  164. this.$parent.$emit('success', {captchaVerification})
  165. }).catch(res=>{
  166. this.$parent.$emit('error', this)
  167. this.barAreaColor = '#d9534f'
  168. this.barAreaBorderColor = '#d9534f'
  169. this.text = '验证失败'
  170. setTimeout(() => {
  171. this.refresh();
  172. }, 700);
  173. })
  174. }, 400);
  175. }
  176. if (this.num < this.checkNum) {
  177. this.num = this.createPoint(this.getMousePos(this.$refs.canvas, e));
  178. }
  179. }).exec();
  180. },
  181. //获取坐标
  182. getMousePos: function (obj, e) {
  183. let position = {
  184. x:Math.ceil(e.detail.x)-this.imgLeft,
  185. y:Math.ceil(e.detail.y)-this.imgTop,
  186. }
  187. return position
  188. },
  189. //创建坐标点
  190. createPoint: function (pos) {
  191. this.tempPoints.push(Object.assign({}, pos))
  192. return ++this.num;
  193. },
  194. refresh: function () {
  195. this.tempPoints.splice(0, this.tempPoints.length)
  196. this.barAreaColor = '#000'
  197. this.barAreaBorderColor = '#ddd'
  198. this.bindingClick = true
  199. this.fontPos.splice(0, this.fontPos.length)
  200. this.checkPosArr.splice(0, this.checkPosArr.length)
  201. this.num = 1
  202. this.getPictrue();
  203. // this.text = '验证失败'
  204. this.showRefresh = true
  205. },
  206. // 请求背景图片和验证图片
  207. getPictrue(){
  208. let data = {
  209. captchaType:this.captchaType,
  210. clientUid: uni.getStorageSync('point'),
  211. ts: Date.now(), // 现在的时间戳
  212. }
  213. getAjcaptcha(data).then((result) => {
  214. let res = result.data
  215. this.pointBackImgBase = res.originalImageBase64
  216. this.backToken = res.token
  217. this.secretKey = res.secretKey
  218. this.poinTextList = res.wordList
  219. this.text = '请依次点击【' + this.poinTextList.join(",") + '】'
  220. }).catch(()=>{
  221. this.pointBackImgBase = null
  222. })
  223. },
  224. //坐标转换函数
  225. pointTransfrom(pointArr,imgSize){
  226. var newPointArr = pointArr.map(p=>{
  227. let x = Math.round(310 * p.x/parseInt(imgSize.width))
  228. let y =Math.round(155 * p.y/parseInt(imgSize.height))
  229. return {x,y}
  230. })
  231. // console.log(newPointArr,"newPointArr");
  232. return newPointArr
  233. },
  234. },
  235. watch: {
  236. // type变化则全面刷新
  237. type: {
  238. immediate: true,
  239. handler() {
  240. this.init()
  241. }
  242. }
  243. },
  244. mounted() {
  245. console.log(this.defaultImg)
  246. }
  247. }
  248. </script>
  249. <style scoped>
  250. .verifybox {
  251. position: relative;
  252. box-sizing: border-box;
  253. border-radius: 2px;
  254. border: 1px solid #e4e7eb;
  255. background-color: #fff;
  256. box-shadow: 0 0 10px rgba(0, 0, 0, .3);
  257. left: 50%;
  258. top: 50%;
  259. transform: translate(-50%, -50%);
  260. }
  261. .verifybox-top {
  262. padding: 0 15px;
  263. height: 50px;
  264. line-height: 50px;
  265. text-align: left;
  266. font-size: 16px;
  267. color: #45494c;
  268. border-bottom: 1px solid #e4e7eb;
  269. box-sizing: border-box;
  270. }
  271. .verifybox-bottom {
  272. /* padding: 15px; */
  273. box-sizing: border-box;
  274. }
  275. .verifybox-close {
  276. position: absolute;
  277. top: 13px;
  278. right: 9px;
  279. width: 24px;
  280. height: 24px;
  281. text-align: center;
  282. cursor: pointer;
  283. }
  284. .mask {
  285. position: fixed;
  286. top: 0;
  287. left: 0;
  288. z-index: 1001;
  289. width: 100%;
  290. height: 100vh;
  291. background: rgba(0, 0, 0, .3);
  292. /* display: none; */
  293. transition: all .5s;
  294. }
  295. .verify-tips {
  296. position: absolute;
  297. left: 0px;
  298. bottom: 0px;
  299. width: 100%;
  300. height: 30px;
  301. background-color: rgb(231, 27, 27, .5);
  302. line-height: 30px;
  303. color: #fff;
  304. }
  305. .tips-enter,
  306. .tips-leave-to {
  307. bottom: -30px;
  308. }
  309. .tips-enter-active,
  310. .tips-leave-active {
  311. transition: bottom .5s;
  312. }
  313. /* ---------------------------- */
  314. /*常规验证码*/
  315. .verify-code {
  316. font-size: 20px;
  317. text-align: center;
  318. cursor: pointer;
  319. margin-bottom: 5px;
  320. border: 1px solid #ddd;
  321. }
  322. .cerify-code-panel {
  323. height: 100%;
  324. overflow: hidden;
  325. }
  326. .verify-code-area {
  327. float: left;
  328. }
  329. .verify-input-area {
  330. float: left;
  331. width: 60%;
  332. padding-right: 10px;
  333. }
  334. .verify-change-area {
  335. line-height: 30px;
  336. float: left;
  337. }
  338. .varify-input-code {
  339. display: inline-block;
  340. width: 100%;
  341. height: 25px;
  342. }
  343. .verify-change-code {
  344. color: #337AB7;
  345. cursor: pointer;
  346. }
  347. .verify-btn {
  348. width: 200px;
  349. height: 30px;
  350. background-color: #337AB7;
  351. color: #FFFFFF;
  352. border: none;
  353. margin-top: 10px;
  354. }
  355. /*滑动验证码*/
  356. .verify-bar-area {
  357. position: relative;
  358. background: #FFFFFF;
  359. text-align: center;
  360. -webkit-box-sizing: content-box;
  361. -moz-box-sizing: content-box;
  362. box-sizing: content-box;
  363. border: 1px solid #ddd;
  364. -webkit-border-radius: 4px;
  365. }
  366. .verify-bar-area .verify-move-block {
  367. position: absolute;
  368. top: 0px;
  369. left: 0;
  370. background: #fff;
  371. cursor: pointer;
  372. -webkit-box-sizing: content-box;
  373. -moz-box-sizing: content-box;
  374. box-sizing: content-box;
  375. box-shadow: 0 0 2px #888888;
  376. -webkit-border-radius: 1px;
  377. }
  378. .verify-bar-area .verify-move-block:hover {
  379. background-color: #337ab7;
  380. color: #FFFFFF;
  381. }
  382. .verify-bar-area .verify-left-bar {
  383. position: absolute;
  384. top: -1px;
  385. left: -1px;
  386. background: #f0fff0;
  387. cursor: pointer;
  388. -webkit-box-sizing: content-box;
  389. -moz-box-sizing: content-box;
  390. box-sizing: content-box;
  391. border: 1px solid #ddd;
  392. }
  393. .verify-image-panel {
  394. margin: 0;
  395. -webkit-box-sizing: content-box;
  396. -moz-box-sizing: content-box;
  397. box-sizing: content-box;
  398. border-top: 1px solid #ddd;
  399. border-bottom: 1px solid #ddd;
  400. border-radius: 3px;
  401. position: relative;
  402. }
  403. .verify-image-panel .verify-refresh {
  404. width: 25px;
  405. height: 25px;
  406. text-align: center;
  407. padding: 5px;
  408. cursor: pointer;
  409. position: absolute;
  410. top: 0;
  411. right: 0;
  412. z-index: 2;
  413. }
  414. .verify-image-panel .icon-refresh {
  415. font-size: 20px;
  416. color: #fff;
  417. }
  418. .verify-image-panel .verify-gap {
  419. background-color: #fff;
  420. position: relative;
  421. z-index: 2;
  422. border: 1px solid #fff;
  423. }
  424. .verify-bar-area .verify-move-block .verify-sub-block {
  425. position: absolute;
  426. text-align: center;
  427. z-index: 3;
  428. /* border: 1px solid #fff; */
  429. }
  430. .verify-bar-area .verify-move-block .verify-icon {
  431. width: 80rpx;
  432. height: 80rpx;
  433. font-size: 18px;
  434. }
  435. .verify-bar-area .verify-msg {
  436. z-index: 3;
  437. }
  438. /*字体图标的css*/
  439. /*@font-face {font-family: "iconfont";*/
  440. /*src: url('../fonts/iconfont.eot?t=1508229193188'); !* IE9*!*/
  441. /*src: url('../fonts/iconfont.eot?t=1508229193188#iefix') format('embedded-opentype'), !* IE6-IE8 *!*/
  442. /*url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'),*/
  443. /*url('../fonts/iconfont.ttf?t=1508229193188') format('truetype'), !* chrome, firefox, opera, Safari, Android, iOS 4.2+*!*/
  444. /*url('../fonts/iconfont.svg?t=1508229193188#iconfont') format('svg'); !* iOS 4.1- *!*/
  445. /*}*/
  446. .iconfont {
  447. font-family: "iconfont" !important;
  448. font-size: 16px;
  449. font-style: normal;
  450. -webkit-font-smoothing: antialiased;
  451. -moz-osx-font-smoothing: grayscale;
  452. }
  453. .icon-check:before {
  454. content: " ";
  455. display: block;
  456. width: 16px;
  457. height: 16px;
  458. position: absolute;
  459. margin: auto;
  460. left: 0;
  461. right: 0;
  462. top: 0;
  463. bottom: 0;
  464. z-index: 9999;
  465. background-image: url("@/static/images/arrow-good.png");
  466. background-size: contain;
  467. }
  468. .icon-close:before {
  469. content: " ";
  470. display: block;
  471. width: 16px;
  472. height: 16px;
  473. position: absolute;
  474. margin: auto;
  475. left: 0;
  476. right: 0;
  477. top: 0;
  478. bottom: 0;
  479. z-index: 9999;
  480. background-image: url("@/static/images/arrow-close.png");
  481. background-size: contain;
  482. }
  483. .icon-right:before {
  484. content: " ";
  485. display: block;
  486. width: 16px;
  487. height: 16px;
  488. position: absolute;
  489. margin: auto;
  490. left: 0;
  491. right: 0;
  492. top: 0;
  493. bottom: 0;
  494. background-size: cover;
  495. z-index: 9999;
  496. background-image: url("@/static/images/arrow-right.png");
  497. background-size: contain;
  498. }
  499. .icon-refresh:before {
  500. content: " ";
  501. display: block;
  502. width: 16px;
  503. height: 16px;
  504. position: absolute;
  505. margin: auto;
  506. left: 0;
  507. right: 0;
  508. top: 0;
  509. bottom: 0;
  510. z-index: 9999;
  511. background-image: url("@/static/images/refresh.png");
  512. background-size: contain;
  513. }
  514. </style>