验证码接口适配调整

This commit is contained in:
灌糖包子 2026-05-16 16:03:54 +08:00
parent f9b0b05c7e
commit 25e80d73ae
Signed by: sookie
GPG Key ID: 0599BECB75C1E68D
5 changed files with 45 additions and 34 deletions

4
components.d.ts vendored
View File

@ -11,6 +11,8 @@ declare module '@vue/runtime-core' {
ElAside: typeof import('element-plus/es')['ElAside']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckboxButton: typeof import('element-plus/es')['ElCheckboxButton']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElContainer: typeof import('element-plus/es')['ElContainer']
@ -34,8 +36,6 @@ declare module '@vue/runtime-core' {
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']

View File

@ -1,9 +1,9 @@
<template>
<div ref="captchaBoxRef" class="captcha-panel"></div>
<div ref="captchaBoxRef"></div>
</template>
<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, ref } from 'vue'
import { nextTick, onBeforeUnmount, ref } from 'vue'
import {
createTianAiCaptcha,
type TianAiCaptchaConfig,
@ -13,17 +13,15 @@ import {
} from '@/vendor/tianai-captcha'
type CaptchaMode = 'data' | 'validate'
type CaptchaType = 'RANDOM' | 'SLIDER' | 'ROTATE' | 'CONCAT' | 'WORD_IMAGE_CLICK'
type CaptchaType = 'SLIDER' | 'ROTATE' | 'CONCAT' | 'WORD_IMAGE_CLICK'
const props = withDefaults(defineProps<{
mode?: CaptchaMode
type?: CaptchaType
validCaptchaUrl?: string
requestHeaders?: Record<string, string>
types?: CaptchaType[]
styleConfig?: TianAiCaptchaStyle
}>(), {
mode: 'data',
type: 'RANDOM'
types: () => []
})
const emit = defineEmits<{
@ -38,17 +36,8 @@ const emit = defineEmits<{
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)}`
})
const requestCaptchaDataUrl = buildCaptchaApiUrl('/captcha/generate')
const validCaptchaUrl = buildCaptchaApiUrl('/captcha/check')
async function init() {
await nextTick()
@ -62,8 +51,8 @@ async function init() {
const config: TianAiCaptchaConfig = {
bindEl: captchaBoxRef.value,
requestCaptchaDataUrl: requestCaptchaDataUrl.value,
requestHeaders: props.requestHeaders,
requestCaptchaDataUrl,
types: props.types,
btnRefreshFun: (_el, tac) => {
tac.reloadCaptcha()
},
@ -74,7 +63,7 @@ async function init() {
}
switch (props.mode) {
case 'validate':
config.validCaptchaUrl = resolvedValidCaptchaUrl.value
config.validCaptchaUrl = validCaptchaUrl
config.validSuccess = res => emit('valid-success', res)
config.validFail = res => emit('valid-fail', res)
break

View File

@ -14,6 +14,7 @@ class CaptchaConfig {
this.bindEl = args.bindEl;
this.domBindEl = Dom(args.bindEl);
this.requestCaptchaDataUrl = args.requestCaptchaDataUrl;
this.types = Array.isArray(args.types) ? args.types.filter(Boolean) : [];
this.validCaptchaUrl = args.validCaptchaUrl;
if (args.validSuccess) {
this.validSuccess = args.validSuccess;
@ -64,7 +65,7 @@ class CaptchaConfig {
requestCaptchaData() {
const requestParam = {};
requestParam.headers = this.requestHeaders || {};
requestParam.data = {};
requestParam.data = { types: this.types };
// 设置默认值
requestParam.headers["Content-Type"] = "application/json;charset=UTF-8";
requestParam.method = "POST";

View File

@ -17,8 +17,8 @@ export interface TianAiCaptchaStyle {
export interface TianAiCaptchaConfig {
bindEl: TianAiCaptchaBindTarget
requestCaptchaDataUrl: string
types?: string[]
validCaptchaUrl?: string
requestHeaders?: Record<string, string>
timeToTimestamp?: boolean
validSuccess?: (res: any, captcha: any, tac: TianAiCaptchaInstance) => void
validFail?: (res: any, captcha: any, tac: TianAiCaptchaInstance) => void

View File

@ -10,11 +10,19 @@
<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">
<p class="type-hint">
可同时勾选多种类型不选时将按随机类型请求
</p>
<el-checkbox-group v-model="selectedTypes" size="large">
<el-checkbox-button v-for="item in captchaTypeOptions" :key="item.value" :value="item.value">
{{ item.label }}
</el-radio-button>
</el-radio-group>
</el-checkbox-button>
</el-checkbox-group>
<div class="type-actions">
<el-button link type="primary" @click="clearTypes" :disabled="selectedTypes.length === 0">恢复随机</el-button>
</div>
</el-card>
<el-card class="preview-card" shadow="never">
@ -23,7 +31,7 @@
ref="captchaRef"
class="captcha-box"
mode="validate"
:type="selectedType"
:types="selectedTypes"
@close="handleCaptchaClose"
@state-change="handleCaptchaStateChange"
@valid-fail="handleValidFail"
@ -38,7 +46,7 @@
import { onBeforeUnmount, ref } from 'vue'
import CaptchaPanel from '@/components/CaptchaPanel.vue'
type CaptchaType = 'RANDOM' | 'SLIDER' | 'ROTATE' | 'CONCAT' | 'WORD_IMAGE_CLICK'
type CaptchaType = 'SLIDER' | 'ROTATE' | 'CONCAT' | 'WORD_IMAGE_CLICK'
type CaptchaPanelRef = {
destroy: () => void
@ -47,7 +55,6 @@ type CaptchaPanelRef = {
}
const captchaTypeOptions: Array<{ label: string, value: CaptchaType }> = [
{ label: '随机', value: 'RANDOM' },
{ label: '滑块拼图', value: 'SLIDER' },
{ label: '旋转', value: 'ROTATE' },
{ label: '拼接', value: 'CONCAT' },
@ -56,17 +63,21 @@ const captchaTypeOptions: Array<{ label: string, value: CaptchaType }> = [
const captchaRef = ref<CaptchaPanelRef | null>(null)
const captchaActive = ref(false)
const selectedType = ref<CaptchaType>('RANDOM')
const selectedTypes = ref<CaptchaType[]>([])
async function startCaptcha() {
console.info('[captcha sandbox] init captcha', {
type: selectedType.value,
types: selectedTypes.value,
mode: 'validate'
})
await captchaRef.value?.init()
}
function clearTypes() {
selectedTypes.value = []
}
function reloadCaptcha() {
if (!captchaActive.value) return
console.info('[captcha sandbox] manual reload')
@ -107,6 +118,16 @@ onBeforeUnmount(() => captchaRef.value?.destroy())
margin-bottom: 24px;
}
.type-hint {
margin: 0 0 16px;
color: #606266;
line-height: 1.6;
}
.type-actions {
margin-top: 12px;
}
.sandbox-grid {
display: flex;
gap: 20px;