Files
xsh-assistant-next/components/ModalAuthentication.vue
2024-02-28 10:38:39 +08:00

162 lines
5.6 KiB
Vue

<script setup lang="ts">
import {Label, PinInputInput, PinInputRoot} from 'radix-vue'
const toast = useToast()
const modal = useModal()
const sms_triggered = ref(false)
const sms_sending = ref(false)
const sms_counting_down = ref(0)
const final_loading = ref(false)
const handleComplete = (e: string[]) => toast.add({title: `提交验证码 ${e.join('')}`, color: 'indigo', icon: 'i-tabler-circle-check'})
const items = [
{
key: 'sms',
label: '短信登录',
icon: 'i-tabler-message-2',
description: '使用短信验证码登录,未注册的账号将自动注册'
},
{
key: 'account',
label: '密码登录',
icon: 'i-tabler-key',
description: '使用已有账号和密码登录'
},
]
const accountForm = reactive({username: '', password: ''})
const smsForm = reactive({mobile: '', sms_code: []})
function onSubmit(form: any) {
console.log('Submitted form:', form)
}
const obtainSmsCode = () => {
smsForm.sms_code = []
sms_sending.value = true
setTimeout(() => {
sms_triggered.value = true
sms_sending.value = false
sms_counting_down.value = 15 // TODO: modify this to change the countdown time
toast.add({title: '短信验证码已发送', color: 'indigo', icon: 'i-tabler-circle-check'})
const interval = setInterval(() => {
sms_counting_down.value--
if (sms_counting_down.value <= 0) {
clearInterval(interval)
}
}, 1000)
}, 2000)
}
</script>
<template>
<UModal prevent-close>
<UCard>
<template #header>
<div class="flex items-center justify-between">
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Modals
</h3>
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="modal.close()"/>
</div>
</template>
<UTabs :items="items" class="w-full">
<template #default="{ item, index, selected }">
<div class="flex items-center gap-2 relative truncate">
<UIcon :name="item.icon" class="w-4 h-4 flex-shrink-0"/>
<span class="truncate">{{ item.label }}</span>
<span v-if="selected" class="absolute -right-4 w-2 h-2 rounded-full bg-primary-500 dark:bg-primary-400"/>
</div>
</template>
<template #item="{ item }">
<UCard @submit.prevent="() => onSubmit(item.key === 'account' ? accountForm : smsForm)">
<template #header>
<p class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
{{ item.label }}
</p>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
{{ item.description }}
</p>
</template>
<div v-if="item.key === 'account'" class="space-y-3">
<UFormGroup label="用户名" name="username" required>
<UInput v-model="accountForm.username" :disabled="final_loading" required/>
</UFormGroup>
<UFormGroup label="密码" name="password" required>
<UInput v-model="accountForm.password" :disabled="final_loading" type="password" required/>
</UFormGroup>
</div>
<div v-else-if="item.key === 'sms'" class="space-y-3">
<UFormGroup label="手机号" name="mobile" required>
<UButtonGroup class="w-full">
<UInput v-model="smsForm.mobile" type="sms" class="w-full" required>
<template #leading>
<span class="text-gray-500 dark:text-gray-400 text-xs">+86</span>
</template>
</UInput>
<UButton :label="sms_counting_down ? `${sms_counting_down}秒后重发` : '获取验证码'"
@click="obtainSmsCode"
:loading="sms_sending" :disabled="!!sms_counting_down"
class="text-xs font-bold" color="gray"/>
</UButtonGroup>
</UFormGroup>
<Transition name="pin-root">
<div v-if="sms_triggered">
<Label for="pin-input" class="pin-label">验证码</Label>
<PinInputRoot
id="sms-input"
v-model="smsForm.sms_code"
:disabled="sms_sending"
placeholder="○"
class="w-full flex gap-2 justify-between items-center mt-1"
@complete="handleComplete"
type="number" otp required
>
<PinInputInput
v-for="(id, index) in 6"
:key="id"
:index="index"
class="pin-input"
/>
</PinInputRoot>
</div>
</Transition>
</div>
<template #footer v-if="item.key !== 'sms'">
<UButton type="submit" color="black" :disabled="final_loading">
登录
</UButton>
</template>
</UCard>
</template>
</UTabs>
</UCard>
</UModal>
</template>
<style scoped>
.pin-root-enter-active,
.pin-root-leave-active {
@apply transition duration-500;
}
.pin-root-enter-from,
.pin-root-leave-to {
@apply opacity-0 -translate-y-2;
}
.pin-input {
@apply w-full aspect-square rounded text-center shadow caret-transparent;
@apply outline-0 ring-indigo-500 focus:ring font-bold;
}
.pin-label {
@apply block text-sm font-medium text-gray-700 dark:text-gray-200 after:content-['*'] after:ms-0.5 after:text-red-500 dark:after:text-red-400;
}
</style>