119 lines
2.8 KiB
Vue
119 lines
2.8 KiB
Vue
<template>
|
|
<v-sheet v-bind="$attrs" class="verify-container mb-6 mb-md-4" color="transparent">
|
|
<div v-if="loading && !captchaImage" class="d-flex justify-center align-center py-4">
|
|
<v-progress-circular color="primary" indeterminate></v-progress-circular>
|
|
</div>
|
|
|
|
<div v-else class="d-flex align-center gap-2">
|
|
<!-- Captcha Image and Refresh -->
|
|
<div
|
|
class="captcha-wrapper d-flex align-center cursor-pointer me-1 me-md-2"
|
|
:title="props.refreshTitle"
|
|
@click="handleRefresh"
|
|
>
|
|
<img v-if="captchaImage" alt="Captcha" class="captcha-img" :src="captchaImage" />
|
|
<v-icon class="ms-2" color="grey" :icon="mdiRefresh"></v-icon>
|
|
</div>
|
|
|
|
<!-- Input and Verify -->
|
|
<v-text-field
|
|
v-model="inputCode"
|
|
:append-inner-icon="props.verified ? mdiCheckCircle : undefined"
|
|
bg-color="surface"
|
|
class="flex-grow-1"
|
|
color="primary"
|
|
density="compact"
|
|
:disabled="props.verified"
|
|
:error="!!errorMsg"
|
|
hide-details
|
|
:placeholder="props.captchaPlaceholder"
|
|
variant="outlined"
|
|
>
|
|
<template v-if="props.verified" #append-inner>
|
|
<v-icon color="success" :icon="mdiCheckCircle" />
|
|
</template>
|
|
</v-text-field>
|
|
</div>
|
|
|
|
<div v-if="errorMsg" class="text-caption text-error mt-1">
|
|
{{ errorMsg }}
|
|
</div>
|
|
</v-sheet>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { mdiCheckCircle, mdiRefresh } from '@mdi/js'
|
|
import { computed } from 'vue'
|
|
|
|
interface CaptchaPayload {
|
|
imgUrl?: string
|
|
id?: string
|
|
tokenValue?: string
|
|
}
|
|
|
|
interface Props {
|
|
captcha?: CaptchaPayload
|
|
modelValue?: string
|
|
loading?: boolean
|
|
errorMessage?: string
|
|
verified?: boolean
|
|
verifyText?: string
|
|
captchaPlaceholder?: string
|
|
refreshTitle?: string
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
captcha: undefined,
|
|
modelValue: '',
|
|
loading: false,
|
|
errorMessage: '',
|
|
verified: false,
|
|
verifyText: '驗證',
|
|
captchaPlaceholder: '驗證碼',
|
|
refreshTitle: '點擊刷新驗證碼',
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
(event: 'update:modelValue', value: string): void
|
|
(event: 'refresh'): void
|
|
}>()
|
|
|
|
const captchaImage = computed(() => props.captcha?.imgUrl ?? '')
|
|
|
|
const inputCode = computed({
|
|
get: () => props.modelValue,
|
|
set: (val: string) => emit('update:modelValue', val),
|
|
})
|
|
|
|
const errorMsg = computed(() => props.errorMessage)
|
|
|
|
const loading = computed(() => props.loading)
|
|
|
|
function handleRefresh() {
|
|
if (props.verified) return
|
|
emit('refresh')
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.verify-container {
|
|
width: 100%;
|
|
}
|
|
|
|
.captcha-wrapper {
|
|
border: 1px solid rgba(var(--v-theme-on-surface), 0.12);
|
|
border-radius: 4px;
|
|
padding: 0 8px;
|
|
height: 40px;
|
|
background: rgb(var(--v-theme-surface));
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.captcha-img {
|
|
height: 100%;
|
|
width: auto;
|
|
display: block;
|
|
}
|
|
</style>
|