feat: refactor layouts and login components
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user