VerifyPoints.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <template>
  2. <div class="verify-points">
  3. <div class="verify-points-logo">
  4. <img src="../assets/verify/verify-logo.png" alt="">
  5. </div>
  6. <div class="verify-points-panel">
  7. <div class="verify-points-panel-header">
  8. <span>请在下图依次点击:</span>
  9. <span>{{ textVerify }}</span>
  10. </div>
  11. <div class="verify-points-panel-main" @click="addPoint($event)" ref="pointsPanelMain">
  12. <img class="points-panel-img" :src="imgUrl || imgBase64AddPrefix(imgBase64)" alt="" ref="pointsPanelImg">
  13. <span
  14. class="icon-verify-point point"
  15. v-for="(item, index) in points"
  16. :style="{left: item.offsetX + 'px', top: item.offsetY + 'px'}"
  17. :key="item.id"
  18. ref="points"
  19. @click.stop.prevent="removePoint(item, index)"
  20. ></span>
  21. </div>
  22. <div class="verify-points-panel-footer">
  23. <span class="verify-refresh" @click="onRefresh"></span>
  24. <button class="confirm-button verify-confirm" @click="onConfirm">确定</button>
  25. </div>
  26. </div>
  27. </div>
  28. </template>
  29. <script>
  30. // 参考vue2-verify/components/Verify/VerifyPoints.vue
  31. // https://github.com/mizuka-wu/vue2-verify/
  32. // 每个点的属性
  33. class Point {
  34. constructor (offsetX, offsetY, x, y) {
  35. // offsetX/offsetY圆点的位置坐标
  36. this.offsetX = offsetX
  37. this.offsetY = offsetY
  38. // x/y选择的实际坐标(精确坐标)需要发送到后端进行验证
  39. this.x = x
  40. this.y = y
  41. this.id = Date.now()
  42. }
  43. }
  44. export default {
  45. name: 'VerifyPoints',
  46. props: {
  47. /**
  48. * 验证码图片地址
  49. */
  50. imgUrl: {
  51. type: String,
  52. default: ''
  53. },
  54. /**
  55. * 验证码图片base64
  56. */
  57. imgBase64: {
  58. type: String,
  59. default: ''
  60. },
  61. /**
  62. * 验证文字
  63. */
  64. textVerify: {
  65. type: String,
  66. default: ''
  67. },
  68. /**
  69. * 最多有几个点
  70. */
  71. pointsMaxCount: {
  72. type: Number,
  73. default: 3
  74. }
  75. },
  76. data () {
  77. return {
  78. points: []
  79. }
  80. },
  81. computed: {
  82. pointsCoordinate () {
  83. return this.points.map(item => {
  84. return `${parseInt(item.x)},${parseInt(item.y)}`
  85. })
  86. }
  87. },
  88. watch: {
  89. textVerify () {
  90. this.points = []
  91. }
  92. },
  93. methods: {
  94. imgBase64AddPrefix (base64) {
  95. if (base64) {
  96. return `data:image/png;base64,${base64}`
  97. } else {
  98. return ''
  99. }
  100. },
  101. createPoint (offsetX, offsetY, x, y) {
  102. return new Point(offsetX, offsetY, x, y)
  103. },
  104. addPoint (e) {
  105. if (this.points.length >= this.pointsMaxCount) return
  106. const pointsPanelMain = this.$refs.pointsPanelMain
  107. const pointsPanelMainRect = pointsPanelMain.getBoundingClientRect()
  108. // 校正坐标(圆点的半径)圆的位置看起来正确
  109. // 使用相似计算,以图片宽度256px为例,其下坐标点的半径为9
  110. const correctNum = parseInt(9 * (pointsPanelMainRect.width / 256))
  111. // offsetX/offsetY圆点的位置坐标(需要偏移进行微调) === pointDom.offsetX/offsetY
  112. // offset = 鼠标相对视口坐标 - pointsMain相对视口坐标 - 校正坐标
  113. const offsetX = e.clientX - pointsPanelMainRect.left - correctNum
  114. const offsetY = e.clientY - pointsPanelMainRect.top - correctNum
  115. // x/y选择的实际坐标(精确坐标)需要发送到后端进行判断
  116. const x = offsetX + correctNum
  117. const y = offsetY + correctNum
  118. const point = this.createPoint(offsetX, offsetY, x, y)
  119. this.points.push(point)
  120. },
  121. removePoint (item, index) {
  122. this.points.splice(index, 1)
  123. },
  124. onReset () {
  125. this.points = []
  126. },
  127. onRefresh () {
  128. this.onReset()
  129. this.$emit('refresh')
  130. },
  131. onConfirm () {
  132. if (this.points.length <= 0) return
  133. const imgWidth = this.$refs.pointsPanelImg.getBoundingClientRect().width
  134. const payload = {
  135. pointsCoordinate: this.pointsCoordinate,
  136. imgWidth: parseInt(imgWidth)
  137. }
  138. this.$emit('confirm', payload)
  139. }
  140. }
  141. }
  142. </script>
  143. <style lang="scss" scoped>
  144. $white: #fff;
  145. $black: #000;
  146. .confirm-button {
  147. position: relative;
  148. display: flex;
  149. align-items: center;
  150. justify-content: center;
  151. background-color: #2abed1;
  152. border-radius: 4Px;
  153. color: $white;
  154. &:before {
  155. content: '';
  156. position: absolute;
  157. top: 50%;
  158. left: 50%;
  159. width: 100%;
  160. height: 100%;
  161. background-color: $black;
  162. border: inherit;
  163. border-color: $black;
  164. border-radius: inherit;
  165. -webkit-transform: translate(-50%,-50%);
  166. transform: translate(-50%,-50%);
  167. opacity: 0;
  168. }
  169. &:active:before {
  170. opacity: 0.1;
  171. }
  172. }
  173. .verify-points {
  174. position: relative;
  175. width: 280Px;
  176. transform-origin: center center;
  177. transition: transform,width 0.2s ease;
  178. .verify-points-logo {
  179. width: 100%;
  180. height: 160Px;
  181. img {
  182. width: 100%;
  183. height: 100%;
  184. }
  185. }
  186. .verify-points-panel {
  187. margin-top: -1Px;
  188. padding: 12Px;
  189. background-color: $white;
  190. border-radius: 4px;
  191. &-header {
  192. font-size: 16Px;
  193. }
  194. &-main {
  195. position: relative;
  196. margin: 10Px 0;
  197. min-height: 60Px;
  198. }
  199. &-footer {
  200. display: flex;
  201. align-items: center;
  202. justify-content: space-between;
  203. height: 28Px;
  204. }
  205. .verify-refresh {
  206. width: 28Px;
  207. height: 28Px;
  208. background: transparent url(../assets/verify/verify-refresh.png) no-repeat;
  209. background-size: contain;
  210. }
  211. .verify-confirm {
  212. padding: 0 12Px;
  213. font-size: 16Px;
  214. height: 100%;
  215. }
  216. }
  217. .point {
  218. position: absolute;
  219. z-index: 5;
  220. }
  221. .icon-verify-point {
  222. display: inline-block;
  223. width: 18Px;
  224. height: 18Px;
  225. background: transparent url(../assets/verify/verify-check.png) no-repeat;
  226. background-size: contain;
  227. }
  228. }
  229. </style>