130 lines
3.2 KiB
Vue
130 lines
3.2 KiB
Vue
<template>
|
|
<div>
|
|
<div class="hero-actions">
|
|
<el-button type="primary" @click="startCaptcha" plain>开始验证</el-button>
|
|
<el-button @click="reloadCaptcha" :disabled="!captchaActive">刷新</el-button>
|
|
<el-button @click="destroyCaptcha" :disabled="!captchaActive" plain>销毁</el-button>
|
|
</div>
|
|
|
|
<div class="sandbox-grid">
|
|
<el-card shadow="never">
|
|
<template #header>验证码类型</template>
|
|
|
|
<el-radio-group v-model="selectedType" size="large">
|
|
<el-radio-button v-for="item in captchaTypeOptions" :key="item.value" :label="item.value">
|
|
{{ item.label }}
|
|
</el-radio-button>
|
|
</el-radio-group>
|
|
</el-card>
|
|
|
|
<el-card class="preview-card" shadow="never">
|
|
<template #header>验证码预览</template>
|
|
<CaptchaPanel
|
|
ref="captchaRef"
|
|
class="captcha-box"
|
|
mode="validate"
|
|
:type="selectedType"
|
|
@close="handleCaptchaClose"
|
|
@state-change="handleCaptchaStateChange"
|
|
@valid-fail="handleValidFail"
|
|
@valid-success="handleValidSuccess"
|
|
/>
|
|
</el-card>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onBeforeUnmount, ref } from 'vue'
|
|
import CaptchaPanel from '@/components/CaptchaPanel.vue'
|
|
|
|
type CaptchaType = 'RANDOM' | 'SLIDER' | 'ROTATE' | 'CONCAT' | 'WORD_IMAGE_CLICK'
|
|
|
|
type CaptchaPanelRef = {
|
|
destroy: () => void
|
|
init: () => Promise<void>
|
|
reload: () => void
|
|
}
|
|
|
|
const captchaTypeOptions: Array<{ label: string, value: CaptchaType }> = [
|
|
{ label: '随机', value: 'RANDOM' },
|
|
{ label: '滑块拼图', value: 'SLIDER' },
|
|
{ label: '旋转', value: 'ROTATE' },
|
|
{ label: '拼接', value: 'CONCAT' },
|
|
{ label: '汉字点选', value: 'WORD_IMAGE_CLICK' },
|
|
]
|
|
|
|
const captchaRef = ref<CaptchaPanelRef | null>(null)
|
|
const captchaActive = ref(false)
|
|
const selectedType = ref<CaptchaType>('RANDOM')
|
|
|
|
async function startCaptcha() {
|
|
console.info('[captcha sandbox] init captcha', {
|
|
type: selectedType.value,
|
|
mode: 'validate'
|
|
})
|
|
|
|
await captchaRef.value?.init()
|
|
}
|
|
|
|
function reloadCaptcha() {
|
|
if (!captchaActive.value) return
|
|
console.info('[captcha sandbox] manual reload')
|
|
captchaRef.value?.reload()
|
|
}
|
|
|
|
function destroyCaptcha(showMessage = true) {
|
|
if (!captchaActive.value) return
|
|
captchaRef.value?.destroy()
|
|
if (showMessage) {
|
|
console.info('[captcha sandbox] destroy captcha instance')
|
|
}
|
|
}
|
|
|
|
function handleCaptchaClose() {
|
|
console.info('[captcha sandbox] close captcha window')
|
|
}
|
|
|
|
function handleCaptchaStateChange(active: boolean) {
|
|
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>
|
|
|
|
<style lang="less" scoped>
|
|
.hero-actions {
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.sandbox-grid {
|
|
display: flex;
|
|
gap: 20px;
|
|
|
|
.el-card {
|
|
flex: 1;
|
|
|
|
&.preview-card {
|
|
flex: 0 0 420px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.captcha-box {
|
|
min-height: 350px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
</style>
|