Files
NBA-Vue/src/components/AuthModal.vue

366 lines
7.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="auth-overlay" @click.self="close">
<div class="auth-modal">
<header class="auth-header">
<div class="tabs">
<button
:class="{ active: mode === 'login' }"
@click="switchMode('login')"
>
登录
</button>
<button
:class="{ active: mode === 'register' }"
@click="switchMode('register')"
>
注册
</button>
<button
:class="{ active: mode === 'reset' }"
@click="switchMode('reset')"
>
修改密码
</button>
</div>
<button class="close-btn" @click="close">×</button>
</header>
<form @submit.prevent="handleSubmit" class="auth-form">
<div class="form-group">
<label>邮箱</label>
<div class="email-row">
<input
v-model="form.email"
type="email"
required
placeholder="请输入邮箱"
/>
<button
v-if="mode !== 'login'"
type="button"
class="code-btn"
:disabled="countdown > 0"
@click="handleSendCode"
>
{{ countdown > 0 ? countdown + ' 秒后重发' : '发送验证码' }}
</button>
</div>
</div>
<div class="form-group" v-if="mode !== 'login'">
<label>邮箱验证码</label>
<input
v-model="form.code"
type="text"
required
placeholder="请输入邮箱验证码"
/>
</div>
<div class="form-group">
<label>{{ mode === 'reset' ? '新密码' : '密码' }}</label>
<input
v-model="form.password"
type="password"
required
placeholder="请输入密码"
/>
</div>
<div class="form-group" v-if="mode !== 'login'">
<label>确认密码</label>
<input
v-model="form.confirm"
type="password"
required
placeholder="请再次输入密码"
/>
</div>
<div class="actions">
<button type="submit" class="primary-btn">
{{
mode === 'login'
? '登录'
: mode === 'register'
? '注册'
: '修改密码'
}}
</button>
<button type="button" class="ghost-btn" @click="close">
取消
</button>
</div>
</form>
</div>
</div>
</template>
<script setup>
import { reactive, ref, watch, onUnmounted } from "vue";
const props = defineProps({
modelValue: {
type: Boolean,
default: false,
},
defaultMode: {
type: String,
default: "login",
},
});
const emit = defineEmits(["update:modelValue", "submit", "send-code"]);
const countdown = ref(0);
let timer = null;
const form = reactive({
email: "",
password: "",
confirm: "",
code: "",
});
const mode = ref(props.defaultMode || "login");
watch(
() => props.defaultMode,
() => {
resetForm();
mode.value = props.defaultMode || "login";
}
);
const resetForm = () => {
form.email = "";
form.password = "";
form.confirm = "";
form.code = "";
countdown.value = 0;
if (timer) {
clearInterval(timer);
timer = null;
}
};
const switchMode = (target) => {
if (target !== mode.value) {
mode.value = target;
resetForm();
}
};
const close = () => {
resetForm();
mode.value = props.defaultMode || "login";
emit("update:modelValue", false);
};
const isEmailValid = (email) => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
};
const handleSubmit = () => {
if (!isEmailValid(form.email)) {
alert("邮箱格式不正确");
return;
}
if (mode.value !== "login" && form.password !== form.confirm) {
alert("两次输入的密码不一致");
return;
}
if (mode.value !== "login" && !form.code) {
alert("请输入邮箱验证码");
return;
}
emit("submit", {
mode: mode.value,
email: form.email,
password: form.password,
code: form.code,
});
resetForm();
close();
};
const handleSendCode = () => {
if (countdown.value > 0) {
// 在倒计时中,直接忽略点击
return;
}
if (!form.email) {
alert("请先输入邮箱");
return;
}
if (!isEmailValid(form.email)) {
alert("邮箱格式不正确");
return;
}
// 通知父组件发送验证码
emit("send-code", { email: form.email, mode: mode.value });
// 这里默认认为发送成功,如果你要等后端返回再开始倒计时,
// 可以把下面这段逻辑放到父组件里,再通过 props 传回当前组件。
// alert("验证码发送成功");
countdown.value = 60;
timer = setInterval(() => {
countdown.value--;
if (countdown.value <= 0) {
countdown.value = 0;
clearInterval(timer);
timer = null;
}
}, 1000);
};
onUnmounted(() => {
if (timer) {
clearInterval(timer);
timer = null;
}
});
// const handleSendCode = () => {
// if (!form.email) {
// alert("请先输入邮箱");
// return;
// }
// if (!isEmailValid(form.email)) {
//
("邮箱格式不正确");
// return;
// }
// emit("send-code", { email: form.email });
// };
</script>
<style scoped>
.code-btn[disabled] {
opacity: 0.6;
cursor: not-allowed;
}
.auth-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 2000;
}
.auth-modal {
width: 360px;
background: #fff;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);
padding: 20px;
}
.auth-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.tabs {
display: flex;
gap: 8px;
}
.tabs button {
padding: 8px 14px;
border: none;
border-radius: 6px;
background: #f2f4f7;
cursor: pointer;
font-weight: 600;
}
.tabs button.active {
background: #1d428a;
color: #fff;
}
.close-btn {
border: none;
background: transparent;
font-size: 20px;
cursor: pointer;
}
.auth-form {
display: flex;
flex-direction: column;
gap: 12px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.email-row {
display: flex;
gap: 8px;
}
.email-row input {
flex: 1;
}
.code-btn {
white-space: nowrap;
padding: 0 12px;
border: 1px solid #1d428a;
background: #1d428a;
color: #fff;
border-radius: 6px;
cursor: pointer;
}
.form-group label {
font-size: 14px;
color: #444;
}
.form-group input {
padding: 10px 12px;
border-radius: 6px;
border: 1px solid #d9d9d9;
font-size: 14px;
}
.actions {
display: flex;
gap: 10px;
margin-top: 6px;
}
.primary-btn,
.ghost-btn {
flex: 1;
padding: 10px 12px;
border-radius: 6px;
border: none;
cursor: pointer;
font-weight: 600;
}
.primary-btn {
background: #1d428a;
color: #fff;
}
.ghost-btn {
background: #f2f4f7;
color: #1d428a;
}
</style>