318 lines
7.6 KiB
Vue
318 lines
7.6 KiB
Vue
<template>
|
|
<div class="auth-page">
|
|
<div class="auth-card">
|
|
<div class="auth-tabs">
|
|
<button :class="{ active: mode === 'login' }" @click="mode = 'login'">登录</button>
|
|
<button :class="{ active: mode === 'register' }" @click="mode = 'register'">注册</button>
|
|
</div>
|
|
|
|
<transition name="fade" mode="out-in">
|
|
<div key="login" v-if="mode === 'login'" class="auth-form">
|
|
<form @submit.prevent="handleLogin">
|
|
<h2>欢迎回来</h2>
|
|
<div class="form-group">
|
|
<input type="text" placeholder="用户名" v-model="loginInfo.user"/>
|
|
</div>
|
|
<div class="form-group">
|
|
<input :type="viewPSWD?'text':'password'" placeholder="密码" v-model="loginInfo.password"/>
|
|
</div>
|
|
<button type="submit">登录</button>
|
|
</form>
|
|
</div>
|
|
<div key="register" v-else class="auth-form">
|
|
<form @submit.prevent="handleRegister">
|
|
<h2>创建新账号</h2>
|
|
<div class="form-group">
|
|
<input type="text" placeholder="用户名" v-model="loginInfo.user"/>
|
|
</div>
|
|
<div class="form-group">
|
|
<input type="email" placeholder="邮箱" v-model="loginInfo.email"/>
|
|
</div>
|
|
<div class="form-group">
|
|
<input type="password" placeholder="密码" v-model="loginInfo.password"/>
|
|
</div>
|
|
<button type="submit">注册</button>
|
|
</form>
|
|
</div>
|
|
</transition>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import {ref} from 'vue'
|
|
import swal from "../utils/sweetalert.js";
|
|
import store from "../store/index.js";
|
|
import router from "../router/index.js";
|
|
import AuthService from "../../services/auth.js";
|
|
|
|
const mode = ref('login')
|
|
const viewPSWD = ref(false)
|
|
|
|
const loginInfo = ref({
|
|
user: '',
|
|
email: '',
|
|
password: ''
|
|
})
|
|
|
|
const handleLogin = async () => {
|
|
await (async function () {
|
|
if (
|
|
!(loginInfo.value.user && loginInfo.value.password)
|
|
) {
|
|
swal.tip('error', '用户名和密码不得为空!');
|
|
return;
|
|
}
|
|
store.commit('startLoading', "正在验证...");
|
|
|
|
const result = await AuthService.login(loginInfo.value.user, loginInfo.value.password);
|
|
|
|
if (result.code === 1) {
|
|
swal.tip('error', '用户名或密码错误!');
|
|
return;
|
|
}
|
|
if (result.code === 2) {
|
|
swal.tip('error', '服务端程序出错...');
|
|
return;
|
|
}
|
|
if (result.code === 3) {
|
|
swal.tip('error', '网络连接错误');
|
|
return;
|
|
}
|
|
store.commit('setToken', result.token);
|
|
|
|
store.commit('startLoading', '请稍后...');
|
|
await AuthService.setSelfInfo();
|
|
if (!store.state.token) {
|
|
swal.tip('error', '未知错误...');
|
|
return;
|
|
}
|
|
if (!store.state.userInfo.uid) {
|
|
swal.tip('info', '登录成功, 加载用户信息失败');
|
|
return;
|
|
}
|
|
store.commit('stopLoading');
|
|
swal.tip('success', '登录成功! 即将跳转');
|
|
loginInfo.value.isFinish = true;
|
|
setTimeout(() => {
|
|
if (router.currentRoute.value.path === '/login') {
|
|
router.push('/');
|
|
}
|
|
}, 2000);
|
|
}
|
|
)
|
|
();
|
|
store.commit('stopLoading');
|
|
}
|
|
|
|
const handleRegister = async () => {
|
|
if (!(loginInfo.value.user && loginInfo.value.password && loginInfo.value.email)) {
|
|
swal.tip('error', '用户名、邮箱及密码不得为空!');
|
|
store.commit('stopLoading');
|
|
return;
|
|
}
|
|
if (!(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(loginInfo.value.email))) {
|
|
swal.tip('error', '邮箱格式不正确');
|
|
store.commit('stopLoading');
|
|
return;
|
|
}
|
|
store.commit('startLoading', "请稍后...");
|
|
try {
|
|
|
|
const message = await AuthService.sendMessage(loginInfo.value.user, loginInfo.value.email, loginInfo.value.password);
|
|
|
|
if (message.code !== 0) {
|
|
swal.tip('error', '注册失败(已存在此用户)');
|
|
store.commit('stopLoading');
|
|
return;
|
|
}
|
|
|
|
} catch (e) {
|
|
console.warn(e);
|
|
swal.tip('error', '服务器连接错误...');
|
|
store.commit('stopLoading');
|
|
return;
|
|
}
|
|
store.commit('stopLoading');
|
|
swal.window('info', '邮箱验证链接发送成功!', '请前往邮箱查看', '好的', 'ok');
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* =======================================
|
|
全局变量
|
|
========================================== */
|
|
:global(:root) {
|
|
--bg-color: #000;
|
|
--accent-color: #005757;
|
|
--text-color: #fff;
|
|
--card-bg: rgba(0, 0, 0, 0.8);
|
|
--input-bg: #222;
|
|
--input-text: #fff;
|
|
--border-color: #00ffff;
|
|
}
|
|
|
|
:global(.theme-light) {
|
|
--bg-color: #fff;
|
|
--accent-color: #eea2a2;
|
|
--text-color: #2f2f2f;
|
|
--card-bg: #f9f9f9;
|
|
--input-bg: #fff;
|
|
--input-text: #2f2f2f;
|
|
--border-color: #2f2f2f;
|
|
}
|
|
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* =======================================
|
|
整体页面布局
|
|
========================================== */
|
|
.auth-page {
|
|
min-height: calc(100vh - 60px);
|
|
margin-top: 60px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
opacity: 0;
|
|
background: linear-gradient(45deg, var(--bg-color), var(--accent-color));
|
|
background-size: 400% 400%;
|
|
animation: gradientAnimation 15s ease infinite, bgDisplay 0.5s forwards;
|
|
padding: 1rem;
|
|
}
|
|
|
|
@keyframes bgDisplay {
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes gradientAnimation {
|
|
0% {
|
|
background-position: 0 50%;
|
|
}
|
|
50% {
|
|
background-position: 100% 50%;
|
|
}
|
|
100% {
|
|
background-position: 0 50%;
|
|
}
|
|
}
|
|
|
|
/* =======================================
|
|
表单卡片
|
|
========================================== */
|
|
.auth-card {
|
|
background: var(--card-bg);
|
|
padding: 2rem;
|
|
border-radius: 10px;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
|
width: 320px;
|
|
text-align: center;
|
|
height: 400px;
|
|
transform: translateY(20px);
|
|
opacity: 0;
|
|
animation: cardFadeIn 0.5s forwards;
|
|
}
|
|
|
|
@keyframes cardFadeIn {
|
|
to {
|
|
transform: translateY(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* =======================================
|
|
登册切换
|
|
========================================== */
|
|
.auth-tabs {
|
|
display: flex;
|
|
justify-content: center;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.auth-tabs button {
|
|
flex: 1;
|
|
background: transparent;
|
|
border: none;
|
|
padding: 0.5rem;
|
|
cursor: pointer;
|
|
font-size: 1.1rem;
|
|
color: var(--text-color);
|
|
border-bottom: 2px solid transparent;
|
|
transition: border-color 0.3s;
|
|
}
|
|
|
|
.auth-tabs button.active {
|
|
border-color: var(--accent-color);
|
|
font-weight: bold;
|
|
}
|
|
|
|
/* =======================================
|
|
表单
|
|
========================================== */
|
|
.auth-form h2 {
|
|
margin-bottom: 1rem;
|
|
color: var(--text-color);
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 1rem;
|
|
text-align: left;
|
|
}
|
|
|
|
.form-group input {
|
|
width: 100%;
|
|
padding: 0.75rem;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 5px;
|
|
background: var(--input-bg);
|
|
color: var(--input-text);
|
|
transition: border-color 0.3s, box-shadow 0.3s;
|
|
}
|
|
|
|
.form-group input:focus {
|
|
outline: none;
|
|
border-color: var(--accent-color);
|
|
box-shadow: 0 0 5px var(--accent-color);
|
|
}
|
|
|
|
.auth-form button {
|
|
width: 100%;
|
|
padding: 0.75rem;
|
|
border: none;
|
|
border-radius: 5px;
|
|
background: var(--accent-color);
|
|
color: var(--bg-color);
|
|
font-size: 1rem;
|
|
cursor: pointer;
|
|
transition: background 0.3s, transform 0.2s;
|
|
}
|
|
|
|
.auth-form button:hover {
|
|
filter: brightness(0.9);
|
|
}
|
|
|
|
.auth-form button:active {
|
|
transform: scale(0.980);
|
|
}
|
|
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.fade-enter-from,
|
|
.fade-leave-to {
|
|
opacity: 0;
|
|
}
|
|
|
|
.fade-enter-to,
|
|
.fade-leave-from {
|
|
opacity: 1;
|
|
}
|
|
</style>
|