Pre Merge pull request !796 from DanielTsui/master-pictureWordCaptcha

pull/796/MERGE
DanielTsui 2025-08-04 01:04:47 +00:00 committed by Gitee
commit ae86b06cf8
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
6 changed files with 213 additions and 8 deletions

View File

@ -36,14 +36,15 @@
* Verify 验证码组件
* @description 分发验证码使用
* */
import { VerifyPoints, VerifySlide } from './Verify'
import {VerifyPictureWord, VerifyPoints, VerifySlide} from './Verify'
import { computed, ref, toRefs, watchEffect } from 'vue'
export default {
name: 'Vue3Verify',
components: {
VerifySlide,
VerifyPoints
VerifyPoints,
VerifyPictureWord
},
props: {
captchaType: {
@ -118,6 +119,10 @@ export default {
}
watchEffect(() => {
switch (captchaType.value) {
case 'pictureWord':
verifyType.value = '3'
componentType.value = 'VerifyPictureWord'
break
case 'blockPuzzle':
verifyType.value = '2'
componentType.value = 'VerifySlide'
@ -438,4 +443,4 @@ export default {
content: ' ';
inset: 0;
}
</style>
</style>

View File

@ -0,0 +1,194 @@
<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
@click="refresh"
ref="canvas"
:src="'data:image/png;base64,' + verificationCodeImg"
alt=""
style="display: block; width: 100%; height: 100%"
/>
</div>
</div>
<div
:style="{
width: setSize.imgWidth,
color: barAreaColor,
'border-color': barAreaBorderColor,
// 'line-height': barSize.height
}"
class="verify-bar-area"
>
<div class="verify-msg">{{ text }}</div>
<div :style="{
'line-height': barSize.height
}">
<input class="verify-input" type="text" v-model="userCode" />
</div>
<button type="button" class="verify-btn" @click="submit" :disabled="checking">{{ t('captcha.verify') }}</button>
</div>
</div>
</template>
<script setup type="text/babel">
/**
* VerifyPictureWord
* @description 输入文字
* */
import { resetSize } from '../utils/util';
import { aesEncrypt } from '../utils/ase';
import { getCode, reqCheck } from '@/api/login';
import { getCurrentInstance, nextTick, onMounted, reactive, ref, toRefs } from 'vue';
const props = defineProps({
//popfixed
mode: {
type: String,
default: 'fixed'
},
captchaType: {
type: String
},
//
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 secretKey = ref(''), //ase
userCode = ref(''), // pointJson
verificationCodeImg = ref(''), //
backToken = ref(''), //token
setSize = reactive({
imgHeight: 0,
imgWidth: 0,
barHeight: 0,
barWidth: 0
}),
text = ref(''),
barAreaColor = ref('#000'),
barAreaBorderColor = ref('#ddd'),
showRefresh = ref(true),
// bindingClick = ref(true)
checking = ref(false);
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 canvas = ref(null);
const submit = () => {
checking.value = true;
//
var captchaVerification = secretKey.value
? aesEncrypt(backToken.value + '---' + userCode.value, secretKey.value)
: backToken.value + '---' + userCode.value;
let data = {
captchaType: captchaType.value,
pointJson: userCode.value,
token: backToken.value
};
reqCheck(data).then((res) => {
if (res.repCode == '0000') {
barAreaColor.value = '#4cae4c';
barAreaBorderColor.value = '#5cb85c';
text.value = t('captcha.success');
// bindingClick.value = false
if (mode.value == 'pop') {
setTimeout(() => {
proxy.$parent.clickShow = false;
refresh();
}, 1500);
}
proxy.$parent.$emit('success', { captchaVerification });
} else {
proxy.$parent.$emit('error', proxy);
barAreaColor.value = '#d9534f';
barAreaBorderColor.value = '#d9534f';
text.value = t('captcha.fail');
setTimeout(() => {
refresh();
}, 700);
}
checking.value = false;
});
};
const refresh = async function() {
barAreaColor.value = '#000';
barAreaBorderColor.value = '#ddd';
checking.value = false;
userCode.value = '';
await getPicture();
showRefresh.value = true;
};
//
const getPicture = async () => {
let data = {
captchaType: captchaType.value
};
const res = await getCode(data);
if (res.repCode == '0000') {
verificationCodeImg.value = res.repData.originalImageBase64;
backToken.value = res.repData.token;
secretKey.value = res.repData.secretKey;
text.value = t('captcha.code');
} else {
text.value = res.repMsg;
}
};
</script>

View File

@ -1,4 +1,5 @@
import VerifySlide from './VerifySlide.vue'
import VerifyPoints from './VerifyPoints.vue'
import VerifyPictureWord from './VerifyPictureWord.vue'
export { VerifySlide, VerifyPoints }
export { VerifySlide, VerifyPoints, VerifyPictureWord }

View File

@ -146,9 +146,11 @@ export default {
invalidTenantName:"Invalid Tenant Name"
},
captcha: {
verify: 'Verify',
verification: 'Please complete security verification',
slide: 'Swipe right to complete verification',
point: 'Please click',
code: 'Please enter the verification code',
success: 'Verification succeeded',
fail: 'verification failed'
},
@ -457,4 +459,4 @@ export default {
btn_zoom_out: 'Zoom out',
preview: 'Preivew'
}
}
}

View File

@ -147,9 +147,11 @@ export default {
invalidTenantName: '无效的租户名称'
},
captcha: {
verify: '验证',
verification: '请完成安全验证',
slide: '向右滑动完成验证',
point: '请依次点击',
code: '请输入验证码',
success: '验证成功',
fail: '验证失败'
},
@ -453,4 +455,4 @@ export default {
preview: '预览'
},
'OAuth 2.0': 'OAuth 2.0' // 避免菜单名是 OAuth 2.0 时,一直 warn 报错
}
}

View File

@ -177,7 +177,8 @@ const permissionStore = usePermissionStore()
const redirect = ref<string>('')
const loginLoading = ref(false)
const verify = ref()
const captchaType = ref('blockPuzzle') // blockPuzzle clickWord
const captchaType = ref('pictureWord') // blockPuzzle clickWord pictureWord
// const captchaType = ref('blockPuzzle') // blockPuzzle clickWord pictureWord
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN)
@ -360,4 +361,4 @@ onMounted(() => {
cursor: pointer;
}
}
</style>
</style>