Pre Merge pull request !809 from 山野羡民/feat-captcha
commit
f1a096eec1
|
|
@ -36,14 +36,15 @@
|
||||||
* Verify 验证码组件
|
* Verify 验证码组件
|
||||||
* @description 分发验证码使用
|
* @description 分发验证码使用
|
||||||
* */
|
* */
|
||||||
import { VerifyPoints, VerifySlide } from './Verify'
|
import { VerifyPoints, VerifySlide, VerifyInput } from './Verify'
|
||||||
import { computed, ref, toRefs, watchEffect } from 'vue'
|
import { computed, ref, toRefs, watchEffect } from 'vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Vue3Verify',
|
name: 'Vue3Verify',
|
||||||
components: {
|
components: {
|
||||||
VerifySlide,
|
VerifySlide,
|
||||||
VerifyPoints
|
VerifyPoints,
|
||||||
|
VerifyInput
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
captchaType: {
|
captchaType: {
|
||||||
|
|
@ -118,14 +119,21 @@ export default {
|
||||||
}
|
}
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
switch (captchaType.value) {
|
switch (captchaType.value) {
|
||||||
case 'blockPuzzle':
|
case 'blockPuzzle': // 滑块拼图
|
||||||
verifyType.value = '2'
|
verifyType.value = '2'
|
||||||
componentType.value = 'VerifySlide'
|
componentType.value = 'VerifySlide'
|
||||||
break
|
break
|
||||||
case 'clickWord':
|
case 'clickWord': // 文字点选
|
||||||
verifyType.value = ''
|
verifyType.value = ''
|
||||||
componentType.value = 'VerifyPoints'
|
componentType.value = 'VerifyPoints'
|
||||||
break
|
break
|
||||||
|
case 'pictureWord': // 文字输入
|
||||||
|
verifyType.value = ''
|
||||||
|
componentType.value = 'VerifyInput'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
// TODO 不支持的验证码类型,如:rotatePuzzle 旋转拼图
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
<template>
|
||||||
|
<div style="position: relative">
|
||||||
|
<div class="verify-img-out">
|
||||||
|
<div
|
||||||
|
:style="{
|
||||||
|
width: setSize.imgWidth,
|
||||||
|
height: setSize.imgHeight,
|
||||||
|
'background-size': setSize.imgWidth + ' ' + setSize.imgHeight,
|
||||||
|
'margin-bottom': vSpace + 'px'
|
||||||
|
}"
|
||||||
|
class="verify-img-panel"
|
||||||
|
>
|
||||||
|
<div v-show="showRefresh" class="verify-refresh" style="z-index: 3" @click="refresh">
|
||||||
|
<i class="iconfont icon-refresh"></i>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
ref="canvas"
|
||||||
|
:src="'data:image/png;base64,' + inputBackImgBase"
|
||||||
|
alt=""
|
||||||
|
style="display: block; width: 100%; height: 100%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
:style="{
|
||||||
|
width: setSize.imgWidth,
|
||||||
|
border: 'none',
|
||||||
|
'line-height': barSize.height
|
||||||
|
}"
|
||||||
|
class="verify-bar-area flex"
|
||||||
|
>
|
||||||
|
<el-text>{{ t('login.code') }}</el-text>
|
||||||
|
<el-input v-model="userCode" :placeholder="t('login.codePlaceholder')" class="flex-1 ml-10px"/>
|
||||||
|
<el-button type="primary" @click="doCheck" class="ml-10px">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
/**
|
||||||
|
* VerifyInput
|
||||||
|
* @description 输入
|
||||||
|
* */
|
||||||
|
import {resetSize} from '../utils/util'
|
||||||
|
import {getCode, reqCheck} from '@/api/login'
|
||||||
|
import {getCurrentInstance, nextTick, onMounted, reactive, ref, toRefs} from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
//弹出式-pop,固定-fixed
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'fixed'
|
||||||
|
},
|
||||||
|
captchaType: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
//间隔,单位px
|
||||||
|
vSpace: {
|
||||||
|
type: Number,
|
||||||
|
default: 5
|
||||||
|
},
|
||||||
|
imgSize: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
width: '310px',
|
||||||
|
height: '155px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
barSize: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
width: '310px',
|
||||||
|
height: '40px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const {mode, captchaType} = toRefs(props)
|
||||||
|
const {proxy} = getCurrentInstance()
|
||||||
|
let inputBackImgBase = ref('') //后端获取到的背景图片
|
||||||
|
let backToken = ref('') //后端返回的token值
|
||||||
|
let setSize = reactive({
|
||||||
|
imgHeight: 0,
|
||||||
|
imgWidth: 0,
|
||||||
|
barHeight: 0,
|
||||||
|
barWidth: 0
|
||||||
|
})
|
||||||
|
let userCode = ref('')
|
||||||
|
let showRefresh = ref(true)
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
//加载页面
|
||||||
|
getPicture()
|
||||||
|
nextTick(() => {
|
||||||
|
let {imgHeight, imgWidth, barHeight, barWidth} = resetSize(proxy)
|
||||||
|
setSize.imgHeight = imgHeight
|
||||||
|
setSize.imgWidth = imgWidth
|
||||||
|
setSize.barHeight = barHeight
|
||||||
|
setSize.barWidth = barWidth
|
||||||
|
proxy.$parent.$emit('ready', proxy)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
// 禁止拖拽
|
||||||
|
init()
|
||||||
|
proxy.$el.onselectstart = function () {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const doCheck = () => {
|
||||||
|
if (!userCode.value) {
|
||||||
|
ElMessage.error(t('login.codePlaceholder'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//发送后端请求
|
||||||
|
const captchaVerification = backToken.value + '---' + userCode.value
|
||||||
|
let data = {
|
||||||
|
captchaType: captchaType.value,
|
||||||
|
pointJson: userCode.value,
|
||||||
|
token: backToken.value
|
||||||
|
}
|
||||||
|
reqCheck(data).then((res) => {
|
||||||
|
if (res.repCode === '0000') {
|
||||||
|
ElMessage.success(t('captcha.success'))
|
||||||
|
if (mode.value === 'pop') {
|
||||||
|
setTimeout(() => {
|
||||||
|
proxy.$parent.clickShow = false
|
||||||
|
refresh()
|
||||||
|
}, 1500)
|
||||||
|
}
|
||||||
|
proxy.$parent.$emit('success', {captchaVerification})
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.repMsg)
|
||||||
|
proxy.$parent.$emit('error', proxy)
|
||||||
|
setTimeout(() => {
|
||||||
|
refresh()
|
||||||
|
}, 700)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const refresh = async function () {
|
||||||
|
await getPicture()
|
||||||
|
showRefresh.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求背景图片和验证图片
|
||||||
|
const getPicture = async () => {
|
||||||
|
let data = {
|
||||||
|
captchaType: captchaType.value
|
||||||
|
}
|
||||||
|
const res = await getCode(data)
|
||||||
|
if (res.repCode === '0000') {
|
||||||
|
inputBackImgBase.value = res.repData.originalImageBase64
|
||||||
|
backToken.value = res.repData.token
|
||||||
|
secretKey.value = res.repData.secretKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import VerifySlide from './VerifySlide.vue'
|
import VerifySlide from './VerifySlide.vue'
|
||||||
import VerifyPoints from './VerifyPoints.vue'
|
import VerifyPoints from './VerifyPoints.vue'
|
||||||
|
import VerifyInput from './VerifyInput.vue'
|
||||||
|
|
||||||
export { VerifySlide, VerifyPoints }
|
export {VerifySlide, VerifyPoints, VerifyInput}
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ const { push } = useRouter()
|
||||||
const permissionStore = usePermissionStore()
|
const permissionStore = usePermissionStore()
|
||||||
const loginLoading = ref(false)
|
const loginLoading = ref(false)
|
||||||
const verify = ref()
|
const verify = ref()
|
||||||
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字
|
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块拼图 clickWord 点击文字 pictureWord 文本输入
|
||||||
|
|
||||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN)
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<Verify
|
<Verify
|
||||||
|
v-if="getShow && resetPasswordData.captchaEnable === 'true'"
|
||||||
ref="verify"
|
ref="verify"
|
||||||
:captchaType="captchaType"
|
:captchaType="captchaType"
|
||||||
:imgSize="{ width: '400px', height: '200px' }"
|
:imgSize="{ width: '400px', height: '200px' }"
|
||||||
|
|
@ -143,7 +144,7 @@ const iconCircleCheck = useIcon({ icon: 'ep:circle-check' })
|
||||||
const { validForm } = useFormValid(formSmsResetPassword)
|
const { validForm } = useFormValid(formSmsResetPassword)
|
||||||
const { handleBackLogin, getLoginState, setLoginState } = useLoginState()
|
const { handleBackLogin, getLoginState, setLoginState } = useLoginState()
|
||||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD)
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD)
|
||||||
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字
|
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块拼图 clickWord 点击文字 pictureWord 文本输入
|
||||||
|
|
||||||
const validatePass2 = (_rule, value, callback) => {
|
const validatePass2 = (_rule, value, callback) => {
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<Verify
|
<Verify
|
||||||
v-if="loginData.captchaEnable === 'true'"
|
v-if="getShow && loginData.captchaEnable === 'true'"
|
||||||
ref="verify"
|
ref="verify"
|
||||||
:captchaType="captchaType"
|
:captchaType="captchaType"
|
||||||
:imgSize="{ width: '400px', height: '200px' }"
|
:imgSize="{ width: '400px', height: '200px' }"
|
||||||
|
|
@ -177,7 +177,7 @@ const permissionStore = usePermissionStore()
|
||||||
const redirect = ref<string>('')
|
const redirect = ref<string>('')
|
||||||
const loginLoading = ref(false)
|
const loginLoading = ref(false)
|
||||||
const verify = ref()
|
const verify = ref()
|
||||||
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字
|
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块拼图 clickWord 点击文字 pictureWord 文本输入
|
||||||
|
|
||||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN)
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<Verify
|
<Verify
|
||||||
v-if="registerData.captchaEnable === 'true'"
|
v-if="getShow && registerData.captchaEnable === 'true'"
|
||||||
ref="verify"
|
ref="verify"
|
||||||
:captchaType="captchaType"
|
:captchaType="captchaType"
|
||||||
:imgSize="{ width: '400px', height: '200px' }"
|
:imgSize="{ width: '400px', height: '200px' }"
|
||||||
|
|
@ -119,7 +119,7 @@ const permissionStore = usePermissionStore()
|
||||||
const redirect = ref<string>('')
|
const redirect = ref<string>('')
|
||||||
const loginLoading = ref(false)
|
const loginLoading = ref(false)
|
||||||
const verify = ref()
|
const verify = ref()
|
||||||
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字
|
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块拼图 clickWord 点击文字 pictureWord 文本输入
|
||||||
|
|
||||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER)
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue