增加邮箱校验
All checks were successful
site-dist-deploy / build-and-deploy (push) Successful in 1m54s

This commit is contained in:
speakeloudest 2026-01-05 18:37:02 -08:00
parent be35ecb62a
commit 595d1d0932
2 changed files with 110 additions and 8 deletions

View File

@ -1,15 +1,40 @@
<template>
<form @submit.prevent="handleLogin" class="flex flex-col gap-6 text-base text-black md:text-2xl">
<div class="overflow-hidden rounded-[20px] bg-[#78788029] px-4">
<div class="rounded-[20px] bg-[#78788029] px-4">
<div class="relative">
<Input
v-model="email"
type="email"
name="email"
autocomplete="email"
type="text"
name="user_email_identity"
autocomplete="new-password"
placeholder="Email"
class="h-[50px] border-none bg-transparent text-base focus-visible:ring-0 md:text-2xl"
@focus="isFocused = true"
@blur="onBlur"
@keydown="handleKeyDown"
/>
<transition name="fade">
<div
v-if="isFocused && suggestList.length > 0"
class="absolute top-[55px] left-0 z-[100] w-full rounded-xl border border-white/20 bg-white/95 p-1 shadow-2xl backdrop-blur-xl dark:bg-black/90"
>
<div
v-for="(item, index) in suggestList"
:key="item.full"
@mousedown="selectSuggest(item.full)"
:class="[
'flex cursor-pointer items-center rounded-lg px-3 py-2 text-sm transition-colors md:text-xl',
activeIndex === index ? 'bg-[#A8FF53]/10' : '',
]"
>
<span class="font-medium text-black/80">{{ prefix }}</span>
<span class="font-medium text-black">{{ item.userInputPart }}</span>
<span class="text-black opacity-30">{{ item.suggestPart }}</span>
</div>
</div>
</transition>
<div class="absolute bottom-0 left-0 h-[1px] w-full bg-gray-400/30"></div>
</div>
@ -56,7 +81,7 @@
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ref, computed, watch } from 'vue'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { toast } from 'vue-sonner'
@ -68,21 +93,90 @@ const CodeSentTipRef = ref<InstanceType<typeof CodeSentTip> | null>(null)
const email = ref('')
const code = ref('')
const countdown = ref(0)
const isFocused = ref(false)
const commonSuffixes = ['@gmail.com', '@outlook.com', '@qq.com', '@163.com']
const activeIndex = ref(-1) //
// 1. @
const prefix = computed(() => {
const atIndex = email.value.indexOf('@')
return atIndex > -1 ? email.value.slice(0, atIndex) : email.value
})
//
watch(email, () => {
activeIndex.value = -1
})
//
const handleKeyDown = (e: KeyboardEvent) => {
if (!suggestList.value.length) return
if (e.key === 'ArrowDown') {
e.preventDefault()
activeIndex.value = (activeIndex.value + 1) % suggestList.value.length
} else if (e.key === 'ArrowUp') {
e.preventDefault()
activeIndex.value =
(activeIndex.value - 1 + suggestList.value.length) % suggestList.value.length
} else if (e.key === 'Enter' && activeIndex.value !== -1) {
e.preventDefault()
selectSuggest(suggestList.value[activeIndex.value])
}
}
// 2.
const suggestList = computed(() => {
const val = email.value.trim()
if (!val || val.length < 1) return []
const atIndex = val.indexOf('@')
// "abc@gm" domainPart "@gm"
const domainPart = atIndex > -1 ? val.slice(atIndex) : ''
//
const matches = commonSuffixes.filter((s) => s.startsWith(domainPart) && s !== domainPart)
return matches.map((full) => {
return {
full: full, //
userInputPart: domainPart, //
suggestPart: full.replace(domainPart, ''), //
}
})
})
// 4.
const selectSuggest = (fullSuffix: string) => {
email.value = prefix.value + fullSuffix
isFocused.value = false
}
const onBlur = () => {
// blur
setTimeout(() => (isFocused.value = false), 200)
}
// 3.
const validateEmail = (str: string) => {
return /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(str)
}
const emit = defineEmits(['close'])
const handleGetCode = () => {
if (!email.value) {
toast('请输入邮箱')
return
}
CodeSentTipRef.value?.show()
//
if (!validateEmail(email.value)) {
return toast.error('邮箱格式无效,请检查')
}
request
.get<{ exist: boolean }>('/api/v1/auth/check', {
email: email.value,
})
.then(({ exist }) => {
console.log(exist)
request
.post('/api/v1/common/send_code', {
// 1=, 2=,
@ -90,6 +184,7 @@ const handleGetCode = () => {
type: exist ? 2 : 1,
})
.then(() => {
CodeSentTipRef.value?.show()
countdown.value = 60
const timer = setInterval(() => {
countdown.value--
@ -100,6 +195,13 @@ const handleGetCode = () => {
}
const router = useRouter()
const handleLogin = () => {
if (!code.value) {
toast('请输入验证码')
return
}
if (!validateEmail(email.value)) {
return toast.error('邮箱格式无效,请检查')
}
request
.post<any, { token: string }>('/api/v1/auth/login/email', {
email: email.value,

View File

@ -24,7 +24,7 @@
<button
v-else
@click="openLoginModal"
class="flex h-[30px] cursor-pointer items-center justify-center rounded-full bg-[#78788029] px-6 text-sm font-bold backdrop-blur-md transition hover:brightness-110 md:h-[60px] md:w-[220px] md:text-2xl"
class="flex h-[30px] cursor-pointer items-center justify-center rounded-full bg-[#78788029] px-6 text-sm font-bold backdrop-blur-md transition hover:brightness-110 md:h-[40px] md:w-[220px] md:text-xl"
>
登录 / 注册
</button>