Compare commits
No commits in common. "f9b0b05c7ebbf7154f5235db95b50a0ae39e8605" and "4c2d41b6998a57b87f50a880d523a9ff23c8cd6b" have entirely different histories.
f9b0b05c7e
...
4c2d41b699
1
components.d.ts
vendored
1
components.d.ts
vendored
@ -7,7 +7,6 @@ export {}
|
|||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
declare module '@vue/runtime-core' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
CaptchaPanel: typeof import('./src/components/CaptchaPanel.vue')['default']
|
|
||||||
ElAside: typeof import('element-plus/es')['ElAside']
|
ElAside: typeof import('element-plus/es')['ElAside']
|
||||||
ElButton: typeof import('element-plus/es')['ElButton']
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
ElCard: typeof import('element-plus/es')['ElCard']
|
ElCard: typeof import('element-plus/es')['ElCard']
|
||||||
|
|||||||
@ -1,126 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div ref="captchaBoxRef" class="captcha-panel"></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed, nextTick, onBeforeUnmount, ref } from 'vue'
|
|
||||||
import {
|
|
||||||
createTianAiCaptcha,
|
|
||||||
type TianAiCaptchaConfig,
|
|
||||||
type TianAiCaptchaInstance,
|
|
||||||
type TianAiCaptchaResult,
|
|
||||||
type TianAiCaptchaStyle
|
|
||||||
} from '@/vendor/tianai-captcha'
|
|
||||||
|
|
||||||
type CaptchaMode = 'data' | 'validate'
|
|
||||||
type CaptchaType = 'RANDOM' | 'SLIDER' | 'ROTATE' | 'CONCAT' | 'WORD_IMAGE_CLICK'
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
|
||||||
mode?: CaptchaMode
|
|
||||||
type?: CaptchaType
|
|
||||||
validCaptchaUrl?: string
|
|
||||||
requestHeaders?: Record<string, string>
|
|
||||||
styleConfig?: TianAiCaptchaStyle
|
|
||||||
}>(), {
|
|
||||||
mode: 'data',
|
|
||||||
type: 'RANDOM'
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'close'): void
|
|
||||||
(e: 'data-ready', result: TianAiCaptchaResult): void
|
|
||||||
(e: 'init-error', error: Error): void
|
|
||||||
(e: 'state-change', active: boolean): void
|
|
||||||
(e: 'valid-fail', res: any): void
|
|
||||||
(e: 'valid-success', res: any): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const captchaBoxRef = ref<HTMLElement | null>(null)
|
|
||||||
const captchaInstance = ref<TianAiCaptchaInstance | null>(null)
|
|
||||||
const active = ref(false)
|
|
||||||
const requestCaptchaBaseUrl = buildCaptchaApiUrl('/captcha/gen')
|
|
||||||
const resolvedValidCaptchaUrl = computed(() => {
|
|
||||||
return props.validCaptchaUrl || buildCaptchaApiUrl('/captcha/check')
|
|
||||||
})
|
|
||||||
const requestCaptchaDataUrl = computed(() => {
|
|
||||||
if (props.type === 'RANDOM') {
|
|
||||||
return requestCaptchaBaseUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${requestCaptchaBaseUrl}?type=${encodeURIComponent(props.type)}`
|
|
||||||
})
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
if (!captchaBoxRef.value) {
|
|
||||||
emit('init-error', new Error('验证码容器初始化失败'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy()
|
|
||||||
|
|
||||||
const config: TianAiCaptchaConfig = {
|
|
||||||
bindEl: captchaBoxRef.value,
|
|
||||||
requestCaptchaDataUrl: requestCaptchaDataUrl.value,
|
|
||||||
requestHeaders: props.requestHeaders,
|
|
||||||
btnRefreshFun: (_el, tac) => {
|
|
||||||
tac.reloadCaptcha()
|
|
||||||
},
|
|
||||||
btnCloseFun: () => {
|
|
||||||
destroy()
|
|
||||||
emit('close')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (props.mode) {
|
|
||||||
case 'validate':
|
|
||||||
config.validCaptchaUrl = resolvedValidCaptchaUrl.value
|
|
||||||
config.validSuccess = res => emit('valid-success', res)
|
|
||||||
config.validFail = res => emit('valid-fail', res)
|
|
||||||
break
|
|
||||||
case 'data':
|
|
||||||
config.onDataReady = result => emit('data-ready', result)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
emit('init-error', new Error(`未知的验证码模式: ${props.mode}`))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
captchaInstance.value = createTianAiCaptcha(config, props.styleConfig)
|
|
||||||
captchaInstance.value.init()
|
|
||||||
setActive(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
function reload() {
|
|
||||||
captchaInstance.value?.reloadCaptcha()
|
|
||||||
}
|
|
||||||
|
|
||||||
function destroy() {
|
|
||||||
if (captchaInstance.value) {
|
|
||||||
captchaInstance.value.destroyWindow()
|
|
||||||
captchaInstance.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
setActive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
function setActive(nextValue: boolean) {
|
|
||||||
if (active.value === nextValue) return
|
|
||||||
active.value = nextValue
|
|
||||||
emit('state-change', nextValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
destroy,
|
|
||||||
init,
|
|
||||||
reload,
|
|
||||||
})
|
|
||||||
|
|
||||||
function buildCaptchaApiUrl(path: string) {
|
|
||||||
const normalizedBase = (import.meta.env.VITE_APP_API_BASE || '').replace(/\/+$/, '')
|
|
||||||
const normalizedPath = path.startsWith('/') ? path : `/${path}`
|
|
||||||
return `${normalizedBase}${normalizedPath}`
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeUnmount(() => destroy())
|
|
||||||
</script>
|
|
||||||
55
src/vendor/tianai-captcha/captcha/captcha.js
vendored
55
src/vendor/tianai-captcha/captcha/captcha.js
vendored
@ -8,24 +8,27 @@ import WordImageClick from "./word_image_click/word_image_click";
|
|||||||
import { CaptchaConfig, wrapConfig, wrapStyle } from "./config/config";
|
import { CaptchaConfig, wrapConfig, wrapStyle } from "./config/config";
|
||||||
import { clearAllPreventDefault } from "./common/common";
|
import { clearAllPreventDefault } from "./common/common";
|
||||||
const template = `
|
const template = `
|
||||||
<div class="__captcha-parent">
|
<div id="tianai-captcha-parent">
|
||||||
<div class="captcha-box">
|
<div id="tianai-captcha-bg-img"></div>
|
||||||
<div class="captcha-loading loading"></div>
|
<div id="tianai-captcha-box">
|
||||||
|
<div id="tianai-captcha-loading" class="loading"></div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 底部 -->
|
<!-- 底部 -->
|
||||||
<div class="slider-bottom">
|
<div class="slider-bottom">
|
||||||
<div class="refresh-btn"></div>
|
<img class="logo" id="tianai-captcha-logo" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAMAAAAM7l6QAAAAMFBMVEVHcEz3tkX3tkX3tkX3tkX3tkX3tkX3tkX3tkX3tkX3tkX3tkX3tkX3tkX3tkX3tkVmTmjZAAAAD3RSTlMASbTm8wh12hOGoCNiyTV98jvOAAABB0lEQVR42nVT0aIFEQiMorD0/397Lc5a7J0n1UylgIniLRKyDcbBDudZH2DYCAabn3PmTrjeUX+7rJGWx0SqVpzReAfTtKU5fgVCNfxWjB69USUDGwoOiauHpZEpSr0tCx8ILb3Dm3WgBbAlifAJk6+Ww6wqEUmpmIorQVZ1JtqKnDMjkb7AgIpO/wMCaQbuBuEtsBUxhuD9daUaZnApiQB8NAKotMwirGGr6mbXpPnHLHDmy6oy3FgP+1j8IBdVklFc01xUJwv3NR0rIeXV5zpzdlruiijzNq/ufOeKWzZLP3160u5P8RjT1M+HHFtx+PwGyOZqT/D8ROOfjOInTLBIHjy/hvwHxkwPu5cCE1QAAAAASUVORK5CYII=" id="tianai-captcha-logo"></img>
|
||||||
<div class="close-btn"></div>
|
<div class="close-btn" id="tianai-captcha-slider-close-btn"></div>
|
||||||
|
<div class="refresh-btn" id="tianai-captcha-slider-refresh-btn"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
function createCaptchaByType(type, tac) {
|
function createCaptchaByType(type, tac) {
|
||||||
const box = tac.config.domBindEl.find(".captcha-box");
|
const box = tac.config.domBindEl.find("#tianai-captcha-box");
|
||||||
const styleConfig = tac.style;
|
const styleConfig = tac.style;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "SLIDER":
|
case "SLIDER":
|
||||||
return new Slider(box, styleConfig);
|
return new Slider(box, styleConfig);
|
||||||
case "ROTATE":
|
case "ROTATE":
|
||||||
|
case "ROTATE_DEGREE":
|
||||||
return new Rotate(box, styleConfig);
|
return new Rotate(box, styleConfig);
|
||||||
case "CONCAT":
|
case "CONCAT":
|
||||||
return new Concat(box, styleConfig);
|
return new Concat(box, styleConfig);
|
||||||
@ -54,16 +57,17 @@ class TianAiCaptcha {
|
|||||||
init() {
|
init() {
|
||||||
this.destroyWindow();
|
this.destroyWindow();
|
||||||
this.config.domBindEl.append(template);
|
this.config.domBindEl.append(template);
|
||||||
this.domTemplate = this.config.domBindEl.find(".__captcha-parent");
|
this.domTemplate = this.config.domBindEl.find("#tianai-captcha-parent");
|
||||||
clearAllPreventDefault(this.domTemplate);
|
clearAllPreventDefault(this.domTemplate);
|
||||||
|
this.loadStyle();
|
||||||
// 绑定按钮事件
|
// 绑定按钮事件
|
||||||
this.config.domBindEl
|
this.config.domBindEl
|
||||||
.find(".refresh-btn")
|
.find("#tianai-captcha-slider-refresh-btn")
|
||||||
.click((el) => {
|
.click((el) => {
|
||||||
this.btnRefreshFun(el, this);
|
this.btnRefreshFun(el, this);
|
||||||
});
|
});
|
||||||
this.config.domBindEl
|
this.config.domBindEl
|
||||||
.find(".close-btn")
|
.find("#tianai-captcha-slider-close-btn")
|
||||||
.click((el) => {
|
.click((el) => {
|
||||||
this.btnCloseFun(el, this);
|
this.btnCloseFun(el, this);
|
||||||
});
|
});
|
||||||
@ -86,16 +90,35 @@ class TianAiCaptcha {
|
|||||||
}
|
}
|
||||||
showLoading() {
|
showLoading() {
|
||||||
this.config.domBindEl
|
this.config.domBindEl
|
||||||
.find(".captcha-loading")
|
.find("#tianai-captcha-loading")
|
||||||
.css("display", "block");
|
.css("display", "block");
|
||||||
}
|
}
|
||||||
|
|
||||||
closeLoading() {
|
closeLoading() {
|
||||||
this.config.domBindEl
|
this.config.domBindEl
|
||||||
.find(".captcha-loading")
|
.find("#tianai-captcha-loading")
|
||||||
.css("display", "none");
|
.css("display", "none");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadStyle() {
|
||||||
|
// 设置样式
|
||||||
|
const bgUrl = this.style.bgUrl;
|
||||||
|
const logoUrl = this.style.logoUrl;
|
||||||
|
if (bgUrl) {
|
||||||
|
// 背景图片
|
||||||
|
this.config.domBindEl
|
||||||
|
.find("#tianai-captcha-bg-img")
|
||||||
|
.css("background-image", "url(" + bgUrl + ")");
|
||||||
|
}
|
||||||
|
if (logoUrl && logoUrl !== "") {
|
||||||
|
// logo
|
||||||
|
this.config.domBindEl.find("#tianai-captcha-logo").attr("src", logoUrl);
|
||||||
|
} else if (logoUrl === null) {
|
||||||
|
// 删除logo
|
||||||
|
this.config.domBindEl.find("#tianai-captcha-logo").css("display", "none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
destroyWindow() {
|
destroyWindow() {
|
||||||
if (this.C) {
|
if (this.C) {
|
||||||
this.C.destroy();
|
this.C.destroy();
|
||||||
@ -144,12 +167,6 @@ class TianAiCaptcha {
|
|||||||
// 清空
|
// 清空
|
||||||
const id = c.currentCaptchaData.currentCaptchaId;
|
const id = c.currentCaptchaData.currentCaptchaId;
|
||||||
c.currentCaptchaData = undefined;
|
c.currentCaptchaData = undefined;
|
||||||
if (this.config.onDataReady) {
|
|
||||||
Promise.resolve(this.config.onDataReady({ id, data }, c, this)).catch((error) => {
|
|
||||||
console.error("[TAC] onDataReady callback failed", error);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 调用验证接口
|
// 调用验证接口
|
||||||
this.config.validCaptcha(id, data, c, this);
|
this.config.validCaptcha(id, data, c, this);
|
||||||
});
|
});
|
||||||
@ -163,7 +180,9 @@ class TianAiCaptcha {
|
|||||||
this.C.el.css("transform", "translateX(300px)");
|
this.C.el.css("transform", "translateX(300px)");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.C.destroy();
|
this.C.destroy();
|
||||||
callback && callback();
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
|
|||||||
69
src/vendor/tianai-captcha/captcha/captcha.less
vendored
69
src/vendor/tianai-captcha/captcha/captcha.less
vendored
@ -1,13 +1,14 @@
|
|||||||
.__captcha-parent {
|
#tianai-captcha-parent {
|
||||||
box-shadow: 0 0 11px 0 #999999;
|
box-shadow: 0 0 11px 0 #999999;
|
||||||
width: 316px;
|
width: 318px;
|
||||||
|
height: 318px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 997;
|
z-index: 997;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
.captcha-box {
|
#tianai-captcha-box {
|
||||||
height: 260px;
|
height: 260px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -25,38 +26,56 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.captcha {
|
#tianai-captcha {
|
||||||
transform-style: preserve-3d;
|
transform-style: preserve-3d;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
transition-duration: 0.45s;
|
transition-duration: 0.45s;
|
||||||
transform: translateX(-100%);
|
transform: translateX(-300px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#tianai-captcha-bg-img {
|
||||||
|
background-color: #fff;
|
||||||
|
background-position: top;
|
||||||
|
background-size: cover;
|
||||||
|
z-index: -1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.slider-bottom {
|
.slider-bottom {
|
||||||
text-align: right;
|
.close-btn {
|
||||||
margin-top: 8px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 10px;
|
|
||||||
.close-btn, .refresh-btn {
|
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
background-image: url("../images/icon.png");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
background-position: 0 -14px;
|
||||||
|
float: right;
|
||||||
|
margin-right: 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.close-btn {
|
|
||||||
background-image: url("../images/icon.png");
|
|
||||||
background-position: 0 -14px;
|
|
||||||
}
|
|
||||||
.refresh-btn {
|
.refresh-btn {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
background-image: url("../images/icon.png");
|
background-image: url("../images/icon.png");
|
||||||
background-position: 0 -167px;
|
background-position: 0 -167px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
float: right;
|
||||||
|
margin-right: 10px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.logo {
|
||||||
|
height: 30px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
height: 19px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
.slider-move-shadow {
|
.slider-move-shadow {
|
||||||
animation: move-shadow 2s infinite;
|
animation: myanimation 2s infinite;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 5px;
|
width: 5px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
@ -67,7 +86,7 @@
|
|||||||
box-shadow: 1px 1px 1px #fff;
|
box-shadow: 1px 1px 1px #fff;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
.captcha-slider-move-track-mask {
|
#tianai-captcha-slider-move-track-mask {
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-color: #00f4ab;
|
border-color: #00f4ab;
|
||||||
@ -81,18 +100,4 @@
|
|||||||
left: -1px;
|
left: -1px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
@keyframes move-shadow {
|
|
||||||
from {
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
left: 289px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@import "./common/common.less";
|
|
||||||
@import "./slider/slider.less";
|
|
||||||
@import "./rotate/rotate.less";
|
|
||||||
@import "./concat/concat.less";
|
|
||||||
@import "./disable/disable.less";
|
|
||||||
@import "./image_click/image_click.less";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -191,10 +191,10 @@ function initConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function closeTips(el, callback) {
|
function closeTips(el, callback) {
|
||||||
const tipEl = Dom(el).find(".captcha-tips");
|
const tipEl = Dom(el).find("#tianai-captcha-tips");
|
||||||
tipEl.removeClass("captcha-tips-on");
|
tipEl.removeClass("tianai-captcha-tips-on");
|
||||||
// tipEl.removeClass("captcha-tips-success")
|
// tipEl.removeClass("tianai-captcha-tips-success")
|
||||||
// tipEl.removeClass("captcha-tips-error")
|
// tipEl.removeClass("tianai-captcha-tips-error")
|
||||||
// 延时
|
// 延时
|
||||||
if (callback) {
|
if (callback) {
|
||||||
setTimeout(callback, 0.35);
|
setTimeout(callback, 0.35);
|
||||||
@ -202,18 +202,18 @@ function closeTips(el, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showTips(el, msg, type, callback) {
|
function showTips(el, msg, type, callback) {
|
||||||
const tipEl = Dom(el).find(".captcha-tips");
|
const tipEl = Dom(el).find("#tianai-captcha-tips");
|
||||||
tipEl.text(msg);
|
tipEl.text(msg);
|
||||||
if (type === 1) {
|
if (type === 1) {
|
||||||
// 成功
|
// 成功
|
||||||
tipEl.removeClass("captcha-tips-error");
|
tipEl.removeClass("tianai-captcha-tips-error");
|
||||||
tipEl.addClass("captcha-tips-success");
|
tipEl.addClass("tianai-captcha-tips-success");
|
||||||
} else {
|
} else {
|
||||||
// 失败
|
// 失败
|
||||||
tipEl.removeClass("captcha-tips-success");
|
tipEl.removeClass("tianai-captcha-tips-success");
|
||||||
tipEl.addClass("captcha-tips-error");
|
tipEl.addClass("tianai-captcha-tips-error");
|
||||||
}
|
}
|
||||||
tipEl.addClass("captcha-tips-on");
|
tipEl.addClass("tianai-captcha-tips-on");
|
||||||
// 延时
|
// 延时
|
||||||
setTimeout(callback, 1000);
|
setTimeout(callback, 1000);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.captcha {
|
#tianai-captcha {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
width: 100%;
|
width: 300px;
|
||||||
height: 260px;
|
height: 260px;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
.slider-bottom .logo {
|
.slider-bottom .logo {
|
||||||
@ -12,7 +12,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
.captcha-tips {
|
.tianai-captcha-tips {
|
||||||
height: 25px;
|
height: 25px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -21,21 +21,25 @@
|
|||||||
z-index: 999;
|
z-index: 999;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
|
/*background-color: #FF5D39;*/
|
||||||
color: #fff;
|
color: #fff;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
/* transform: translateY(0px); */
|
||||||
|
/* display: none; */
|
||||||
|
/* transition: max-height 0.5s; */
|
||||||
transition: bottom 0.3s ease-in-out;
|
transition: bottom 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
.captcha-tips.captcha-tips-error {
|
.tianai-captcha-tips.tianai-captcha-tips-error {
|
||||||
background-color: #ff5d39;
|
background-color: #ff5d39;
|
||||||
}
|
}
|
||||||
.captcha-tips.captcha-tips-success {
|
.tianai-captcha-tips.tianai-captcha-tips-success {
|
||||||
background-color: #39c522;
|
background-color: #39c522;
|
||||||
}
|
}
|
||||||
.captcha-tips.captcha-tips-on {
|
.tianai-captcha-tips.tianai-captcha-tips-on {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha-loading {
|
#tianai-captcha-loading {
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -52,7 +56,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.captcha-slider-bg-canvas {
|
#tianai-captcha-slider-bg-canvas {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -60,14 +64,23 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
.captcha-slider-bg-div {
|
#tianai-captcha-slider-bg-div {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.captcha-slider-bg-div-slice {
|
border-radius: 5px;
|
||||||
|
.tianai-captcha-slider-bg-div-slice {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@keyframes myanimation {
|
||||||
|
from {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
left: 289px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
import "../common/common.less";
|
||||||
|
import "../slider/slider.less";
|
||||||
|
import "./concat.less";
|
||||||
import {
|
import {
|
||||||
Dom,
|
Dom,
|
||||||
CommonCaptcha,
|
CommonCaptcha,
|
||||||
@ -9,23 +12,25 @@ import {
|
|||||||
|
|
||||||
const TYPE = "CONCAT";
|
const TYPE = "CONCAT";
|
||||||
|
|
||||||
function getTemplate() {
|
function getTemplate(styleConfig) {
|
||||||
return `
|
return `
|
||||||
<div class="captcha captcha-slider captcha-concat">
|
<div id="tianai-captcha" class="tianai-captcha-slider tianai-captcha-concat">
|
||||||
<div class="slider-tip">
|
<div class="slider-tip">
|
||||||
<span class="captcha-slider-move-track-font" >拖动滑块完成拼图</span>
|
<span id="tianai-captcha-slider-move-track-font" >拖动滑块完成拼图</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="captcha-slider-concat-img-div"></div>
|
<div class="tianai-captcha-slider-concat-img-div" id="tianai-captcha-slider-concat-img-div">
|
||||||
<div class="captcha-slider-concat-bg-img"></div>
|
<img id="tianai-captcha-slider-concat-slider-img" src="" alt/>
|
||||||
<div class="captcha-tips"></div>
|
</div>
|
||||||
|
<div class="tianai-captcha-slider-concat-bg-img"></div>
|
||||||
|
<div class="tianai-captcha-tips" id="tianai-captcha-tips"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-move">
|
<div class="slider-move">
|
||||||
<div class="slider-move-track">
|
<div class="slider-move-track">
|
||||||
<div class="captcha-slider-move-track-mask"></div>
|
<div id="tianai-captcha-slider-move-track-mask"></div>
|
||||||
<div class="slider-move-shadow"></div>
|
<div class="slider-move-shadow"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-move-btn captcha-slider-move-btn">
|
<div class="slider-move-btn" id="tianai-captcha-slider-move-btn">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -44,15 +49,15 @@ class Concat extends CommonCaptcha {
|
|||||||
init(captchaData, endCallback, loadSuccessCallback) {
|
init(captchaData, endCallback, loadSuccessCallback) {
|
||||||
// 重载样式
|
// 重载样式
|
||||||
this.destroy();
|
this.destroy();
|
||||||
this.boxEl.append(getTemplate());
|
this.boxEl.append(getTemplate(this.styleConfig));
|
||||||
this.el = this.boxEl.find(".captcha");
|
this.el = this.boxEl.find("#tianai-captcha");
|
||||||
this.loadStyle();
|
this.loadStyle();
|
||||||
// 按钮绑定事件
|
// 按钮绑定事件
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-btn")
|
.find("#tianai-captcha-slider-move-btn")
|
||||||
.mousedown(down.bind(null, this));
|
.mousedown(down.bind(null, this));
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-btn")
|
.find("#tianai-captcha-slider-move-btn")
|
||||||
.touchstart(down.bind(null, this));
|
.touchstart(down.bind(null, this));
|
||||||
clearAllPreventDefault(this.el);
|
clearAllPreventDefault(this.el);
|
||||||
// 绑定全局
|
// 绑定全局
|
||||||
@ -69,7 +74,7 @@ class Concat extends CommonCaptcha {
|
|||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
destroyEvent();
|
destroyEvent();
|
||||||
const existsCaptchaEl = this.boxEl.children(".captcha");
|
const existsCaptchaEl = this.boxEl.children("#tianai-captcha");
|
||||||
if (existsCaptchaEl) {
|
if (existsCaptchaEl) {
|
||||||
existsCaptchaEl.remove();
|
existsCaptchaEl.remove();
|
||||||
}
|
}
|
||||||
@ -78,13 +83,13 @@ class Concat extends CommonCaptcha {
|
|||||||
doMove() {
|
doMove() {
|
||||||
const moveX = this.currentCaptchaData.moveX;
|
const moveX = this.currentCaptchaData.moveX;
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-btn")
|
.find("#tianai-captcha-slider-move-btn")
|
||||||
.css("transform", "translate(" + moveX + "px, 0px)");
|
.css("transform", "translate(" + moveX + "px, 0px)");
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-concat-img-div")
|
.find("#tianai-captcha-slider-concat-img-div")
|
||||||
.css("background-position-x", moveX + "px");
|
.css("background-position-x", moveX + "px");
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-track-mask")
|
.find("#tianai-captcha-slider-move-track-mask")
|
||||||
.css("width", moveX + "px");
|
.css("width", moveX + "px");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,18 +106,18 @@ class Concat extends CommonCaptcha {
|
|||||||
this.el
|
this.el
|
||||||
.find(".slider-move .slider-move-btn")
|
.find(".slider-move .slider-move-btn")
|
||||||
.css("background-image", "url(" + sliderImg + ")");
|
.css("background-image", "url(" + sliderImg + ")");
|
||||||
// this.el.find(".captcha-slider-move-track-font").text(title);
|
// this.el.find("#tianai-captcha-slider-move-track-font").text(title);
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-track-mask")
|
.find("#tianai-captcha-slider-move-track-mask")
|
||||||
.css("border-color", moveTrackMaskBorderColor);
|
.css("border-color", moveTrackMaskBorderColor);
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-track-mask")
|
.find("#tianai-captcha-slider-move-track-mask")
|
||||||
.css("background-color", moveTrackMaskBgColor);
|
.css("background-color", moveTrackMaskBgColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadCaptchaForData(that, data) {
|
loadCaptchaForData(that, data) {
|
||||||
const bgImg = that.el.find(".captcha-slider-concat-bg-img");
|
const bgImg = that.el.find(".tianai-captcha-slider-concat-bg-img");
|
||||||
const sliderImg = that.el.find(".captcha-slider-concat-img-div");
|
const sliderImg = that.el.find("#tianai-captcha-slider-concat-img-div");
|
||||||
bgImg.css("background-image", "url(" + data.data.backgroundImage + ")");
|
bgImg.css("background-image", "url(" + data.data.backgroundImage + ")");
|
||||||
sliderImg.css("background-image", "url(" + data.data.backgroundImage + ")");
|
sliderImg.css("background-image", "url(" + data.data.backgroundImage + ")");
|
||||||
sliderImg.css("background-position", "0px 0px");
|
sliderImg.css("background-position", "0px 0px");
|
||||||
|
|||||||
@ -1,25 +1,13 @@
|
|||||||
.captcha.captcha-concat {
|
#tianai-captcha.tianai-captcha-concat {
|
||||||
.captcha-slider-concat-img-div {
|
.tianai-captcha-slider-concat-img-div {
|
||||||
background-size: 100% 180px;
|
background-size: 100% 180px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translate(0px, 0px);
|
transform: translate(0px, 0px);
|
||||||
|
/* border-bottom: 1px solid blue; */
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
&::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
height: 1px;
|
|
||||||
background: rgba(255, 255, 255, 0.92);
|
|
||||||
box-shadow:
|
|
||||||
0 0 6px 3px rgb(255 255 255 / 60%),
|
|
||||||
0 1px 4px rgb(0 0 0 / 90%);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.captcha-slider-concat-bg-img {
|
.tianai-captcha-slider-concat-bg-img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@ -8,7 +8,7 @@ class CaptchaConfig {
|
|||||||
if (!args.requestCaptchaDataUrl) {
|
if (!args.requestCaptchaDataUrl) {
|
||||||
throw new Error("[TAC] 必须配置 [requestCaptchaDataUrl]请求验证码接口");
|
throw new Error("[TAC] 必须配置 [requestCaptchaDataUrl]请求验证码接口");
|
||||||
}
|
}
|
||||||
if (!args.validCaptchaUrl && !args.onDataReady) {
|
if (!args.validCaptchaUrl) {
|
||||||
throw new Error("[TAC] 必须配置 [validCaptchaUrl]验证验证码接口");
|
throw new Error("[TAC] 必须配置 [validCaptchaUrl]验证验证码接口");
|
||||||
}
|
}
|
||||||
this.bindEl = args.bindEl;
|
this.bindEl = args.bindEl;
|
||||||
@ -21,9 +21,6 @@ class CaptchaConfig {
|
|||||||
if (args.validFail) {
|
if (args.validFail) {
|
||||||
this.validFail = args.validFail;
|
this.validFail = args.validFail;
|
||||||
}
|
}
|
||||||
if (args.onDataReady) {
|
|
||||||
this.onDataReady = args.onDataReady;
|
|
||||||
}
|
|
||||||
if (args.requestHeaders) {
|
if (args.requestHeaders) {
|
||||||
this.requestHeaders = args.requestHeaders;
|
this.requestHeaders = args.requestHeaders;
|
||||||
} else {
|
} else {
|
||||||
@ -195,6 +192,21 @@ function wrapConfig(config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function wrapStyle(style) {
|
function wrapStyle(style) {
|
||||||
|
// if (!style) {
|
||||||
|
// style = {}
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!style.btnUrl) {
|
||||||
|
// // 设置默认图片
|
||||||
|
// style.btnUrl = "data:image/png;base64,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";
|
||||||
|
// }
|
||||||
|
// if (!style.moveTrackMaskBgColor && !style.moveTrackMaskBorderColor) {
|
||||||
|
// style.moveTrackMaskBgColor = "#89d2ff";
|
||||||
|
// style.moveTrackMaskBorderColor = "#0298f8";
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// return style;
|
||||||
|
|
||||||
let margeStyle = { ...StyleConfig, ...style };
|
let margeStyle = { ...StyleConfig, ...style };
|
||||||
margeStyle.i18n = { ...StyleConfig.i18n, ...style?.i18n };
|
margeStyle.i18n = { ...StyleConfig.i18n, ...style?.i18n };
|
||||||
return margeStyle;
|
return margeStyle;
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
const TYPE = "DISABLE";
|
const TYPE = "DISABLE";
|
||||||
|
import "./disable.less";
|
||||||
|
|
||||||
function getTemplate(styleConfig) {
|
function getTemplate(styleConfig) {
|
||||||
return `
|
return `
|
||||||
<div class="captcha captcha-disable">
|
<div id="tianai-captcha" class="tianai-captcha-disable">
|
||||||
<div class="slider-tip">
|
<div class="slider-tip">
|
||||||
<span class="captcha-slider-move-track-font" style="font-size: ${styleConfig.i18n.disable_title_size}">${styleConfig.i18n.disable_title}</span>
|
<span id="tianai-captcha-slider-move-track-font" style="font-size: ${styleConfig.i18n.disable_title_size}">${styleConfig.i18n.disable_title}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="bg-img-div">
|
<div class="bg-img-div">
|
||||||
@ -12,7 +13,7 @@ function getTemplate(styleConfig) {
|
|||||||
<!-- <polygon points="50,10 90,90 10,90" fill="none" stroke="#FF9900" stroke-width="4"/>-->
|
<!-- <polygon points="50,10 90,90 10,90" fill="none" stroke="#FF9900" stroke-width="4"/>-->
|
||||||
<!-- <path d="M50 35V65 M50 75V75" stroke="#FF9900" stroke-width="4" stroke-linecap="round"/>-->
|
<!-- <path d="M50 35V65 M50 75V75" stroke="#FF9900" stroke-width="4" stroke-linecap="round"/>-->
|
||||||
<!-- </svg>-->
|
<!-- </svg>-->
|
||||||
<span class="content-span"></span>
|
<span id="content-span"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -29,7 +30,7 @@ class Disable {
|
|||||||
// 重载样式
|
// 重载样式
|
||||||
this.destroy();
|
this.destroy();
|
||||||
this.boxEl.append(getTemplate(this.styleConfig));
|
this.boxEl.append(getTemplate(this.styleConfig));
|
||||||
this.el = this.boxEl.find(".captcha");
|
this.el = this.boxEl.find("#tianai-captcha");
|
||||||
// 绑定全局
|
// 绑定全局
|
||||||
// window.currentCaptcha = this;
|
// window.currentCaptcha = this;
|
||||||
// 载入验证码
|
// 载入验证码
|
||||||
@ -43,14 +44,14 @@ class Disable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
const existsCaptchaEl = this.boxEl.find(".captcha");
|
const existsCaptchaEl = this.boxEl.find("#tianai-captcha");
|
||||||
if (existsCaptchaEl) {
|
if (existsCaptchaEl) {
|
||||||
existsCaptchaEl.remove();
|
existsCaptchaEl.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadCaptchaForData(that, data) {
|
loadCaptchaForData(that, data) {
|
||||||
const msg = data.msg || data.message || "接口异常";
|
const msg = data.msg || data.message || "接口异常";
|
||||||
that.el.find(".content-span").text(msg);
|
that.el.find("#content-span").text(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
.captcha.captcha-disable {
|
#tianai-captcha.tianai-captcha-disable {
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -13,7 +13,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.content-span {
|
#content-span {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-top: 132px;
|
margin-top: 132px;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import "./image_click.less";
|
||||||
import {
|
import {
|
||||||
CommonCaptcha,
|
CommonCaptcha,
|
||||||
move,
|
move,
|
||||||
@ -12,18 +13,18 @@ import {
|
|||||||
const TYPE = "IMAGE_CLICK";
|
const TYPE = "IMAGE_CLICK";
|
||||||
function getTemplate(styleConfig) {
|
function getTemplate(styleConfig) {
|
||||||
return `
|
return `
|
||||||
<div class="captcha captcha-slider captcha-word-click">
|
<div id="tianai-captcha" class="tianai-captcha-slider tianai-captcha-word-click">
|
||||||
<div class="click-tip">
|
<div class="click-tip">
|
||||||
<span class="captcha-click-track-font" style="font-size: ${styleConfig.i18n.image_click_title_size}">${styleConfig.i18n.image_click_title}</span>
|
<span id="tianai-captcha-click-track-font" style="font-size: ${styleConfig.i18n.image_click_title_size}">${styleConfig.i18n.image_click_title}</span>
|
||||||
<img src="" class="captcha-tip-img tip-img">
|
<img src="" id="tianai-captcha-tip-img" class="tip-img">
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="bg-img-div">
|
<div class="bg-img-div">
|
||||||
<img class="captcha-slider-bg-img" src="" alt/>
|
<img id="tianai-captcha-slider-bg-img" src="" alt/>
|
||||||
<canvas class="captcha-slider-bg-canvas"></canvas>
|
<canvas id="tianai-captcha-slider-bg-canvas"></canvas>
|
||||||
<div class="bg-img-click-mask"></div>
|
<div id="bg-img-click-mask"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="captcha-tips"></div>
|
<div class="tianai-captcha-tips" id="tianai-captcha-tips"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="click-confirm-btn">确定</div>
|
<div class="click-confirm-btn">确定</div>
|
||||||
</div>
|
</div>
|
||||||
@ -41,7 +42,7 @@ class ImageClick extends CommonCaptcha {
|
|||||||
// 重载样式
|
// 重载样式
|
||||||
this.destroy();
|
this.destroy();
|
||||||
this.boxEl.append(getTemplate(this.styleConfig));
|
this.boxEl.append(getTemplate(this.styleConfig));
|
||||||
this.el = this.boxEl.find(".captcha");
|
this.el = this.boxEl.find("#tianai-captcha");
|
||||||
// 绑定全局
|
// 绑定全局
|
||||||
// window.currentCaptcha = this;
|
// window.currentCaptcha = this;
|
||||||
// 载入验证码
|
// 载入验证码
|
||||||
@ -49,7 +50,7 @@ class ImageClick extends CommonCaptcha {
|
|||||||
this.endCallback = endCallback;
|
this.endCallback = endCallback;
|
||||||
const moveFun = move.bind(null, this);
|
const moveFun = move.bind(null, this);
|
||||||
// 绑定事件
|
// 绑定事件
|
||||||
this.el.find(".bg-img-click-mask").click((event) => {
|
this.el.find("#bg-img-click-mask").click((event) => {
|
||||||
if (event.target.className === "click-span") {
|
if (event.target.className === "click-span") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -72,7 +73,7 @@ class ImageClick extends CommonCaptcha {
|
|||||||
const left = event.offsetX - 10;
|
const left = event.offsetX - 10;
|
||||||
const top = event.offsetY - 10;
|
const top = event.offsetY - 10;
|
||||||
this.el
|
this.el
|
||||||
.find(".bg-img-click-mask")
|
.find("#bg-img-click-mask")
|
||||||
.append(
|
.append(
|
||||||
"<span class='click-span' style='left:" +
|
"<span class='click-span' style='left:" +
|
||||||
left +
|
left +
|
||||||
@ -105,15 +106,15 @@ class ImageClick extends CommonCaptcha {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
destroy() {
|
destroy() {
|
||||||
const existsCaptchaEl = this.boxEl.children(".captcha");
|
const existsCaptchaEl = this.boxEl.children("#tianai-captcha");
|
||||||
if (existsCaptchaEl) {
|
if (existsCaptchaEl) {
|
||||||
existsCaptchaEl.remove();
|
existsCaptchaEl.remove();
|
||||||
}
|
}
|
||||||
destroyEvent();
|
destroyEvent();
|
||||||
}
|
}
|
||||||
loadCaptchaForData(that, data) {
|
loadCaptchaForData(that, data) {
|
||||||
const bgImg = that.el.find(".captcha-slider-bg-img");
|
const bgImg = that.el.find("#tianai-captcha-slider-bg-img");
|
||||||
const tipImg = that.el.find(".captcha-tip-img");
|
const tipImg = that.el.find("#tianai-captcha-tip-img");
|
||||||
bgImg.on("load", () => {
|
bgImg.on("load", () => {
|
||||||
that.currentCaptchaData = initConfig(
|
that.currentCaptchaData = initConfig(
|
||||||
bgImg.width(),
|
bgImg.width(),
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
.captcha.captcha-word-click {
|
#tianai-captcha.tianai-captcha-word-click {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
.click-tip {
|
.click-tip {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -9,7 +9,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
}
|
}
|
||||||
.captcha-click-track-font {
|
#tianai-captcha-click-track-font {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@ -22,7 +22,7 @@
|
|||||||
top: 6px;
|
top: 6px;
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
.bg-img-click-mask {
|
#bg-img-click-mask {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import "../slider/slider.less";
|
||||||
|
import "./rotate.less";
|
||||||
import {
|
import {
|
||||||
CommonCaptcha,
|
CommonCaptcha,
|
||||||
down,
|
down,
|
||||||
@ -12,26 +14,26 @@ import {
|
|||||||
const TYPE = "ROTATE";
|
const TYPE = "ROTATE";
|
||||||
function getTemplate(styleConfig) {
|
function getTemplate(styleConfig) {
|
||||||
return `
|
return `
|
||||||
<div class="captcha captcha-slider captcha-rotate">
|
<div id="tianai-captcha" class="tianai-captcha-slider tianai-captcha-rotate">
|
||||||
<div class="slider-tip">
|
<div class="slider-tip">
|
||||||
<span class="captcha-slider-move-track-font" style="font-size: ${styleConfig.i18n.rotate_title_size}">${styleConfig.i18n.rotate_title}</span>
|
<span id="tianai-captcha-slider-move-track-font" style="font-size: ${styleConfig.i18n.rotate_title_size}">${styleConfig.i18n.rotate_title}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="bg-img-div">
|
<div class="bg-img-div">
|
||||||
<img class="captcha-slider-bg-img" src="" alt/>
|
<img id="tianai-captcha-slider-bg-img" src="" alt/>
|
||||||
<canvas class="captcha-slider-bg-canvas"></canvas>
|
<canvas id="tianai-captcha-slider-bg-canvas"></canvas>
|
||||||
</div>
|
</div>
|
||||||
<div class="rotate-img-div captcha-slider-img-div">
|
<div class="rotate-img-div" id="tianai-captcha-slider-img-div">
|
||||||
<img class="captcha-slider-move-img" src="" alt/>
|
<img id="tianai-captcha-slider-move-img" src="" alt/>
|
||||||
</div>
|
</div>
|
||||||
<div class="captcha-tips"></div>
|
<div class="tianai-captcha-tips" id="tianai-captcha-tips"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-move">
|
<div class="slider-move">
|
||||||
<div class="slider-move-track">
|
<div class="slider-move-track">
|
||||||
<div class="captcha-slider-move-track-mask"></div>
|
<div id="tianai-captcha-slider-move-track-mask"></div>
|
||||||
<div class="slider-move-shadow"></div>
|
<div class="slider-move-shadow"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-move-btn captcha-slider-move-btn">
|
<div class="slider-move-btn" id="tianai-captcha-slider-move-btn">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -49,14 +51,14 @@ class Rotate extends CommonCaptcha {
|
|||||||
// 重载样式
|
// 重载样式
|
||||||
this.destroy();
|
this.destroy();
|
||||||
this.boxEl.append(getTemplate(this.styleConfig));
|
this.boxEl.append(getTemplate(this.styleConfig));
|
||||||
this.el = this.boxEl.find(".captcha");
|
this.el = this.boxEl.find("#tianai-captcha");
|
||||||
this.loadStyle();
|
this.loadStyle();
|
||||||
// 按钮绑定事件
|
// 按钮绑定事件
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-btn")
|
.find("#tianai-captcha-slider-move-btn")
|
||||||
.mousedown(down.bind(null, this));
|
.mousedown(down.bind(null, this));
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-btn")
|
.find("#tianai-captcha-slider-move-btn")
|
||||||
.touchstart(down.bind(null, this));
|
.touchstart(down.bind(null, this));
|
||||||
// 绑定全局
|
// 绑定全局
|
||||||
// window.currentCaptcha = this;
|
// window.currentCaptcha = this;
|
||||||
@ -71,7 +73,7 @@ class Rotate extends CommonCaptcha {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
const existsCaptchaEl = this.boxEl.children(".captcha");
|
const existsCaptchaEl = this.boxEl.children("#tianai-captcha");
|
||||||
if (existsCaptchaEl) {
|
if (existsCaptchaEl) {
|
||||||
existsCaptchaEl.remove();
|
existsCaptchaEl.remove();
|
||||||
}
|
}
|
||||||
@ -80,16 +82,16 @@ class Rotate extends CommonCaptcha {
|
|||||||
doMove() {
|
doMove() {
|
||||||
const moveX = this.currentCaptchaData.moveX;
|
const moveX = this.currentCaptchaData.moveX;
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-btn")
|
.find("#tianai-captcha-slider-move-btn")
|
||||||
.css("transform", "translate(" + moveX + "px, 0px)");
|
.css("transform", "translate(" + moveX + "px, 0px)");
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-img")
|
.find("#tianai-captcha-slider-move-img")
|
||||||
.css(
|
.css(
|
||||||
"transform",
|
"transform",
|
||||||
"rotate(" + moveX / (this.currentCaptchaData.end / 360) + "deg)",
|
"rotate(" + moveX / (this.currentCaptchaData.end / 360) + "deg)",
|
||||||
);
|
);
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-track-mask")
|
.find("#tianai-captcha-slider-move-track-mask")
|
||||||
.css("width", moveX + "px");
|
.css("width", moveX + "px");
|
||||||
}
|
}
|
||||||
loadStyle() {
|
loadStyle() {
|
||||||
@ -105,17 +107,17 @@ class Rotate extends CommonCaptcha {
|
|||||||
this.el
|
this.el
|
||||||
.find(".slider-move .slider-move-btn")
|
.find(".slider-move .slider-move-btn")
|
||||||
.css("background-image", "url(" + sliderImg + ")");
|
.css("background-image", "url(" + sliderImg + ")");
|
||||||
// this.el.find(".captcha-slider-move-track-font").text(title);
|
// this.el.find("#tianai-captcha-slider-move-track-font").text(title);
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-track-mask")
|
.find("#tianai-captcha-slider-move-track-mask")
|
||||||
.css("border-color", moveTrackMaskBorderColor);
|
.css("border-color", moveTrackMaskBorderColor);
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-track-mask")
|
.find("#tianai-captcha-slider-move-track-mask")
|
||||||
.css("background-color", moveTrackMaskBgColor);
|
.css("background-color", moveTrackMaskBgColor);
|
||||||
}
|
}
|
||||||
loadCaptchaForData(that, data) {
|
loadCaptchaForData(that, data) {
|
||||||
const bgImg = that.el.find(".captcha-slider-bg-img");
|
const bgImg = that.el.find("#tianai-captcha-slider-bg-img");
|
||||||
const sliderImg = that.el.find(".captcha-slider-move-img");
|
const sliderImg = that.el.find("#tianai-captcha-slider-move-img");
|
||||||
bgImg.attr("src", data.data.backgroundImage);
|
bgImg.attr("src", data.data.backgroundImage);
|
||||||
sliderImg.attr("src", data.data.templateImage);
|
sliderImg.attr("src", data.data.templateImage);
|
||||||
bgImg.on("load", () => {
|
bgImg.on("load", () => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
.captcha.captcha-rotate {
|
#tianai-captcha.tianai-captcha-rotate {
|
||||||
.rotate-img-div {
|
.rotate-img-div {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import "../common/common.less";
|
||||||
|
import "./slider.less";
|
||||||
import {
|
import {
|
||||||
CommonCaptcha,
|
CommonCaptcha,
|
||||||
closeTips,
|
closeTips,
|
||||||
@ -14,27 +16,27 @@ import {
|
|||||||
const TYPE = "SLIDER";
|
const TYPE = "SLIDER";
|
||||||
function getTemplate(styleConfig) {
|
function getTemplate(styleConfig) {
|
||||||
return `
|
return `
|
||||||
<div class="captcha captcha-slider">
|
<div id="tianai-captcha" class="tianai-captcha-slider">
|
||||||
<div class="slider-tip">
|
<div class="slider-tip">
|
||||||
<span class="captcha-slider-move-track-font" style="font-size: ${styleConfig.i18n.slider_title_size}">${styleConfig.i18n.slider_title}</span>
|
<span id="tianai-captcha-slider-move-track-font" style="font-size: ${styleConfig.i18n.slider_title_size}">${styleConfig.i18n.slider_title}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="bg-img-div">
|
<div class="bg-img-div">
|
||||||
<img class="captcha-slider-bg-img" src="" alt/>
|
<img id="tianai-captcha-slider-bg-img" src="" alt/>
|
||||||
<canvas class="captcha-slider-bg-canvas"></canvas>
|
<canvas id="tianai-captcha-slider-bg-canvas"></canvas>
|
||||||
<div class="captcha-slider-bg-div"></div>
|
<div id="tianai-captcha-slider-bg-div"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-img-div captcha-slider-img-div">
|
<div class="slider-img-div" id="tianai-captcha-slider-img-div">
|
||||||
<img class="captcha-slider-move-img" src="" alt/>
|
<img id="tianai-captcha-slider-move-img" src="" alt/>
|
||||||
</div>
|
</div>
|
||||||
<div class="captcha-tips"></div>
|
<div class="tianai-captcha-tips" id="tianai-captcha-tips"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-move">
|
<div class="slider-move">
|
||||||
<div class="slider-move-track">
|
<div class="slider-move-track">
|
||||||
<div class="captcha-slider-move-track-mask"></div>
|
<div id="tianai-captcha-slider-move-track-mask"></div>
|
||||||
<div class="slider-move-shadow"></div>
|
<div class="slider-move-shadow"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-move-btn captcha-slider-move-btn">
|
<div class="slider-move-btn" id="tianai-captcha-slider-move-btn">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -54,14 +56,14 @@ class Slider extends CommonCaptcha {
|
|||||||
// 重载样式
|
// 重载样式
|
||||||
this.destroy();
|
this.destroy();
|
||||||
this.boxEl.append(getTemplate(this.styleConfig));
|
this.boxEl.append(getTemplate(this.styleConfig));
|
||||||
this.el = this.boxEl.find(".captcha");
|
this.el = this.boxEl.find("#tianai-captcha");
|
||||||
this.loadStyle();
|
this.loadStyle();
|
||||||
// 按钮绑定事件
|
// 按钮绑定事件
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-btn")
|
.find("#tianai-captcha-slider-move-btn")
|
||||||
.mousedown(down.bind(null, this));
|
.mousedown(down.bind(null, this));
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-btn")
|
.find("#tianai-captcha-slider-move-btn")
|
||||||
.touchstart(down.bind(null, this));
|
.touchstart(down.bind(null, this));
|
||||||
// 绑定全局
|
// 绑定全局
|
||||||
// window.currentCaptcha = this;
|
// window.currentCaptcha = this;
|
||||||
@ -82,7 +84,7 @@ class Slider extends CommonCaptcha {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
const existsCaptchaEl = this.boxEl.children(".captcha");
|
const existsCaptchaEl = this.boxEl.children("#tianai-captcha");
|
||||||
if (existsCaptchaEl) {
|
if (existsCaptchaEl) {
|
||||||
existsCaptchaEl.remove();
|
existsCaptchaEl.remove();
|
||||||
}
|
}
|
||||||
@ -91,13 +93,13 @@ class Slider extends CommonCaptcha {
|
|||||||
doMove() {
|
doMove() {
|
||||||
const moveX = this.currentCaptchaData.moveX;
|
const moveX = this.currentCaptchaData.moveX;
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-btn")
|
.find("#tianai-captcha-slider-move-btn")
|
||||||
.css("transform", "translate(" + moveX + "px, 0px)");
|
.css("transform", "translate(" + moveX + "px, 0px)");
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-img-div")
|
.find("#tianai-captcha-slider-img-div")
|
||||||
.css("transform", "translate(" + moveX + "px, 0px)");
|
.css("transform", "translate(" + moveX + "px, 0px)");
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-track-mask")
|
.find("#tianai-captcha-slider-move-track-mask")
|
||||||
.css("width", moveX + "px");
|
.css("width", moveX + "px");
|
||||||
}
|
}
|
||||||
loadStyle() {
|
loadStyle() {
|
||||||
@ -113,17 +115,17 @@ class Slider extends CommonCaptcha {
|
|||||||
this.el
|
this.el
|
||||||
.find(".slider-move .slider-move-btn")
|
.find(".slider-move .slider-move-btn")
|
||||||
.css("background-image", "url(" + sliderImg + ")");
|
.css("background-image", "url(" + sliderImg + ")");
|
||||||
// this.el.find(".captcha-slider-move-track-font").text(title);
|
// this.el.find("#tianai-captcha-slider-move-track-font").text(title);
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-track-mask")
|
.find("#tianai-captcha-slider-move-track-mask")
|
||||||
.css("border-color", moveTrackMaskBorderColor);
|
.css("border-color", moveTrackMaskBorderColor);
|
||||||
this.el
|
this.el
|
||||||
.find(".captcha-slider-move-track-mask")
|
.find("#tianai-captcha-slider-move-track-mask")
|
||||||
.css("background-color", moveTrackMaskBgColor);
|
.css("background-color", moveTrackMaskBgColor);
|
||||||
}
|
}
|
||||||
loadCaptchaForData(that, data) {
|
loadCaptchaForData(that, data) {
|
||||||
const bgImg = that.el.find(".captcha-slider-bg-img");
|
const bgImg = that.el.find("#tianai-captcha-slider-bg-img");
|
||||||
const sliderImg = that.el.find(".captcha-slider-move-img");
|
const sliderImg = that.el.find("#tianai-captcha-slider-move-img");
|
||||||
bgImg.attr("src", data.data.backgroundImage);
|
bgImg.attr("src", data.data.backgroundImage);
|
||||||
sliderImg.attr("src", data.data.templateImage);
|
sliderImg.attr("src", data.data.templateImage);
|
||||||
bgImg.on("load", () => {
|
bgImg.on("load", () => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
.captcha.captcha-slider {
|
#tianai-captcha.tianai-captcha-slider {
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -17,6 +17,7 @@
|
|||||||
img {
|
img {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.slider-img-div {
|
.slider-img-div {
|
||||||
@ -24,7 +25,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
transform: translate(0px, 0px);
|
transform: translate(0px, 0px);
|
||||||
.captcha-slider-move-img {
|
#tianai-captcha-slider-move-img {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,6 +80,7 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
|
color: black;
|
||||||
}
|
}
|
||||||
.slider-move-btn:hover {
|
.slider-move-btn:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
10
src/vendor/tianai-captcha/index.ts
vendored
10
src/vendor/tianai-captcha/index.ts
vendored
@ -2,13 +2,10 @@ import { CaptchaConfig, TianAiCaptcha } from "./captcha/captcha.js"
|
|||||||
|
|
||||||
export type TianAiCaptchaBindTarget = string | HTMLElement
|
export type TianAiCaptchaBindTarget = string | HTMLElement
|
||||||
|
|
||||||
export interface TianAiCaptchaResult {
|
|
||||||
id: string
|
|
||||||
data: Record<string, any>
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TianAiCaptchaStyle {
|
export interface TianAiCaptchaStyle {
|
||||||
btnUrl?: string
|
btnUrl?: string
|
||||||
|
logoUrl?: string | null
|
||||||
|
bgUrl?: string
|
||||||
moveTrackMaskBgColor?: string
|
moveTrackMaskBgColor?: string
|
||||||
moveTrackMaskBorderColor?: string
|
moveTrackMaskBorderColor?: string
|
||||||
i18n?: Record<string, string>
|
i18n?: Record<string, string>
|
||||||
@ -17,12 +14,11 @@ export interface TianAiCaptchaStyle {
|
|||||||
export interface TianAiCaptchaConfig {
|
export interface TianAiCaptchaConfig {
|
||||||
bindEl: TianAiCaptchaBindTarget
|
bindEl: TianAiCaptchaBindTarget
|
||||||
requestCaptchaDataUrl: string
|
requestCaptchaDataUrl: string
|
||||||
validCaptchaUrl?: string
|
validCaptchaUrl: string
|
||||||
requestHeaders?: Record<string, string>
|
requestHeaders?: Record<string, string>
|
||||||
timeToTimestamp?: boolean
|
timeToTimestamp?: boolean
|
||||||
validSuccess?: (res: any, captcha: any, tac: TianAiCaptchaInstance) => void
|
validSuccess?: (res: any, captcha: any, tac: TianAiCaptchaInstance) => void
|
||||||
validFail?: (res: any, captcha: any, tac: TianAiCaptchaInstance) => void
|
validFail?: (res: any, captcha: any, tac: TianAiCaptchaInstance) => void
|
||||||
onDataReady?: (result: TianAiCaptchaResult, captcha: any, tac: TianAiCaptchaInstance) => void | Promise<void>
|
|
||||||
btnRefreshFun?: (el: Event, tac: TianAiCaptchaInstance) => void
|
btnRefreshFun?: (el: Event, tac: TianAiCaptchaInstance) => void
|
||||||
btnCloseFun?: (el: Event, tac: TianAiCaptchaInstance) => void
|
btnCloseFun?: (el: Event, tac: TianAiCaptchaInstance) => void
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
placeholder="请输入用户名"
|
placeholder="请输入用户名"
|
||||||
prefix-icon="User"
|
prefix-icon="User"
|
||||||
size="large"
|
size="large"
|
||||||
@keyup.enter="handleLogin"
|
@keyup.enter="login"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="密码" prop="password">
|
<el-form-item label="密码" prop="password">
|
||||||
@ -29,80 +29,35 @@
|
|||||||
prefix-icon="Lock"
|
prefix-icon="Lock"
|
||||||
size="large"
|
size="large"
|
||||||
show-password
|
show-password
|
||||||
@keyup.enter="handleLogin"
|
@keyup.enter="login"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-button class="login-btn" type="primary" size="large" :loading="loading" @click="handleLogin" plain>
|
<el-button class="login-btn" type="primary" size="large" :loading="loading" @click="login" plain>
|
||||||
登 录
|
登 录
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dialog
|
|
||||||
v-model="captchaDialogVisible"
|
|
||||||
class="captcha-dialog"
|
|
||||||
width="460px"
|
|
||||||
align-center
|
|
||||||
destroy-on-close
|
|
||||||
append-to-body
|
|
||||||
title="安全验证"
|
|
||||||
:close-on-click-modal="!loading"
|
|
||||||
:close-on-press-escape="!loading"
|
|
||||||
:show-close="!loading"
|
|
||||||
@closed="handleCaptchaDialogClosed"
|
|
||||||
>
|
|
||||||
<p class="captcha-dialog-subtitle">请先完成验证码校验,再继续登录。</p>
|
|
||||||
<CaptchaPanel
|
|
||||||
ref="captchaRef"
|
|
||||||
class="captcha-box"
|
|
||||||
@close="closeCaptchaDialog"
|
|
||||||
@data-ready="handleCaptchaSolved"
|
|
||||||
@init-error="handleCaptchaInitError"
|
|
||||||
/>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { nextTick, onBeforeUnmount, reactive, ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import type { VForm } from '../types'
|
import type { VForm } from '../types'
|
||||||
import CaptchaPanel from '@/components/CaptchaPanel.vue'
|
|
||||||
import http from '@/utils/http'
|
import http from '@/utils/http'
|
||||||
import type { TianAiCaptchaResult } from '@/vendor/tianai-captcha'
|
|
||||||
|
|
||||||
type LoginFormState = {
|
type UserInfo = {
|
||||||
username: string | null,
|
username: string | null,
|
||||||
password: string | null
|
password: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginRequest = LoginFormState & {
|
|
||||||
captchaId?: string,
|
|
||||||
captchaData?: Record<string, any>
|
|
||||||
}
|
|
||||||
|
|
||||||
type LoginConfigResponse = {
|
|
||||||
captcha?: {
|
|
||||||
enabled?: boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptchaPanelRef = {
|
|
||||||
destroy: () => void
|
|
||||||
init: () => Promise<void>
|
|
||||||
reload: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loginForm = ref<VForm>()
|
const loginForm = ref<VForm>()
|
||||||
const captchaRef = ref<CaptchaPanelRef | null>(null)
|
|
||||||
const captchaDialogVisible = ref(false)
|
|
||||||
const captchaErrorPattern = /验证码|captcha/i
|
|
||||||
|
|
||||||
const userInfo: LoginFormState = reactive({
|
const userInfo: UserInfo = reactive({
|
||||||
username: null,
|
username: null,
|
||||||
password: null
|
password: null
|
||||||
})
|
})
|
||||||
@ -122,95 +77,19 @@ store.commit('logout')
|
|||||||
store.commit('clearTabs')
|
store.commit('clearTabs')
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
|
|
||||||
async function openCaptchaDialog() {
|
async function login() {
|
||||||
captchaDialogVisible.value = true
|
|
||||||
await nextTick()
|
|
||||||
await captchaRef.value?.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleLogin() {
|
|
||||||
loginForm.value?.validate(async (valid: boolean) => {
|
loginForm.value?.validate(async (valid: boolean) => {
|
||||||
if (!valid || loading.value) return
|
if (!valid) return
|
||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
const data = await http.post<UserInfo, any>('/common/login', userInfo).finally(() => {
|
||||||
try {
|
|
||||||
const loginConfig = await http.get<any, LoginConfigResponse>('/common/loginConfig', {
|
|
||||||
params: {
|
|
||||||
username: userInfo.username,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (loginConfig.captcha?.enabled) {
|
|
||||||
loading.value = false
|
|
||||||
await openCaptchaDialog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await submitLogin(userInfo)
|
|
||||||
} catch {
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
})
|
||||||
|
if (data.token) {
|
||||||
|
store.commit('login', data)
|
||||||
|
router.push('/')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCaptchaSolved(result: TianAiCaptchaResult) {
|
|
||||||
if (loading.value) return
|
|
||||||
|
|
||||||
loading.value = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
await submitLogin({
|
|
||||||
...userInfo,
|
|
||||||
captchaId: result.id,
|
|
||||||
captchaData: result.data,
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
if (isCaptchaError(error)) {
|
|
||||||
captchaRef.value?.reload()
|
|
||||||
} else {
|
|
||||||
closeCaptchaDialog()
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function submitLogin(payload: LoginRequest) {
|
|
||||||
const data = await http.post<LoginRequest, any>('/common/login', payload)
|
|
||||||
|
|
||||||
closeCaptchaDialog(false)
|
|
||||||
if (data.token) {
|
|
||||||
store.commit('login', data)
|
|
||||||
router.push('/')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeCaptchaDialog(resetLoading = true) {
|
|
||||||
captchaDialogVisible.value = false
|
|
||||||
captchaRef.value?.destroy()
|
|
||||||
if (resetLoading) {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCaptchaDialogClosed() {
|
|
||||||
captchaRef.value?.destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCaptchaInitError() {
|
|
||||||
captchaDialogVisible.value = false
|
|
||||||
ElMessage.error('验证码初始化失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCaptchaError(error: unknown) {
|
|
||||||
if (error instanceof Error) {
|
|
||||||
return captchaErrorPattern.test(error.message)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeUnmount(() => captchaRef.value?.destroy())
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.login-page {
|
.login-page {
|
||||||
@ -348,15 +227,4 @@ onBeforeUnmount(() => captchaRef.value?.destroy())
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha-dialog-subtitle {
|
|
||||||
margin: 0 0 16px;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.captcha-box {
|
|
||||||
width: fit-content;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@ -1,51 +1,51 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="hero-actions">
|
<div class="hero-actions">
|
||||||
<el-button type="primary" @click="startCaptcha" plain>开始验证</el-button>
|
<el-button type="primary" @click="startCaptcha">开始验证</el-button>
|
||||||
<el-button @click="reloadCaptcha" :disabled="!captchaActive">刷新</el-button>
|
<el-button @click="reloadCaptcha" :disabled="!captchaInstance">刷新</el-button>
|
||||||
<el-button @click="destroyCaptcha" :disabled="!captchaActive" plain>销毁</el-button>
|
<el-button @click="destroyCaptcha" :disabled="!captchaInstance" plain>销毁</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sandbox-grid">
|
<div class="sandbox-grid">
|
||||||
<el-card shadow="never">
|
<el-card class="panel-card" shadow="never">
|
||||||
<template #header>验证码类型</template>
|
<template #header>
|
||||||
|
<div class="panel-title">验证码类型</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<el-radio-group v-model="selectedType" size="large">
|
<el-radio-group v-model="selectedType" class="type-group">
|
||||||
<el-radio-button v-for="item in captchaTypeOptions" :key="item.value" :label="item.value">
|
<el-radio-button v-for="item in captchaTypeOptions" :key="item.value" :label="item.value">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
|
|
||||||
|
<div class="current-selection">
|
||||||
|
<span class="selection-label">当前选择</span>
|
||||||
|
<strong>{{ currentTypeLabel }}</strong>
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card class="preview-card" shadow="never">
|
<el-card class="panel-card preview-card" shadow="never">
|
||||||
<template #header>验证码预览</template>
|
<template #header>
|
||||||
<CaptchaPanel
|
<div class="panel-title">验证码预览</div>
|
||||||
ref="captchaRef"
|
</template>
|
||||||
class="captcha-box"
|
<div ref="captchaBoxRef" class="captcha-box"></div>
|
||||||
mode="validate"
|
|
||||||
:type="selectedType"
|
|
||||||
@close="handleCaptchaClose"
|
|
||||||
@state-change="handleCaptchaStateChange"
|
|
||||||
@valid-fail="handleValidFail"
|
|
||||||
@valid-success="handleValidSuccess"
|
|
||||||
/>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeUnmount, ref } from 'vue'
|
import { computed, onBeforeUnmount, ref } from 'vue'
|
||||||
import CaptchaPanel from '@/components/CaptchaPanel.vue'
|
import {
|
||||||
|
createTianAiCaptcha,
|
||||||
|
type TianAiCaptchaConfig,
|
||||||
|
type TianAiCaptchaInstance
|
||||||
|
} from '@/vendor/tianai-captcha'
|
||||||
|
|
||||||
type CaptchaType = 'RANDOM' | 'SLIDER' | 'ROTATE' | 'CONCAT' | 'WORD_IMAGE_CLICK'
|
type CaptchaType = 'RANDOM' | 'SLIDER' | 'ROTATE' | 'CONCAT' | 'WORD_IMAGE_CLICK'
|
||||||
|
|
||||||
type CaptchaPanelRef = {
|
const requestCaptchaBaseUrl = buildApiUrl('/captcha/gen')
|
||||||
destroy: () => void
|
const validCaptchaUrl = buildApiUrl('/captcha/check')
|
||||||
init: () => Promise<void>
|
|
||||||
reload: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const captchaTypeOptions: Array<{ label: string, value: CaptchaType }> = [
|
const captchaTypeOptions: Array<{ label: string, value: CaptchaType }> = [
|
||||||
{ label: '随机', value: 'RANDOM' },
|
{ label: '随机', value: 'RANDOM' },
|
||||||
{ label: '滑块拼图', value: 'SLIDER' },
|
{ label: '滑块拼图', value: 'SLIDER' },
|
||||||
@ -54,76 +54,148 @@ const captchaTypeOptions: Array<{ label: string, value: CaptchaType }> = [
|
|||||||
{ label: '汉字点选', value: 'WORD_IMAGE_CLICK' },
|
{ label: '汉字点选', value: 'WORD_IMAGE_CLICK' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const captchaRef = ref<CaptchaPanelRef | null>(null)
|
const captchaInstance = ref<TianAiCaptchaInstance | null>(null)
|
||||||
const captchaActive = ref(false)
|
const captchaBoxRef = ref<HTMLElement | null>(null)
|
||||||
const selectedType = ref<CaptchaType>('RANDOM')
|
const selectedType = ref<CaptchaType>('RANDOM')
|
||||||
|
|
||||||
async function startCaptcha() {
|
const currentTypeLabel = computed(() => {
|
||||||
|
return captchaTypeOptions.find(item => item.value === selectedType.value)?.label || '随机'
|
||||||
|
})
|
||||||
|
|
||||||
|
const requestCaptchaDataUrl = computed(() => {
|
||||||
|
if (selectedType.value === 'RANDOM') {
|
||||||
|
return requestCaptchaBaseUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${requestCaptchaBaseUrl}?type=${encodeURIComponent(selectedType.value)}`
|
||||||
|
})
|
||||||
|
|
||||||
|
function startCaptcha() {
|
||||||
|
if (!captchaBoxRef.value) {
|
||||||
|
console.warn('[captcha sandbox] captcha container is not ready')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyCaptcha(false)
|
||||||
|
|
||||||
|
const config: TianAiCaptchaConfig = {
|
||||||
|
bindEl: captchaBoxRef.value,
|
||||||
|
requestCaptchaDataUrl: requestCaptchaDataUrl.value,
|
||||||
|
validCaptchaUrl,
|
||||||
|
validSuccess: (res, _captcha, tac) => {
|
||||||
|
console.log('[captcha sandbox] valid success', res)
|
||||||
|
tac.destroyWindow()
|
||||||
|
},
|
||||||
|
validFail: (res, _captcha, tac) => {
|
||||||
|
console.warn('[captcha sandbox] valid fail', res)
|
||||||
|
tac.reloadCaptcha()
|
||||||
|
},
|
||||||
|
btnRefreshFun: (_el, tac) => {
|
||||||
|
console.info('[captcha sandbox] refresh captcha')
|
||||||
|
tac.reloadCaptcha()
|
||||||
|
},
|
||||||
|
btnCloseFun: (_el, tac) => {
|
||||||
|
console.info('[captcha sandbox] close captcha window')
|
||||||
|
tac.destroyWindow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.info('[captcha sandbox] init captcha', {
|
console.info('[captcha sandbox] init captcha', {
|
||||||
type: selectedType.value,
|
type: selectedType.value,
|
||||||
mode: 'validate'
|
requestCaptchaDataUrl: requestCaptchaDataUrl.value,
|
||||||
|
validCaptchaUrl
|
||||||
})
|
})
|
||||||
|
|
||||||
await captchaRef.value?.init()
|
captchaInstance.value = createTianAiCaptcha(config)
|
||||||
|
captchaInstance.value.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadCaptcha() {
|
function reloadCaptcha() {
|
||||||
if (!captchaActive.value) return
|
if (!captchaInstance.value) return
|
||||||
console.info('[captcha sandbox] manual reload')
|
console.info('[captcha sandbox] manual reload')
|
||||||
captchaRef.value?.reload()
|
captchaInstance.value.reloadCaptcha()
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroyCaptcha(showMessage = true) {
|
function destroyCaptcha(showMessage = true) {
|
||||||
if (!captchaActive.value) return
|
if (!captchaInstance.value) return
|
||||||
captchaRef.value?.destroy()
|
captchaInstance.value.destroyWindow()
|
||||||
|
captchaInstance.value = null
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
console.info('[captcha sandbox] destroy captcha instance')
|
console.info('[captcha sandbox] destroy captcha instance')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCaptchaClose() {
|
function buildApiUrl(path: string) {
|
||||||
console.info('[captcha sandbox] close captcha window')
|
const normalizedBase = (import.meta.env.VITE_APP_API_BASE || '').replace(/\/+$/, '')
|
||||||
|
const normalizedPath = path.startsWith('/') ? path : `/${path}`
|
||||||
|
return `${normalizedBase}${normalizedPath}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCaptchaStateChange(active: boolean) {
|
onBeforeUnmount(() => destroyCaptcha(false))
|
||||||
captchaActive.value = active
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleValidSuccess(res: any) {
|
|
||||||
console.log('[captcha sandbox] valid success', res)
|
|
||||||
captchaRef.value?.destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleValidFail(res: any) {
|
|
||||||
console.warn('[captcha sandbox] valid fail', res)
|
|
||||||
captchaRef.value?.reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeUnmount(() => captchaRef.value?.destroy())
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.hero-actions {
|
.hero-actions {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sandbox-grid {
|
.sandbox-grid {
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-columns: minmax(320px, 420px) minmax(420px, 1fr);
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.el-card {
|
.panel-card {
|
||||||
flex: 1;
|
border-radius: 18px;
|
||||||
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
|
}
|
||||||
|
|
||||||
&.preview-card {
|
.panel-title {
|
||||||
flex: 0 0 420px;
|
font-size: 15px;
|
||||||
}
|
font-weight: 700;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
:deep(.el-radio-button__inner) {
|
||||||
|
min-width: 112px;
|
||||||
|
border-radius: 999px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.current-selection {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 18px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selection-label {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
.captcha-box {
|
.captcha-box {
|
||||||
min-height: 350px;
|
min-height: 350px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
border-radius: 16px;
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, rgba(64, 158, 255, 0.07), rgba(103, 194, 58, 0.05)),
|
||||||
|
#fff;
|
||||||
|
border: 1px dashed rgba(64, 158, 255, 0.3);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user