feat: 更新邮箱验证逻辑,添加用户姓名字段,优化组件交互

This commit is contained in:
2025-12-12 02:17:35 +07:00
parent 8d6e1b3835
commit 2050184075
7 changed files with 59 additions and 36 deletions

View File

@@ -1,4 +1,5 @@
<script lang='ts' setup> <script lang='ts' setup>
import type { InputCustomEvent } from "@ionic/vue";
import { toastController } from "@ionic/vue"; import { toastController } from "@ionic/vue";
import { logoGoogle, phonePortraitOutline } from "ionicons/icons"; import { logoGoogle, phonePortraitOutline } from "ionicons/icons";
import { authClient } from "@/auth"; import { authClient } from "@/auth";
@@ -8,31 +9,31 @@ const emit = defineEmits<{
}>(); }>();
const model = defineModel({ type: String, required: true }); const model = defineModel({ type: String, required: true });
const inputInstance = useTemplateRef<InputInstance>("inputInstance");
function onEmailBlur() { function markTouched() {
const isEmailValid = emailPattern.test(model.value); inputInstance.value?.$el.classList.add("ion-touched");
if (!isEmailValid) {
toastController
.create({
message: "Please enter a valid email address.",
duration: 1500,
position: "bottom",
})
.then((toast) => {
toast.present();
});
}
} }
async function submitSendVerification() { function validate(value: string) {
const isEmailValid = emailPattern.test(model.value); inputInstance.value?.$el.classList.remove("ion-valid");
if (!isEmailValid) { inputInstance.value?.$el.classList.remove("ion-invalid");
const toast = await toastController.create({
message: "Please enter a valid email address.",
duration: 1500,
position: "bottom",
});
await toast.present(); if (value === "") {
return false;
}
const isEmailValid = emailPattern.test(model.value);
isEmailValid ? inputInstance.value?.$el.classList.add("ion-valid") : inputInstance.value?.$el.classList.add("ion-invalid");
return isEmailValid;
}
async function submitSendVerification() {
const isEmailValid = validate(model.value);
if (!isEmailValid) {
inputInstance.value?.$el.classList.remove("ion-invalid");
inputInstance.value?.$el.classList.add("ion-invalid");
inputInstance.value?.$el.classList.add("ion-touched");
return; return;
} }
@@ -60,7 +61,7 @@ async function submitSendVerification() {
<p>You'll use this email to login and access everything we have to offer.</p> <p>You'll use this email to login and access everything we have to offer.</p>
<div> <div>
<ui-input v-model="model" type="email" placeholder="Enter your email address" @ion-blur="onEmailBlur" /> <ui-input ref="inputInstance" v-model="model" type="email" placeholder="email@example.com" error-text="Please enter a valid email address." @ion-input="validate($event.target.value as string)" @ion-blur="markTouched" />
<ion-button expand="block" class="ion-margin-top" shape="round" @click="submitSendVerification"> <ion-button expand="block" class="ion-margin-top" shape="round" @click="submitSendVerification">
Sign up Sign up

View File

@@ -1,16 +1,15 @@
<script lang='ts' setup> <script lang='ts' setup>
import type { PropType } from "vue";
import type { AuthUserSignup } from "@/auth/type";
import { toastController } from "@ionic/vue"; import { toastController } from "@ionic/vue";
const props = defineProps<{
email: string;
}>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: "success", value: string): void; (e: "success", value: AuthUserSignup): void;
}>(); }>();
const model = defineModel({ type: String, required: true }); const model = defineModel({ type: Object as PropType<AuthUserSignup>, required: true });
async function submitSignup() { async function submitSignup() {
if (model.value.length !== 6) { if (model.value.verificationCode.length !== 6) {
const toast = await toastController.create({ const toast = await toastController.create({
message: "Please enter a valid 6-digit verification code.", message: "Please enter a valid 6-digit verification code.",
duration: 1500, duration: 1500,
@@ -27,12 +26,16 @@ async function submitSignup() {
<template> <template>
<h1><strong>Verify your email</strong></h1> <h1><strong>Verify your email</strong></h1>
<p>We have sent a verification code to {{ email }}. Please enter the code below to verify your email address.</p> <p>We have sent a verification code to {{ model.email }}. Please enter the code below to verify your email address.</p>
<div> <div>
<ion-item> <ion-input-otp v-model="model.verificationCode" :length="6" />
<ion-input-otp v-model="model" :length="6" /> <!--
</ion-item> <ui-input v-model="model.name" placeholder="Name" />
<ui-input v-model="model.password" placeholder="Password" />
<ui-input v-model="model.confirmPassword" placeholder="Confirm Password" /> -->
<ion-button expand="block" class="ion-margin-top" shape="round" @click="submitSignup"> <ion-button expand="block" class="ion-margin-top" shape="round" @click="submitSignup">
Submit Submit

View File

@@ -7,6 +7,7 @@ import Step1 from "./email/step1.vue";
import Step2 from "./email/step2.vue"; import Step2 from "./email/step2.vue";
const form = ref<AuthUserSignup>({ const form = ref<AuthUserSignup>({
name: "",
email: "", email: "",
password: "", password: "",
confirmPassword: "", confirmPassword: "",
@@ -16,6 +17,7 @@ const step = ref(1);
function reset() { function reset() {
step.value = 1; step.value = 1;
form.value.name = "";
form.value.email = ""; form.value.email = "";
form.value.password = ""; form.value.password = "";
form.value.confirmPassword = ""; form.value.confirmPassword = "";
@@ -52,7 +54,7 @@ async function submitSignup() {
<template> <template>
<Step1 v-if="step === 1" v-model="form.email" @success="step = 2" /> <Step1 v-if="step === 1" v-model="form.email" @success="step = 2" />
<Step2 v-else-if="step === 2" v-model="form.verificationCode" :email="form.email" @success="submitSignup" /> <Step2 v-else-if="step === 2" v-model="form" @success="submitSignup" />
</template> </template>
<style lang='css' scoped></style> <style lang='css' scoped></style>

View File

@@ -1,4 +1,5 @@
export interface AuthUserSignup { export interface AuthUserSignup {
name: string;
email: string; email: string;
password: string; password: string;
confirmPassword: string; confirmPassword: string;

View File

@@ -24,4 +24,7 @@ defineExpose({} as ComponentInstance<typeof IonInput>);
--color: var(--ui-input-color, #000); --color: var(--ui-input-color, #000);
--border-radius: 8px; --border-radius: 8px;
} }
.ui-input + .ui-input {
margin-top: 12px;
}
</style> </style>

View File

@@ -1 +1 @@
export const emailPattern = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/; export const emailPattern = /^(?=.{1,254}$)(?=.{1,64}@)[\w!#$%&'*+/=?^`{|}~-]+(?:\.[\w!#$%&'*+/=?^`{|}~-]+)*@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i;

View File

@@ -1,13 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { modelControllerSignup } from "@/auth"; import { authClient, modelControllerSignup } from "@/auth";
const page = useTemplateRef<PageInstance>("page"); const page = useTemplateRef<PageInstance>("page");
const { user } = useAuth(); const { user } = useAuth();
async function openSignin() {
const modal = await modelControllerSignup(page.value?.$el);
await modal.present();
}
async function openSignup() { async function openSignup() {
const modal = await modelControllerSignup(page.value?.$el); const modal = await modelControllerSignup(page.value?.$el);
await modal.present(); await modal.present();
} }
async function handleLogout() {
await authClient.signOut();
}
</script> </script>
<template> <template>
@@ -24,9 +31,15 @@ async function openSignup() {
</IonToolbar> </IonToolbar>
</IonHeader> </IonHeader>
<IonButton @click="openSignin">
Signin
</IonButton>
<IonButton @click="openSignup"> <IonButton @click="openSignup">
Signup Signup
</IonButton> </IonButton>
<IonButton @click="handleLogout">
Log out
</IonButton>
{{ user }} {{ user }}
</IonContent> </IonContent>