123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- <template>
- <div class="verify-points">
- <div class="verify-points-logo">
- <img src="../assets/verify/verify-logo.png" alt="">
- </div>
- <div class="verify-points-panel">
- <div class="verify-points-panel-header">
- <span>请在下图依次点击:</span>
- <span>{{ textVerify }}</span>
- </div>
- <div class="verify-points-panel-main" @click="addPoint($event)" ref="pointsPanelMain">
- <img class="points-panel-img" :src="imgUrl || imgBase64AddPrefix(imgBase64)" alt="" ref="pointsPanelImg">
- <span
- class="icon-verify-point point"
- v-for="(item, index) in points"
- :style="{left: item.offsetX + 'px', top: item.offsetY + 'px'}"
- :key="item.id"
- ref="points"
- @click.stop.prevent="removePoint(item, index)"
- ></span>
- </div>
- <div class="verify-points-panel-footer">
- <span class="verify-refresh" @click="onRefresh"></span>
- <button class="confirm-button verify-confirm" @click="onConfirm">确定</button>
- </div>
- </div>
- </div>
- </template>
- <script>
- // 参考vue2-verify/components/Verify/VerifyPoints.vue
- // https://github.com/mizuka-wu/vue2-verify/
- // 每个点的属性
- class Point {
- constructor (offsetX, offsetY, x, y) {
- // offsetX/offsetY圆点的位置坐标
- this.offsetX = offsetX
- this.offsetY = offsetY
- // x/y选择的实际坐标(精确坐标)需要发送到后端进行验证
- this.x = x
- this.y = y
- this.id = Date.now()
- }
- }
- export default {
- name: 'VerifyPoints',
- props: {
- /**
- * 验证码图片地址
- */
- imgUrl: {
- type: String,
- default: ''
- },
- /**
- * 验证码图片base64
- */
- imgBase64: {
- type: String,
- default: ''
- },
- /**
- * 验证文字
- */
- textVerify: {
- type: String,
- default: ''
- },
- /**
- * 最多有几个点
- */
- pointsMaxCount: {
- type: Number,
- default: 3
- }
- },
- data () {
- return {
- points: []
- }
- },
- computed: {
- pointsCoordinate () {
- return this.points.map(item => {
- return `${parseInt(item.x)},${parseInt(item.y)}`
- })
- }
- },
- watch: {
- textVerify () {
- this.points = []
- }
- },
- methods: {
- imgBase64AddPrefix (base64) {
- if (base64) {
- return `data:image/png;base64,${base64}`
- } else {
- return ''
- }
- },
- createPoint (offsetX, offsetY, x, y) {
- return new Point(offsetX, offsetY, x, y)
- },
- addPoint (e) {
- if (this.points.length >= this.pointsMaxCount) return
- const pointsPanelMain = this.$refs.pointsPanelMain
- const pointsPanelMainRect = pointsPanelMain.getBoundingClientRect()
- // 校正坐标(圆点的半径)圆的位置看起来正确
- // 使用相似计算,以图片宽度256px为例,其下坐标点的半径为9
- const correctNum = parseInt(9 * (pointsPanelMainRect.width / 256))
- // offsetX/offsetY圆点的位置坐标(需要偏移进行微调) === pointDom.offsetX/offsetY
- // offset = 鼠标相对视口坐标 - pointsMain相对视口坐标 - 校正坐标
- const offsetX = e.clientX - pointsPanelMainRect.left - correctNum
- const offsetY = e.clientY - pointsPanelMainRect.top - correctNum
- // x/y选择的实际坐标(精确坐标)需要发送到后端进行判断
- const x = offsetX + correctNum
- const y = offsetY + correctNum
- const point = this.createPoint(offsetX, offsetY, x, y)
- this.points.push(point)
- },
- removePoint (item, index) {
- this.points.splice(index, 1)
- },
- onReset () {
- this.points = []
- },
- onRefresh () {
- this.onReset()
- this.$emit('refresh')
- },
- onConfirm () {
- if (this.points.length <= 0) return
- const imgWidth = this.$refs.pointsPanelImg.getBoundingClientRect().width
- const payload = {
- pointsCoordinate: this.pointsCoordinate,
- imgWidth: parseInt(imgWidth)
- }
- this.$emit('confirm', payload)
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- $white: #fff;
- $black: #000;
- .confirm-button {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- background-color: #2abed1;
- border-radius: 4Px;
- color: $white;
- &:before {
- content: '';
- position: absolute;
- top: 50%;
- left: 50%;
- width: 100%;
- height: 100%;
- background-color: $black;
- border: inherit;
- border-color: $black;
- border-radius: inherit;
- -webkit-transform: translate(-50%,-50%);
- transform: translate(-50%,-50%);
- opacity: 0;
- }
- &:active:before {
- opacity: 0.1;
- }
- }
- .verify-points {
- position: relative;
- width: 280Px;
- transform-origin: center center;
- transition: transform,width 0.2s ease;
- .verify-points-logo {
- width: 100%;
- height: 160Px;
- img {
- width: 100%;
- height: 100%;
- }
- }
- .verify-points-panel {
- margin-top: -1Px;
- padding: 12Px;
- background-color: $white;
- border-radius: 4px;
- &-header {
- font-size: 16Px;
- }
- &-main {
- position: relative;
- margin: 10Px 0;
- min-height: 60Px;
- }
- &-footer {
- display: flex;
- align-items: center;
- justify-content: space-between;
- height: 28Px;
- }
- .verify-refresh {
- width: 28Px;
- height: 28Px;
- background: transparent url(../assets/verify/verify-refresh.png) no-repeat;
- background-size: contain;
- }
- .verify-confirm {
- padding: 0 12Px;
- font-size: 16Px;
- height: 100%;
- }
- }
- .point {
- position: absolute;
- z-index: 5;
- }
- .icon-verify-point {
- display: inline-block;
- width: 18Px;
- height: 18Px;
- background: transparent url(../assets/verify/verify-check.png) no-repeat;
- background-size: contain;
- }
- }
- </style>
|