|
|
@ -1,87 +1,46 @@
|
|
|
|
<template>
|
|
|
|
<template>
|
|
|
|
<div style="position: relative;">
|
|
|
|
<div style="position: relative;">
|
|
|
|
<!-- 图片验证码部分 -->
|
|
|
|
|
|
|
|
<div
|
|
|
|
<div
|
|
|
|
v-if="type === '2'"
|
|
|
|
v-if="type === '2'" class="verify-img-out"
|
|
|
|
class="verify-img-out"
|
|
|
|
|
|
|
|
:style="{height: (parseInt(setSize.imgHeight) + vSpace) + 'px'}"
|
|
|
|
:style="{height: (parseInt(setSize.imgHeight) + vSpace) + 'px'}"
|
|
|
|
>
|
|
|
|
>
|
|
|
|
<div
|
|
|
|
<div
|
|
|
|
class="verify-img-panel"
|
|
|
|
class="verify-img-panel" :style="{width: setSize.imgWidth,
|
|
|
|
:style="{width: setSize.imgWidth, height: setSize.imgHeight}"
|
|
|
|
height: setSize.imgHeight,}">
|
|
|
|
>
|
|
|
|
<img :src="'data:image/png;base64,'+backImgBase" alt="" style="width:100%;height:100%;display:block"/>
|
|
|
|
<!-- 显示背景图片 -->
|
|
|
|
<div v-show="showRefresh" class="verify-refresh" @click="refresh"><i class="iconfont icon-refresh"/>
|
|
|
|
<img
|
|
|
|
|
|
|
|
:src="'data:image/png;base64,'+backImgBase"
|
|
|
|
|
|
|
|
alt=""
|
|
|
|
|
|
|
|
style="width:100%;height:100%;display:block"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 刷新按钮 -->
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
|
|
v-show="showRefresh"
|
|
|
|
|
|
|
|
class="verify-refresh"
|
|
|
|
|
|
|
|
@click="refresh"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<i class="iconfont icon-refresh"/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 提示信息 -->
|
|
|
|
|
|
|
|
<transition name="tips">
|
|
|
|
<transition name="tips">
|
|
|
|
<span
|
|
|
|
<span v-if="tipWords" class="verify-tips" :class="passFlag ?'suc-bg':'err-bg'">{{tipWords}}</span>
|
|
|
|
v-if="tipWords"
|
|
|
|
|
|
|
|
class="verify-tips"
|
|
|
|
|
|
|
|
:class="passFlag ?'suc-bg':'err-bg'"
|
|
|
|
|
|
|
|
>{{tipWords}}</span>
|
|
|
|
|
|
|
|
</transition>
|
|
|
|
</transition>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 公共部分 -->
|
|
|
|
<!-- 公共部分(滑动条) -->
|
|
|
|
|
|
|
|
<div
|
|
|
|
<div
|
|
|
|
class="verify-bar-area"
|
|
|
|
class="verify-bar-area" :style="{width: setSize.imgWidth,
|
|
|
|
:style="{width: setSize.imgWidth, height: barSize.height, 'line-height':barSize.height}"
|
|
|
|
height: barSize.height,
|
|
|
|
>
|
|
|
|
'line-height':barSize.height}">
|
|
|
|
<!-- 滑动条上的消息文本 -->
|
|
|
|
|
|
|
|
<span class="verify-msg" v-text="text"/>
|
|
|
|
<span class="verify-msg" v-text="text"/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 左边进度条 -->
|
|
|
|
|
|
|
|
<div
|
|
|
|
<div
|
|
|
|
class="verify-left-bar"
|
|
|
|
class="verify-left-bar"
|
|
|
|
:style="{width: (leftBarWidth !== undefined)? leftBarWidth: barSize.height, height: barSize.height, 'border-color': leftBarBorderColor, transition: transitionWidth}"
|
|
|
|
:style="{width: (leftBarWidth!==undefined)?leftBarWidth: barSize.height, height: barSize.height, 'border-color': leftBarBorderColor, transaction: transitionWidth}">
|
|
|
|
>
|
|
|
|
|
|
|
|
<!-- 完成后的消息文本 -->
|
|
|
|
|
|
|
|
<span class="verify-msg" v-text="finishText"/>
|
|
|
|
<span class="verify-msg" v-text="finishText"/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 滑动块 -->
|
|
|
|
|
|
|
|
<div
|
|
|
|
<div
|
|
|
|
class="verify-move-block"
|
|
|
|
class="verify-move-block"
|
|
|
|
:style="{width: barSize.height, height: barSize.height, 'background-color': moveBlockBackgroundColor, left: moveBlockLeft, transition: transitionLeft}"
|
|
|
|
:style="{width: barSize.height, height: barSize.height, 'background-color': moveBlockBackgroundColor, left: moveBlockLeft, transition: transitionLeft}"
|
|
|
|
@touchstart="start"
|
|
|
|
@touchstart="start"
|
|
|
|
@mousedown="start"
|
|
|
|
@mousedown="start">
|
|
|
|
>
|
|
|
|
|
|
|
|
<!-- 滑动块内的图标 -->
|
|
|
|
|
|
|
|
<i
|
|
|
|
<i
|
|
|
|
:class="['verify-icon iconfont', iconClass]"
|
|
|
|
:class="['verify-icon iconfont', iconClass]"
|
|
|
|
:style="{color: iconColor}"
|
|
|
|
:style="{color: iconColor}"/>
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 当 type 为 '2' 时显示的小图块 -->
|
|
|
|
|
|
|
|
<div
|
|
|
|
<div
|
|
|
|
v-if="type === '2'"
|
|
|
|
v-if="type === '2'" class="verify-sub-block"
|
|
|
|
class="verify-sub-block"
|
|
|
|
:style="{'width':Math.floor(parseInt(setSize.imgWidth)*47/310)+ 'px',
|
|
|
|
:style="{
|
|
|
|
'height': setSize.imgHeight,
|
|
|
|
width: Math.floor(parseInt(setSize.imgWidth)*47/310) + 'px',
|
|
|
|
'top':'-' + (parseInt(setSize.imgHeight) + vSpace) + 'px',
|
|
|
|
height: setSize.imgHeight,
|
|
|
|
|
|
|
|
top: '-' + (parseInt(setSize.imgHeight) + vSpace) + 'px',
|
|
|
|
|
|
|
|
'background-size': setSize.imgWidth + ' ' + setSize.imgHeight,
|
|
|
|
'background-size': setSize.imgWidth + ' ' + setSize.imgHeight,
|
|
|
|
}"
|
|
|
|
}">
|
|
|
|
>
|
|
|
|
<img :src="'data:image/png;base64,'+blockBackImgBase" alt="" style="width:100%;height:100%;display:block;-webkit-user-drag:none;"/>
|
|
|
|
<img
|
|
|
|
|
|
|
|
:src="'data:image/png;base64,'+blockBackImgBase"
|
|
|
|
|
|
|
|
alt=""
|
|
|
|
|
|
|
|
style="width:100%;height:100%;display:block;-webkit-user-drag:none;"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
@ -91,36 +50,37 @@
|
|
|
|
<script type="text/babel">
|
|
|
|
<script type="text/babel">
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* VerifySlide
|
|
|
|
* VerifySlide
|
|
|
|
* @description 滑块验证组件,用于用户交互验证,防止机器人操作。
|
|
|
|
* @description 滑块
|
|
|
|
*/
|
|
|
|
* */
|
|
|
|
import { aesEncrypt } from './../utils/ase' // AES加密函数,用于加密提交的数据
|
|
|
|
import { aesEncrypt } from './../utils/ase'
|
|
|
|
import { resetSize } from './../utils/util' // 重置大小工具函数
|
|
|
|
import { resetSize } from './../utils/util'
|
|
|
|
import { reqCheck, reqGet } from './../api/index' // 请求接口,包含检查和获取验证码图片的API
|
|
|
|
import { reqCheck, reqGet } from './../api/index'
|
|
|
|
import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref, toRefs, watch } from 'vue'
|
|
|
|
import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref, toRefs, watch } from 'vue'
|
|
|
|
|
|
|
|
// "captchaType":"blockPuzzle",
|
|
|
|
export default {
|
|
|
|
export default {
|
|
|
|
name: 'VerifySlide',
|
|
|
|
name: 'VerifySlide',
|
|
|
|
props: {
|
|
|
|
props: {
|
|
|
|
captchaType: { // 验证码类型
|
|
|
|
captchaType: {
|
|
|
|
type: String
|
|
|
|
type: String
|
|
|
|
},
|
|
|
|
},
|
|
|
|
type: { // 验证码种类,默认为'1'
|
|
|
|
type: {
|
|
|
|
type: String,
|
|
|
|
type: String,
|
|
|
|
default: '1'
|
|
|
|
default: '1'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
mode: { // 显示模式:弹出式或固定式
|
|
|
|
// 弹出式pop,固定fixed
|
|
|
|
|
|
|
|
mode: {
|
|
|
|
type: String,
|
|
|
|
type: String,
|
|
|
|
default: 'fixed'
|
|
|
|
default: 'fixed'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
vSpace: { // 垂直间距
|
|
|
|
vSpace: {
|
|
|
|
type: Number,
|
|
|
|
type: Number,
|
|
|
|
default: 5
|
|
|
|
default: 5
|
|
|
|
},
|
|
|
|
},
|
|
|
|
explain: { // 提示文字
|
|
|
|
explain: {
|
|
|
|
type: String,
|
|
|
|
type: String,
|
|
|
|
default: '向右滑动完成验证'
|
|
|
|
default: '向右滑动完成验证'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
imgSize: { // 背景图尺寸
|
|
|
|
imgSize: {
|
|
|
|
type: Object,
|
|
|
|
type: Object,
|
|
|
|
default () {
|
|
|
|
default () {
|
|
|
|
return {
|
|
|
|
return {
|
|
|
@ -129,7 +89,7 @@ export default {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
blockSize: { // 滑块尺寸
|
|
|
|
blockSize: {
|
|
|
|
type: Object,
|
|
|
|
type: Object,
|
|
|
|
default () {
|
|
|
|
default () {
|
|
|
|
return {
|
|
|
|
return {
|
|
|
@ -138,7 +98,7 @@ export default {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
barSize: { // 进度条尺寸
|
|
|
|
barSize: {
|
|
|
|
type: Object,
|
|
|
|
type: Object,
|
|
|
|
default () {
|
|
|
|
default () {
|
|
|
|
return {
|
|
|
|
return {
|
|
|
@ -151,110 +111,133 @@ export default {
|
|
|
|
setup (props) {
|
|
|
|
setup (props) {
|
|
|
|
const { mode, captchaType, type, blockSize, explain } = toRefs(props)
|
|
|
|
const { mode, captchaType, type, blockSize, explain } = toRefs(props)
|
|
|
|
const { proxy } = getCurrentInstance()
|
|
|
|
const { proxy } = getCurrentInstance()
|
|
|
|
|
|
|
|
const secretKey = ref('') // 后端返回的ase加密秘钥
|
|
|
|
// 定义响应式数据
|
|
|
|
const passFlag = ref('') // 是否通过的标识
|
|
|
|
const secretKey = ref('') // 加密秘钥
|
|
|
|
const backImgBase = ref('') // 验证码背景图片
|
|
|
|
const passFlag = ref(false) // 验证通过标识
|
|
|
|
const blockBackImgBase = ref('') // 验证滑块的背景图片
|
|
|
|
const backImgBase = ref('') // 验证码背景图片base64编码
|
|
|
|
|
|
|
|
const blockBackImgBase = ref('') // 滑块背景图片base64编码
|
|
|
|
|
|
|
|
const backToken = ref('') // 后端返回的唯一token值
|
|
|
|
const backToken = ref('') // 后端返回的唯一token值
|
|
|
|
const startMoveTime = ref(0) // 移动开始的时间戳
|
|
|
|
const startMoveTime = ref('') // 移动开始的时间
|
|
|
|
const endMovetime = ref(0) // 移动结束的时间戳
|
|
|
|
const endMovetime = ref('') // 移动结束的时间
|
|
|
|
const tipsBackColor = ref('') // 提示词的背景颜色
|
|
|
|
const tipsBackColor = ref('') // 提示词的背景颜色
|
|
|
|
const tipWords = ref('') // 提示语
|
|
|
|
const tipWords = ref('')
|
|
|
|
const text = ref(explain.value) // 默认提示文本
|
|
|
|
const text = ref('')
|
|
|
|
const finishText = ref('') // 完成后的文本
|
|
|
|
const finishText = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
const setSize = reactive({
|
|
|
|
const setSize = reactive({
|
|
|
|
imgHeight: 0,
|
|
|
|
imgHeight: 0,
|
|
|
|
imgWidth: 0,
|
|
|
|
imgWidth: 0,
|
|
|
|
barHeight: 0,
|
|
|
|
barHeight: 0,
|
|
|
|
barWidth: 0
|
|
|
|
barWidth: 0
|
|
|
|
}) // 图片和进度条尺寸对象
|
|
|
|
})
|
|
|
|
|
|
|
|
const top = ref(0)
|
|
|
|
const top = ref(0) // 滑块顶部位置
|
|
|
|
const left = ref(0)
|
|
|
|
const left = ref(0) // 滑块左侧位置
|
|
|
|
const moveBlockLeft = ref(undefined)
|
|
|
|
const moveBlockLeft = ref(undefined) // 滑块移动后的位置
|
|
|
|
const leftBarWidth = ref(undefined)
|
|
|
|
const leftBarWidth = ref(undefined) // 左侧进度条宽度
|
|
|
|
// 移动中样式
|
|
|
|
|
|
|
|
const moveBlockBackgroundColor = ref(undefined)
|
|
|
|
// 定义样式相关变量
|
|
|
|
const leftBarBorderColor = ref('#ddd')
|
|
|
|
const moveBlockBackgroundColor = ref(undefined) // 滑块背景色
|
|
|
|
const iconColor = ref(undefined)
|
|
|
|
const leftBarBorderColor = ref('#ddd') // 左侧进度条边框颜色
|
|
|
|
const iconClass = ref('icon-right')
|
|
|
|
const iconColor = ref(undefined) // 图标颜色
|
|
|
|
const status = ref(false) // 鼠标状态
|
|
|
|
const iconClass = ref('icon-right') // 图标类名
|
|
|
|
const isEnd = ref(false) // 是够验证完成
|
|
|
|
|
|
|
|
const showRefresh = ref(true)
|
|
|
|
const status = ref(false) // 是否正在拖拽
|
|
|
|
const transitionLeft = ref('')
|
|
|
|
const isEnd = ref(false) // 是否已经完成验证
|
|
|
|
const transitionWidth = ref('')
|
|
|
|
const showRefresh = ref(true) // 是否显示刷新按钮
|
|
|
|
const startLeft = ref(0)
|
|
|
|
const transitionLeft = ref('') // 左移动画
|
|
|
|
|
|
|
|
const transitionWidth = ref('') // 宽度变化动画
|
|
|
|
|
|
|
|
const startLeft = ref(0) // 开始拖拽时滑块的left值
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const barArea = computed(() => {
|
|
|
|
const barArea = computed(() => {
|
|
|
|
return proxy.$el.querySelector('.verify-bar-area') // 获取进度条区域DOM元素
|
|
|
|
return proxy.$el.querySelector('.verify-bar-area')
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
function init () {
|
|
|
|
function init () {
|
|
|
|
// 初始化设置默认提示文本,并获取新的验证码图片
|
|
|
|
text.value = explain.value
|
|
|
|
getPictrue()
|
|
|
|
getPictrue()
|
|
|
|
// 确保所有尺寸正确设置后触发父组件的ready事件
|
|
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
nextTick(() => {
|
|
|
|
const sizeInfo = resetSize(proxy)
|
|
|
|
const { imgHeight, imgWidth, barHeight, barWidth } = resetSize(proxy)
|
|
|
|
Object.assign(setSize, sizeInfo)
|
|
|
|
setSize.imgHeight = imgHeight
|
|
|
|
|
|
|
|
setSize.imgWidth = imgWidth
|
|
|
|
|
|
|
|
setSize.barHeight = barHeight
|
|
|
|
|
|
|
|
setSize.barWidth = barWidth
|
|
|
|
proxy.$parent.$emit('ready', proxy)
|
|
|
|
proxy.$parent.$emit('ready', proxy)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
// 添加触摸和鼠标事件监听器,确保能够处理移动端和PC端事件
|
|
|
|
|
|
|
|
window.addEventListener('touchmove', move)
|
|
|
|
|
|
|
|
window.addEventListener('mousemove', move)
|
|
|
|
|
|
|
|
window.addEventListener('touchend', end)
|
|
|
|
|
|
|
|
window.addEventListener('mouseup', end)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 监听type属性变化重新初始化
|
|
|
|
window.removeEventListener('touchmove', (e) => {
|
|
|
|
|
|
|
|
move(e)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
window.removeEventListener('mousemove', (e) => {
|
|
|
|
|
|
|
|
move(e)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 鼠标松开
|
|
|
|
|
|
|
|
window.removeEventListener('touchend', () => {
|
|
|
|
|
|
|
|
end()
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
window.removeEventListener('mouseup', () => {
|
|
|
|
|
|
|
|
end()
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('touchmove', (e) => {
|
|
|
|
|
|
|
|
move(e)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
window.addEventListener('mousemove', (e) => {
|
|
|
|
|
|
|
|
move(e)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 鼠标松开
|
|
|
|
|
|
|
|
window.addEventListener('touchend', () => {
|
|
|
|
|
|
|
|
end()
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
window.addEventListener('mouseup', () => {
|
|
|
|
|
|
|
|
end()
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
watch(type, () => {
|
|
|
|
watch(type, () => {
|
|
|
|
init()
|
|
|
|
init()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 组件挂载完成后初始化
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
|
|
// 禁止拖拽
|
|
|
|
init()
|
|
|
|
init()
|
|
|
|
// 禁用选择以防止用户选中页面内容
|
|
|
|
|
|
|
|
proxy.$el.onselectstart = function () {
|
|
|
|
proxy.$el.onselectstart = function () {
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
// 鼠标按下
|
|
|
|
// 鼠标按下事件处理函数
|
|
|
|
|
|
|
|
function start (e) {
|
|
|
|
function start (e) {
|
|
|
|
e = e || window.event
|
|
|
|
e = e || window.event
|
|
|
|
let x = !e.touches ? e.clientX : e.touches[0].pageX // 根据环境获取点击坐标
|
|
|
|
let x
|
|
|
|
startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left) // 计算初始left值
|
|
|
|
if (!e.touches) { // 兼容PC端
|
|
|
|
startMoveTime.value = Date.now() // 记录开始时间
|
|
|
|
x = e.clientX
|
|
|
|
if (!isEnd.value) {
|
|
|
|
} else { // 兼容移动端
|
|
|
|
text.value = '' // 清空提示文本
|
|
|
|
x = e.touches[0].pageX
|
|
|
|
// 设置拖拽过程中的样式
|
|
|
|
}
|
|
|
|
|
|
|
|
startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left)
|
|
|
|
|
|
|
|
startMoveTime.value = Date.now() // 开始滑动的时间
|
|
|
|
|
|
|
|
if (isEnd.value === false) {
|
|
|
|
|
|
|
|
text.value = ''
|
|
|
|
moveBlockBackgroundColor.value = '#337ab7'
|
|
|
|
moveBlockBackgroundColor.value = '#337ab7'
|
|
|
|
leftBarBorderColor.value = '#337AB7'
|
|
|
|
leftBarBorderColor.value = '#337AB7'
|
|
|
|
iconColor.value = '#fff'
|
|
|
|
iconColor.value = '#fff'
|
|
|
|
status.value = true // 更新状态为拖拽中
|
|
|
|
e.stopPropagation()
|
|
|
|
|
|
|
|
status.value = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 鼠标移动
|
|
|
|
// 鼠标移动事件处理函数
|
|
|
|
|
|
|
|
function move (e) {
|
|
|
|
function move (e) {
|
|
|
|
e = e || window.event
|
|
|
|
e = e || window.event
|
|
|
|
let x = !e.touches ? e.clientX : e.touches[0].pageX // 根据环境获取当前坐标
|
|
|
|
let x
|
|
|
|
if (status.value && !isEnd.value) {
|
|
|
|
if (status.value && isEnd.value === false) {
|
|
|
|
|
|
|
|
if (!e.touches) { // 兼容PC端
|
|
|
|
|
|
|
|
x = e.clientX
|
|
|
|
|
|
|
|
} else { // 兼容移动端
|
|
|
|
|
|
|
|
x = e.touches[0].pageX
|
|
|
|
|
|
|
|
}
|
|
|
|
const bar_area_left = barArea.value.getBoundingClientRect().left
|
|
|
|
const bar_area_left = barArea.value.getBoundingClientRect().left
|
|
|
|
let move_block_left = x - bar_area_left // 计算滑块相对于父元素的left值
|
|
|
|
let move_block_left = x - bar_area_left // 小方块相对于父元素的left值
|
|
|
|
// 边界检测,确保滑块不会超出进度条范围
|
|
|
|
if (move_block_left >= barArea.value.offsetWidth - Number.parseInt(Number.parseInt(blockSize.value.width) / 2) - 2) {
|
|
|
|
if (move_block_left >= barArea.value.offsetWidth - parseInt(blockSize.value.width) / 2 - 2) {
|
|
|
|
move_block_left = barArea.value.offsetWidth - Number.parseInt(Number.parseInt(blockSize.value.width) / 2) - 2
|
|
|
|
move_block_left = barArea.value.offsetWidth - parseInt(blockSize.value.width) / 2 - 2
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (move_block_left <= 0) {
|
|
|
|
if (move_block_left <= 0) {
|
|
|
|
move_block_left = parseInt(blockSize.value.width) / 2
|
|
|
|
move_block_left = Number.parseInt(Number.parseInt(blockSize.value.width) / 2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 更新滑块位置和进度条宽度
|
|
|
|
// 拖动后小方块的left值
|
|
|
|
moveBlockLeft.value = `${move_block_left - startLeft.value}px`
|
|
|
|
moveBlockLeft.value = `${move_block_left - startLeft.value}px`
|
|
|
|
leftBarWidth.value = `${move_block_left - startLeft.value}px`
|
|
|
|
leftBarWidth.value = `${move_block_left - startLeft.value}px`
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -262,168 +245,99 @@ export default {
|
|
|
|
|
|
|
|
|
|
|
|
// 鼠标松开
|
|
|
|
// 鼠标松开
|
|
|
|
function end () {
|
|
|
|
function end () {
|
|
|
|
// 设置结束时间戳
|
|
|
|
|
|
|
|
endMovetime.value = Date.now()
|
|
|
|
endMovetime.value = Date.now()
|
|
|
|
|
|
|
|
// 判断是否重合
|
|
|
|
// 判断是否满足验证条件:status为true且isEnd为false
|
|
|
|
|
|
|
|
if (status.value && isEnd.value === false) {
|
|
|
|
if (status.value && isEnd.value === false) {
|
|
|
|
// 解析滑块移动的像素距离,并根据设定的图片宽度调整该距离到合适的比例
|
|
|
|
|
|
|
|
let moveLeftDistance = Number.parseInt((moveBlockLeft.value || '').replace('px', ''))
|
|
|
|
let moveLeftDistance = Number.parseInt((moveBlockLeft.value || '').replace('px', ''))
|
|
|
|
moveLeftDistance = moveLeftDistance * 310 / Number.parseInt(setSize.imgWidth)
|
|
|
|
moveLeftDistance = moveLeftDistance * 310 / Number.parseInt(setSize.imgWidth)
|
|
|
|
|
|
|
|
|
|
|
|
// 构建要发送的数据对象,包含验证码类型、点的位置信息(x坐标为滑动距离,y固定值5.0),以及后端令牌
|
|
|
|
|
|
|
|
const data = {
|
|
|
|
const data = {
|
|
|
|
captchaType: captchaType.value, // 验证码类型
|
|
|
|
captchaType: captchaType.value,
|
|
|
|
pointJson: secretKey.value
|
|
|
|
pointJson: secretKey.value ? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), secretKey.value) : JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
|
|
|
|
? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), secretKey.value) // 如果存在密钥,则加密位置信息
|
|
|
|
token: backToken.value
|
|
|
|
: JSON.stringify({ x: moveLeftDistance, y: 5.0 }), // 否则直接序列化位置信息
|
|
|
|
|
|
|
|
token: backToken.value // 后端提供的令牌
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发送请求进行验证
|
|
|
|
|
|
|
|
reqCheck(data).then(res => {
|
|
|
|
reqCheck(data).then(res => {
|
|
|
|
// 如果返回的状态码是'0000',表示验证成功
|
|
|
|
|
|
|
|
if (res.data.repCode === '0000') {
|
|
|
|
if (res.data.repCode === '0000') {
|
|
|
|
// 修改UI样式以反映成功的状态
|
|
|
|
|
|
|
|
moveBlockBackgroundColor.value = '#5cb85c'
|
|
|
|
moveBlockBackgroundColor.value = '#5cb85c'
|
|
|
|
leftBarBorderColor.value = '#5cb85c'
|
|
|
|
leftBarBorderColor.value = '#5cb85c'
|
|
|
|
iconColor.value = '#fff'
|
|
|
|
iconColor.value = '#fff'
|
|
|
|
iconClass.value = 'icon-check'
|
|
|
|
iconClass.value = 'icon-check'
|
|
|
|
showRefresh.value = false
|
|
|
|
showRefresh.value = false
|
|
|
|
isEnd.value = true
|
|
|
|
isEnd.value = true
|
|
|
|
|
|
|
|
|
|
|
|
// 根据模式不同设置不同的行为
|
|
|
|
|
|
|
|
if (mode.value === 'pop') {
|
|
|
|
if (mode.value === 'pop') {
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
proxy.$parent.clickShow = false
|
|
|
|
proxy.$parent.clickShow = false
|
|
|
|
refresh() // 成功后刷新验证码
|
|
|
|
refresh()
|
|
|
|
}, 1500)
|
|
|
|
}, 1500)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
passFlag.value = true
|
|
|
|
passFlag.value = true // 标记为通过验证
|
|
|
|
tipWords.value = `${((endMovetime.value - startMoveTime.value) / 1000).toFixed(2)}s验证成功`
|
|
|
|
tipWords.value = `${((endMovetime.value - startMoveTime.value) / 1000).toFixed(2)}s验证成功` // 显示用时和结果提示
|
|
|
|
const captchaVerification = secretKey.value ? aesEncrypt(`${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}`, secretKey.value) : `${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}`
|
|
|
|
|
|
|
|
|
|
|
|
// 构造最终的验证码验证信息
|
|
|
|
|
|
|
|
const captchaVerification = secretKey.value
|
|
|
|
|
|
|
|
? aesEncrypt(`${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}`, secretKey.value) // 加密
|
|
|
|
|
|
|
|
: `${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}` // 不加密
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 延迟后关闭当前窗口并触发父组件的成功事件
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
tipWords.value = ''
|
|
|
|
tipWords.value = ''
|
|
|
|
proxy.$parent.closeBox()
|
|
|
|
proxy.$parent.closeBox()
|
|
|
|
proxy.$parent.$emit('success', { captchaVerification })
|
|
|
|
proxy.$parent.$emit('success', { captchaVerification })
|
|
|
|
}, 1000)
|
|
|
|
}, 1000)
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// 如果验证失败,修改UI样式以反映错误状态,并准备重试
|
|
|
|
|
|
|
|
moveBlockBackgroundColor.value = '#d9534f'
|
|
|
|
moveBlockBackgroundColor.value = '#d9534f'
|
|
|
|
leftBarBorderColor.value = '#d9534f'
|
|
|
|
leftBarBorderColor.value = '#d9534f'
|
|
|
|
iconColor.value = '#fff'
|
|
|
|
iconColor.value = '#fff'
|
|
|
|
iconClass.value = 'icon-close'
|
|
|
|
iconClass.value = 'icon-close'
|
|
|
|
passFlag.value = false
|
|
|
|
passFlag.value = false
|
|
|
|
|
|
|
|
|
|
|
|
// 延迟后刷新验证码
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
refresh()
|
|
|
|
refresh()
|
|
|
|
}, 1000)
|
|
|
|
}, 1000)
|
|
|
|
|
|
|
|
|
|
|
|
// 触发父组件的错误事件
|
|
|
|
|
|
|
|
proxy.$parent.$emit('error', proxy)
|
|
|
|
proxy.$parent.$emit('error', proxy)
|
|
|
|
tipWords.value = '验证失败'
|
|
|
|
tipWords.value = '验证失败'
|
|
|
|
|
|
|
|
|
|
|
|
// 清除提示文字
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
tipWords.value = ''
|
|
|
|
tipWords.value = ''
|
|
|
|
}, 1000)
|
|
|
|
}, 1000)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
status.value = false
|
|
|
|
status.value = false // 更新状态为已完成验证尝试
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const refresh = () => {
|
|
|
|
const refresh = () => {
|
|
|
|
// 设置 showRefresh 的值为 true,这可能会触发显示一个刷新图标或按钮,
|
|
|
|
showRefresh.value = true
|
|
|
|
// 允许用户知道他们可以刷新验证或者已经开始刷新过程。
|
|
|
|
finishText.value = ''
|
|
|
|
showRefresh.value = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 清空 finishText 的值,finishText 可能是当验证成功后显示给用户的文本信息。
|
|
|
|
|
|
|
|
// 现在我们清除它,为下一次验证做准备。
|
|
|
|
|
|
|
|
finishText.value = '';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 transitionLeft 的值,定义了左侧滑块移动的动画效果持续时间为0.3秒。
|
|
|
|
|
|
|
|
// 这将使滑块平滑地回到起始位置。
|
|
|
|
|
|
|
|
transitionLeft.value = 'left .3s';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将 moveBlockLeft 的值设置为 0,这会将滑块的位置重置到最左边。
|
|
|
|
|
|
|
|
moveBlockLeft.value = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 leftBarWidth 的值为 undefined。这可能是为了确保进度条宽度能够正确重置。
|
|
|
|
|
|
|
|
// 如果之前设置了具体的宽度,在这里取消设置以恢复默认行为。
|
|
|
|
|
|
|
|
leftBarWidth.value = undefined;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 transitionWidth 的值,定义了左侧进度条宽度变化的动画效果持续时间为0.3秒。
|
|
|
|
|
|
|
|
// 这将使进度条宽度的变化看起来更加流畅。
|
|
|
|
|
|
|
|
transitionWidth.value = 'width .3s';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置左栏边框的颜色为浅灰色(#ddd),这是滑块未开始拖动时的默认颜色。
|
|
|
|
|
|
|
|
leftBarBorderColor.value = '#ddd';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置滑块背景色为白色(#fff),这是滑块未开始拖动时的默认颜色。
|
|
|
|
|
|
|
|
moveBlockBackgroundColor.value = '#fff';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置图标的颜色为黑色(#000),这可能是滑块内箭头或其他指示图标的颜色。
|
|
|
|
transitionLeft.value = 'left .3s'
|
|
|
|
iconColor.value = '#000';
|
|
|
|
moveBlockLeft.value = 0
|
|
|
|
|
|
|
|
|
|
|
|
// 设置图标类名为 'icon-right',这可能是指向右侧的箭头图标,
|
|
|
|
leftBarWidth.value = undefined
|
|
|
|
// 表示用户应该向右拖动滑块进行验证。
|
|
|
|
transitionWidth.value = 'width .3s'
|
|
|
|
iconClass.value = 'icon-right';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 isEnd 的值为 false,表示验证过程尚未结束,用户可以继续尝试验证。
|
|
|
|
leftBarBorderColor.value = '#ddd'
|
|
|
|
isEnd.value = false;
|
|
|
|
moveBlockBackgroundColor.value = '#fff'
|
|
|
|
|
|
|
|
iconColor.value = '#000'
|
|
|
|
|
|
|
|
iconClass.value = 'icon-right'
|
|
|
|
|
|
|
|
isEnd.value = false
|
|
|
|
|
|
|
|
|
|
|
|
// 调用 getPictrue 函数,这可能是用来获取新的验证码图片。
|
|
|
|
getPictrue()
|
|
|
|
// 在刷新验证时,更换验证码图片是必要的,以防止用户使用旧的验证码通过验证。
|
|
|
|
|
|
|
|
getPictrue();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 使用 setTimeout 来延迟执行以下操作300毫秒:
|
|
|
|
|
|
|
|
// - 清除 transitionWidth 和 transitionLeft 的值,停止任何正在进行的CSS过渡效果。
|
|
|
|
|
|
|
|
// - 将 text 的值设置为 explain 的值,这可能是为了更新验证区域中的提示文本。
|
|
|
|
|
|
|
|
// 例如,解释用户接下来应该做什么。
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
transitionWidth.value = '';
|
|
|
|
transitionWidth.value = ''
|
|
|
|
transitionLeft.value = '';
|
|
|
|
transitionLeft.value = ''
|
|
|
|
text.value = explain.value;
|
|
|
|
text.value = explain.value
|
|
|
|
}, 300);
|
|
|
|
}, 300)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 请求背景图片和验证图片
|
|
|
|
// 请求背景图片和验证图片
|
|
|
|
function getPictrue () {
|
|
|
|
function getPictrue () {
|
|
|
|
// 构建请求数据对象,包含必要的参数以获取新的验证码图片
|
|
|
|
|
|
|
|
const data = {
|
|
|
|
const data = {
|
|
|
|
captchaType: captchaType.value, // 验证码类型,可能是滑块验证、点击验证等
|
|
|
|
captchaType: captchaType.value,
|
|
|
|
clientUid: localStorage.getItem('b2cSlider'), // 客户端唯一标识符,从本地存储中获取
|
|
|
|
clientUid: localStorage.getItem('b2cSlider'),
|
|
|
|
ts: Date.now() // 当前时间戳,用来确保每次请求都是唯一的,防止缓存
|
|
|
|
ts: Date.now() // 现在的时间戳
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发送请求以获取新的验证码图片信息
|
|
|
|
|
|
|
|
reqGet(data).then(res => {
|
|
|
|
reqGet(data).then(res => {
|
|
|
|
// 检查服务器响应的状态码是否为'0000',表示成功获取验证码图片
|
|
|
|
|
|
|
|
if (res.data.repCode === '0000') {
|
|
|
|
if (res.data.repCode === '0000') {
|
|
|
|
// 更新背景图片和滑块图片的Base64编码字符串
|
|
|
|
backImgBase.value = res.data.repData.originalImageBase64
|
|
|
|
backImgBase.value = res.data.repData.originalImageBase64; // 背景图的Base64编码
|
|
|
|
blockBackImgBase.value = res.data.repData.jigsawImageBase64
|
|
|
|
blockBackImgBase.value = res.data.repData.jigsawImageBase64; // 滑块图的Base64编码
|
|
|
|
backToken.value = res.data.repData.token
|
|
|
|
|
|
|
|
secretKey.value = res.data.repData.secretKey
|
|
|
|
// 更新后端令牌和密钥,这些可能用于后续的验证请求
|
|
|
|
|
|
|
|
backToken.value = res.data.repData.token;
|
|
|
|
|
|
|
|
secretKey.value = res.data.repData.secretKey;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// 如果获取验证码图片失败,设置提示文字显示错误信息
|
|
|
|
tipWords.value = res.data.repMsg
|
|
|
|
tipWords.value = res.data.repMsg; // 显示来自服务器的错误消息
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
secretKey, // 后端返回的ase加密秘钥
|
|
|
|
secretKey, // 后端返回的ase加密秘钥
|
|
|
|