♻️ geetest 组件函数化调用

This commit is contained in:
BTMuli
2023-09-30 18:57:31 +08:00
parent b57d34419f
commit dd940d5a2b
4 changed files with 225 additions and 140 deletions

View File

@@ -0,0 +1,33 @@
/**
* @file component func geetest.ts
* @description 封装自定义 geetest 组件,通过函数调用的方式,简化 geetest 的使用,使用时需要在 main.ts 中引入 geetest.js
* @since Beta v0.3.3
*/
import { h, render, type VNode } from "vue";
import geetest from "./geetest.vue";
const geetestId = "tg-func-geetest";
const renderBox = (): VNode => {
const container = document.createElement("div");
container.id = geetestId;
const boxVNode: VNode = h(geetest);
render(boxVNode, container);
document.body.appendChild(container);
return boxVNode;
};
let geetestInstance: VNode;
const showGeetest = async (): Promise<string | boolean> => {
if (geetestInstance) {
const boxVue = geetestInstance.component;
return boxVue?.exposeProxy?.displayBox();
} else {
geetestInstance = renderBox();
return await showGeetest();
}
};
export default showGeetest;

View File

@@ -0,0 +1,189 @@
<template>
<transition name="func-geetest-outer">
<div v-show="show || showOuter" class="geetest-overlay" @click.self.prevent="handleOuter">
<transition name="func-geetest-inner">
<div v-show="showInner" class="geetest-box">
<div class="geetest-top">
<div class="geetest-title">请完成如下极验测试</div>
</div>
<div id="verify" class="geetest-mid">
<div id="geetest" ref="geetestRef"></div>
</div>
</div>
</transition>
</div>
</transition>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
import showSnackbar from "./snackbar";
import { useUserStore } from "../../store/modules/user";
import TGRequest from "../../web/request/TGRequest";
const show = ref<boolean>(false);
const showOuter = ref<boolean>(false);
const showInner = ref<boolean>(false);
const userStore = useUserStore();
const geetestRef = ref<HTMLElement | null>(null);
watch(show, () => {
if (show.value) {
showOuter.value = true;
setTimeout(() => {
showInner.value = true;
}, 100);
} else {
setTimeout(() => {
showInner.value = false;
}, 100);
setTimeout(() => {
showOuter.value = false;
}, 300);
}
});
async function displayBox(): Promise<void> {
const cookie = userStore.getCookieGroup3();
const res = await TGRequest.User.verification.get(cookie.ltoken, cookie.ltuid);
if ("retcode" in res) {
showSnackbar({
text: `[${res.retcode}]${res.message}`,
color: "error",
});
return;
}
show.value = true;
// @ts-expect-error Cannot find name 'initGeetest'.
initGeetest(
{
gt: res.gt,
challenge: res.challenge,
offline: false,
new_captcha: true,
product: "custom",
area: "#verify",
width: "250px",
},
(captchaObj: TGApp.BBS.Geetest.GeetestCaptcha) => {
geetestRef.value = document.getElementById("geetest");
if (geetestRef.value === null) throw new Error("Cannot find element with id 'geetest'.");
geetestRef.value.innerHTML = "";
captchaObj.appendTo("#geetest");
captchaObj
.onReady(() => {
console.log("ready");
})
.onSuccess(() => {
const validate = captchaObj.getValidate();
(async () => {
await TGRequest.User.verification.verify(userStore.cookie, validate);
show.value = false;
})().catch(() => {}); // 防止报错
})
.onError(() => {
console.log("error");
})
.onClose(() => {
console.log("close");
});
},
);
}
function handleOuter(): void {
show.value = false;
}
defineExpose({
displayBox,
});
</script>
<style lang="css" scoped>
.func-geetest-outer-enter-active,
.func-geetest-outer-leave-active,
.func-geetest-inner-enter-active {
transition: all 0.3s;
}
.func-geetest-inner-leave-active {
transition: all 0.5s ease-in-out;
}
.func-geetest-inner-enter-from {
opacity: 0;
transform: scale(1.5);
}
.func-geetest-inner-enter-to,
.func-geetest-inner-leave-from {
opacity: 1;
transform: scale(1);
}
.func-geetest-outer-enter-to,
.func-geetest-outer-leave-from {
opacity: 1;
}
.func-geetest-outer-enter-from,
.func-geetest-outer-leave-to {
opacity: 0;
}
.func-geetest-inner-leave-to {
opacity: 0;
transform: scale(0);
}
.geetest-overlay {
position: fixed;
z-index: 100;
top: 0;
left: 0;
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
backdrop-filter: blur(20px);
background: rgb(0 0 0 / 50%);
}
.geetest-box {
display: flex;
flex-direction: column;
padding: 10px;
border-radius: 5px;
background-color: var(--box-bg-1);
color: var(--app-page-content);
gap: 10px;
}
.geetest-top {
border-bottom: 1px solid var(--common-shadow-4);
font-family: var(--font-title);
text-align: center;
}
.geetest-title {
color: var(--common-text-title);
font-size: 20px;
}
.geetest-mid {
display: flex;
width: 100%;
align-items: flex-start;
justify-content: center;
padding: 10px;
border-radius: 5px;
background: var(--box-bg-2);
}
#verify {
width: 256px;
height: 320px;
}
</style>

View File

@@ -1,136 +0,0 @@
<template>
<TOverlay v-model="visible" hide blur-val="20px">
<div class="tog-box">
<div class="tog-top">
<div class="tog-title">请完成如下极验测试</div>
</div>
<div id="verify" class="tog-mid">
<div id="geetest"></div>
</div>
</div>
</TOverlay>
</template>
<script setup lang="ts">
// vue
import { computed, watch } from "vue";
import showSnackbar from "../func/snackbar";
import TOverlay from "../main/t-overlay.vue";
// store
import { useUserStore } from "../../store/modules/user";
// utils
import TGRequest from "../../web/request/TGRequest";
interface ToGeetestProps {
modelValue: boolean;
}
type ToGeetestEmits = (e: "update:modelValue", value: boolean) => void;
const props = withDefaults(defineProps<ToGeetestProps>(), {
modelValue: false,
});
const emits = defineEmits<ToGeetestEmits>();
const visible = computed({
get: () => props.modelValue,
set: (value) => {
emits("update:modelValue", value);
},
});
watch(visible, async (value) => {
if (value) {
await getChallenge();
}
});
const userStore = useUserStore();
// 获取极验验证码
async function getChallenge(): Promise<void> {
const cookie = userStore.getCookieGroup3();
const res = await TGRequest.User.verification.get(cookie.ltoken, cookie.ltuid);
if ("retcode" in res) {
showSnackbar({
text: res.message,
color: "error",
});
return;
}
// @ts-expect-error Cannot find name 'initGeetest'.ts(2304)
initGeetest(
{
gt: res.gt,
challenge: res.challenge,
offline: false,
new_captcha: true,
product: "custom", // 产品形式包括floatpopup, bind
area: "#verify", // 选择验证码展示的位置
width: "250px",
},
(captchaObj: TGApp.BBS.Geetest.GeetestCaptcha) => {
captchaObj.appendTo("#geetest");
captchaObj
.onReady(() => {
console.log("ready");
})
.onSuccess(() => {
const validate = captchaObj.getValidate();
(async () => {
await doVerify(validate);
})().catch(() => {}); // 防止报错
})
.onError(() => {
console.log("error");
})
.onClose(() => {
console.log("close");
});
},
);
}
async function doVerify(data: TGApp.BBS.Geetest.GeetestValidate): Promise<void> {
await TGRequest.User.verification.verify(userStore.cookie, data);
emits("update:modelValue", false);
}
</script>
<style lang="css" scoped>
.tog-box {
display: flex;
flex-direction: column;
padding: 10px;
border-radius: 5px;
background-color: var(--box-bg-1);
color: var(--app-page-content);
gap: 10px;
}
.tog-top {
border-bottom: 1px solid var(--common-shadow-4);
font-family: var(--font-title);
text-align: center;
}
.tog-title {
color: var(--common-text-title);
font-size: 20px;
}
.tog-mid {
display: flex;
width: 100%;
align-items: flex-start;
justify-content: center;
padding: 10px;
border-radius: 5px;
background: var(--box-bg-2);
}
#verify {
width: 256px;
height: 320px;
}
</style>

View File

@@ -17,12 +17,11 @@
<div class="btn-list">
<v-btn class="test-btn" @click="getGC">获取极验</v-btn>
</div>
<to-geetest v-model="visible"></to-geetest>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import ToGeetest from "../../components/overlay/to-geetest.vue";
import showGeetest from "../../components/func/geetest";
const visible = ref<boolean>(false);
@@ -30,8 +29,8 @@ onMounted(async () => {
visible.value = false;
});
function getGC(): void {
visible.value = true;
async function getGC(): Promise<void> {
await showGeetest();
}
</script>
<style lang="css" scoped>