ai-ground-quasar/src/pages/Login.vue
2024-05-31 16:27:32 +08:00

1017 lines
27 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="fullscreen">
<div class="fullscreen out-box">
<div class="bg">
<div
class="carousel"
:id="'silder' + (index + 1)"
v-for="(item, index) in 7"
>
<div class="carousel-track-container">
<ul class="carousel-track" :id="'silder-track' + (index + 1)">
<li class="carousel-slide">
<img src="../assets/image/train.png" />
</li>
<li class="carousel-slide">
<img src="../assets/image/glass.png" />
</li>
<li class="carousel-slide">
<img src="../assets/image/ast.png" />
</li>
<li class="carousel-slide">
<img src="../assets/image/oldman.png" />
</li>
<li class="carousel-slide">
<img src="../assets/image/patato.png" />
</li>
<li class="carousel-slide">
<img src="../assets/image/henshin.png" />
</li>
</ul>
</div>
</div>
</div>
</div>
<div :class="{ 're-item': true, 'is-flipped': isFlipped }">
<div class="login-box">
<img class="title" src="../assets/image/login/title.png" />
<div class="sub-title">登录得积分,畅享免费素材</div>
<q-tabs
v-model="tab"
inline-label
class="bg-black q-pl-xl q-pr-xl"
align="justify"
>
<q-tab name="acaount" label="账号登录" />
<q-tab name="code" label="验证码登录" />
</q-tabs>
<div>
<div
v-show="isErr"
style="display: flex; align-items: center; justify-content: center"
>
<img
style="width: 22px; height: 18px; margin-right: 5px"
src="../assets/image/login/warning.png"
/>
<span style="color: #e0378c">{{ errMsg }}</span>
</div>
<div v-if="tab === 'acaount'">
<div class="account-box">
<span class="account">账号</span>
<q-input
class="input-box"
v-model="telNum"
:dense="true"
@keyup="
() => {
telNum = telNum.trim();
}
"
placeholder="请输入账号"
/>
</div>
<div class="account-box">
<span class="account">密码</span>
<q-input
:type="isPwd ? 'password' : 'text'"
maxlength="8"
class="input-box"
v-model="password"
@keyup="
() => {
password = password.trim();
}
"
:dense="true"
placeholder="请输入密码"
>
<template v-slot:append>
<q-icon
color="grey-13"
:name="isPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="isPwd = !isPwd"
/>
</template>
</q-input>
</div>
<div class="forget">
<span @click="forgetPwd">忘记密码?</span>
</div>
</div>
<div v-if="tab === 'code'">
<div class="account-box">
<span class="account" style="transform: translateX(-100%)"
>手机号</span
>
<q-input
class="input-box"
v-model="telNum"
@keyup="
() => {
telNum = telNum.trim();
}
"
:dense="true"
placeholder="请输入账号"
/>
</div>
<div class="account-box">
<span class="account" style="transform: translateX(-100%)"
>验证码</span
>
<q-input
style="width: 250px"
:type="'text'"
class="input-box"
v-model="code"
@keyup="
() => {
code = code.trim();
}
"
:dense="true"
placeholder="请输入账号"
>
</q-input>
<q-btn
class="code-btn"
@click="getLoginCode"
:loading="codeLoading"
:disabled="loginRestSec < 60"
>
<div v-if="loginRestSec === 60 || loginRestSec < 1">
获取验证码
</div>
<div v-else>{{ loginRestSec }}</div>
</q-btn>
</div>
<div class="forget"></div>
</div>
</div>
<div class="read">
<q-checkbox v-model="readed" />
<a>我已阅读并同意</a>
<span style="color: #c348b8" @click="goAgreement"> 《用户协议》</span>
<span style="color: #c348b8" @click="goPolicy">《隐私政策》</span>
</div>
<q-btn
label="登录"
class="login-btn"
:loading="loading"
text-color="white"
@click="login"
/>
<div class="regist">
<a>去</a>
<span style="color: #c348b8" @click="toggleFlip">注册</span>
</div>
</div>
<div class="regist-box">
<div v-if="registType === 1">
<img class="title" src="../assets/image/login/regist.png" />
<div>
<div
v-show="isErr"
style="
display: flex;
align-items: center;
justify-content: center;
"
>
<img
style="width: 22px; height: 18px; margin-right: 5px"
src="../assets/image/login/warning.png"
/>
<span style="color: #e0378c">{{ errMsg }}</span>
</div>
<div>
<div class="account-box">
<span class="account" style="transform: translateX(-100%)"
>手机号</span
>
<q-input
class="input-box"
v-model="telNum"
@keyup="
() => {
telNum = telNum.trim();
}
"
:dense="true"
placeholder="请输入账号"
/>
</div>
<div class="account-box">
<span class="account" style="transform: translateX(-100%)"
>验证码</span
>
<q-input
style="width: 238px"
:type="'text'"
@keyup="
() => {
code = code.trim();
}
"
class="input-box"
v-model="code"
:dense="true"
placeholder="请输入验证码"
>
</q-input>
<q-btn
class="code-btn"
:loading="codeLoading"
:disabled="restSec < 60"
@click="getCode"
style="margin-right: 10px"
>
<div v-if="restSec === 60 || restSec < 1">获取验证码</div>
<div v-else>{{ restSec }}</div>
</q-btn>
</div>
<div class="account-box">
<span class="account" style="transform: translateX(-160%)"
>密码</span
>
<q-input
:type="isPwd ? 'password' : 'text'"
class="input-box"
v-model="password"
:dense="true"
@keyup="
() => {
password = password.trim();
}
"
placeholder="请输入密码"
maxlength="8"
style="margin-left: 10px"
>
<template v-slot:append>
<q-icon
color="grey-13"
:name="isPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="isPwd = !isPwd"
/>
</template>
</q-input>
</div>
<div class="account-box">
<span class="account" style="transform: translateX(-45%)"
>确认密码</span
>
<q-input
:type="isPwd ? 'password' : 'text'"
class="input-box"
v-model="verfyPassword"
@keyup="
() => {
verfyPassword = verfyPassword.trim();
}
"
:dense="true"
placeholder="请输入密码"
maxlength="8"
style="margin-right: 18px"
>
<template v-slot:append>
<q-icon
color="grey-13"
:name="isPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="isPwd = !isPwd"
/>
</template>
</q-input>
</div>
</div>
</div>
<q-btn
:loading="loading"
label="确定"
class="login-btn"
text-color="white"
@click="goRegist"
style="margin-top: 40px"
/>
<div class="regist">
<a>去</a>
<span style="color: #c348b8" @click="toggleFlip">登录</span>
</div>
</div>
<div
v-if="registType === 2"
style="
display: flex;
align-items: center;
flex-direction: column;
"
>
<img
style="width: 170px; height: 60px; margin-top: 20px"
src="../assets/image/login/success.png"
/>
<img
style="width: 200px; height: 200px; margin-top: 80px"
src="../assets/image/login/succicon.png"
/>
<q-btn
label="返回登录页"
class="login-btn"
text-color="white"
@click="backLogin"
style="margin-top: 80px"
/>
</div>
<div v-if="registType === 3">
<img
class="title"
style="width: 140px"
src="../assets/image/login/forgetpwd.png"
/>
<div>
<div
v-show="isErr"
style="
display: flex;
align-items: center;
justify-content: center;
"
>
<img
style="width: 22px; height: 18px; margin-right: 5px"
src="../assets/image/login/warning.png"
/>
<span style="color: #e0378c">{{ errMsg }}</span>
</div>
<div>
<div class="account-box">
<span class="account" style="transform: translateX(-100%)"
>手机号</span
>
<q-input
class="input-box"
v-model="telNum"
:dense="true"
@keyup="
() => {
telNum = telNum.trim();
}
"
placeholder="请输入账号"
/>
</div>
<div class="account-box">
<span class="account" style="transform: translateX(-100%)"
>验证码</span
>
<q-input
style="width: 232px"
:type="'text'"
class="input-box"
v-model="code"
@keyup="
() => {
code = code.trim();
}
"
:dense="true"
placeholder="请输入验证码"
>
</q-input>
<q-btn
class="code-btn"
:loading="codeLoading"
:disabled="restSec < 60"
@click="getRestCode"
style="margin-right: 10px"
>
<div v-if="restSec === 60 || restSec < 1">获取验证码</div>
<div v-else>{{ restSec }}</div>
</q-btn>
</div>
<div class="account-box">
<span class="account" style="transform: translateX(-90%)"
>新密码</span
>
<q-input
:type="isPwd ? 'password' : 'text'"
class="input-box"
v-model="password"
@keyup="
() => {
password = password.trim();
}
"
:dense="true"
placeholder="请输入密码"
style="margin-left: 5px"
maxlength="8"
>
<template v-slot:append>
<q-icon
color="grey-13"
:name="isPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="isPwd = !isPwd"
/>
</template>
</q-input>
</div>
<div class="account-box">
<span class="account" style="transform: translateX(-20%)"
>确认新密码</span
>
<q-input
:type="isPwd ? 'password' : 'text'"
class="input-box"
v-model="verfyPassword"
:dense="true"
@keyup="
() => {
verfyPassword = verfyPassword.trim();
}
"
placeholder="请输入密码"
style="margin-right: 25px"
maxlength="8"
>
<template v-slot:append>
<q-icon
color="grey-13"
:name="isPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="isPwd = !isPwd"
/>
</template>
</q-input>
</div>
</div>
</div>
<q-btn
:loading="loading"
label="确定"
class="login-btn"
text-color="white"
@click="goResetPwd"
style="margin-top: 40px"
/>
<div class="regist">
<a>去</a>
<span style="color: #c348b8" @click="toggleFlip">登录</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { LocalStorage, SessionStorage } from "quasar";
import { processError, processSuccess } from "../utils/message";
import { useRoute, useRouter } from "vue-router";
import { UserApi } from "src/api";
const router = useRouter();
const tab = ref("acaount");
const telNum = ref("");
const password = ref("");
const verfyPassword = ref("");
const loading = ref(false);
const codeLoading = ref(false);
const code = ref("");
const restSec = ref(60);
const loginRestSec = ref(60);
const isErr = ref(false);
const errMsg = ref("");
const isPwd = ref(true);
const readed = ref(false);
const registType = ref(1);
const isFlipped = ref(false);
// 滚动逻辑
function startScrolling(carousel, track) {
const slides = Array.from(track.children);
slides.forEach((slide) => {
track.appendChild(slide.cloneNode(true));
});
let scrollInterval;
const scrollSpeed = 1;
scrollInterval = setInterval(() => {
if (carousel.scrollTop >= track.scrollHeight / 2) {
carousel.scrollTop = 0;
} else {
carousel.scrollTop += scrollSpeed;
}
}, 20);
}
const inputNum = (val) => {
console.log(val, 123);
};
const goAgreement = () => {
router.push("/agreement");
};
const goPolicy = () => {
router.push("/policy");
};
// 登录
const login = async () => {
// 如果没有勾选同意协议
if (!readed.value) {
errMsg.value = "请先同意用户协议";
isErr.value = true;
return;
}
// 如果是账号登录,且账号密码为空
if (tab.value === "acaount" && (!telNum.value || !password.value)) {
errMsg.value = "账号或密码不能为空";
isErr.value = true;
return;
}
// 如果是验证码登录,且手机号或验证码为空
if (tab.value === "code" && (!telNum.value || !code.value)) {
errMsg.value = "手机号或验证码不能为空";
isErr.value = true;
return;
}
isErr.value = false;
errMsg.value = "";
loading.value = true;
const data = {
telNum: telNum.value,
};
if (tab.value === "acaount") {
data.password = password.value;
} else {
data.code = code.value;
}
try {
const res = await UserApi.login(data).then((res) => {
if (res.status === 0) {
loading.value = false;
LocalStorage.set("sd-token", res.data.token);
LocalStorage.set("sd-accountInfo", res.data.accountInfo);
router.push("/");
} else {
loading.value = false;
processError(res.msg);
errMsg.value = res.msg;
isErr.value = true;
}
});
} catch (error) {
loading.value = false;
console.error(error);
}
};
// 获取登录验证码
const getLoginCode = async () => {
if (!telNum.value) {
errMsg.value = "手机号不能为空";
isErr.value = true;
return;
}
isErr.value = false;
errMsg.value = "";
codeLoading.value = true;
const data = {
telNum: telNum.value,
};
try {
const res = await UserApi.getLoginCode(data).then((res) => {
if (res.status === 0) {
codeLoading.value = false;
let countdown = 60;
const countdownTimer = setInterval(() => {
countdown--;
loginRestSec.value = countdown;
if (countdown === 0) {
clearInterval(countdownTimer);
loginRestSec.value = 60;
}
}, 1000);
} else {
codeLoading.value = false;
errMsg.value = res.msg;
isErr.value = true;
}
});
} catch (error) {
codeLoading.value = false;
console.error(error);
}
};
// 获取注册验证码
const getCode = async () => {
if (!telNum.value) {
errMsg.value = "手机号不能为空";
isErr.value = true;
return;
}
isErr.value = false;
errMsg.value = "";
codeLoading.value = true;
const data = {
telNum: telNum.value,
};
try {
await UserApi.getRegisterCode(data).then((res) => {
if (res.status === 0) {
codeLoading.value = false;
let countdown = 60;
const countdownTimer = setInterval(() => {
countdown--;
restSec.value = countdown;
if (countdown === 0) {
clearInterval(countdownTimer);
restSec.value = 60;
}
}, 1000);
} else {
codeLoading.value = false;
errMsg.value = res.msg;
isErr.value = true;
}
});
} catch (error) {
codeLoading.value = false;
console.error(error);
}
};
// 获取忘记密码验证码
const getRestCode = async () => {
if (!telNum.value) {
errMsg.value = "手机号不能为空";
isErr.value = true;
return;
}
isErr.value = false;
errMsg.value = "";
codeLoading.value = true;
const data = {
telNum: telNum.value,
};
try {
await UserApi.getForgetPwdCode(data).then((res) => {
console.log(res);
if (res.status === 0) {
codeLoading.value = false;
let countdown = 60;
const countdownTimer = setInterval(() => {
countdown--;
restSec.value = countdown;
if (countdown === 0) {
clearInterval(countdownTimer);
restSec.value = 60;
}
}, 1000);
} else {
codeLoading.value = false;
errMsg.value = res.msg;
isErr.value = true;
}
});
} catch (error) {
codeLoading.value = false;
console.error(error);
}
};
// 重置密码
const goResetPwd = async () => {
if (!telNum.value || !code.value || !password.value || !verfyPassword.value) {
errMsg.value = "请填写完整信息";
isErr.value = true;
return;
}
if (password.value !== verfyPassword.value) {
errMsg.value = "两次密码不一致";
isErr.value = true;
return;
}
// 密码不允许有空格
if (password.value.indexOf(" ") !== -1) {
errMsg.value = "密码不允许有空格";
isErr.value = true;
return;
}
isErr.value = false;
errMsg.value = "";
loading.value = true;
const data = {
telNum: telNum.value,
code: code.value,
password: password.value,
};
try {
const res = await UserApi.resetPassword(data).then((res) => {
if (res.status === 0) {
loading.value = false;
code.value = "";
isFlipped.value = false;
} else {
loading.value = false;
errMsg.value = res.msg;
isErr.value = true;
}
});
} catch (error) {
loading.value = false;
console.error(error);
}
};
function toggleFlip() {
isFlipped.value = !isFlipped.value;
code.value = "";
password.value = "";
telNum.value = "";
verfyPassword.value = "";
isErr.value = false;
errMsg.value = "";
registType.value = 1;
}
const forgetPwd = () => {
isFlipped.value = true;
password.value = "";
registType.value = 3;
};
// 注册
const goRegist = async () => {
if (!telNum.value || !code.value || !password.value || !verfyPassword.value) {
errMsg.value = "请填写完整信息";
isErr.value = true;
return;
}
if (password.value !== verfyPassword.value) {
errMsg.value = "两次密码不一致";
isErr.value = true;
return;
}
// 密码不允许有空格
if (password.value.indexOf(" ") !== -1) {
errMsg.value = "密码不允许有空格";
isErr.value = true;
return;
}
isErr.value = false;
errMsg.value = "";
loading.value = true;
const data = {
telNum: telNum.value,
code: code.value,
password: password.value,
};
try {
const res = await UserApi.register(data).then((res) => {
if (res.status === 0) {
loading.value = false;
code.value = "";
registType.value = 2;
} else {
loading.value = false;
errMsg.value = res.msg;
isErr.value = true;
}
});
} catch (error) {
loading.value = false;
console.error(error);
}
};
// 返回登陆
const backLogin = () => {
code.value = "";
password.value = "";
telNum.value = "";
verfyPassword.value = "";
isErr.value = false;
errMsg.value = "";
isFlipped.value = false;
};
onMounted(() => {
const carousels = document.querySelectorAll(".carousel");
carousels.forEach((carousel) => {
const track = carousel.querySelector(".carousel-track");
startScrolling(carousel, track);
});
});
</script>
<style scoped lang="scss">
.out-box {
z-index: 1;
}
.carousel {
overflow: hidden;
height: 100%;
background: #000000;
}
.bg {
display: flex;
transform: rotate(30deg);
height: 207%;
position: relative;
top: -437px;
width: 123%;
left: -133px;
}
/* 创建一个伪元素作为黑色透明蒙版 */
.bg::before {
content: ""; /* 伪元素内容为空 */
display: block; /* 或者使用 inline-block根据需要 */
position: absolute; /* 相对于容器进行绝对定位 */
top: 0;
left: 0;
width: 100%; /* 蒙版覆盖整个容器宽度 */
height: 100%; /* 蒙版覆盖整个容器高度 */
background-color: rgba(0, 0, 0, 0.7); /* 设置黑色背景且透明度为0.7 */
z-index: 1; /* 设置层级确保蒙版出现在内容之上根据需要调整 */
}
.carousel-track {
list-style: none;
padding: 0;
margin: 0;
position: relative;
}
.carousel-slide {
margin: 10px 5px 10px 5px;
}
.carousel-slide img {
width: 100%;
display: block;
-webkit-user-drag: none;
-moz-user-drag: none;
}
.login-box {
backface-visibility: hidden;
position: absolute;
width: 911px;
height: 591px;
background: #000000;
border-radius: 8px;
padding: 20px;
box-sizing: border-box;
text-align: center;
z-index: 99;
.title {
width: 180px;
height: 67px;
}
.sub-title {
font-size: 20px;
color: #c348b8;
font-weight: bolder;
margin-bottom: 20px;
}
}
.account-box {
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
margin-top: 30px;
.account {
color: #bebebe;
transform: translateX(-120%);
}
.input-box {
width: 370px;
padding-bottom: 0;
}
}
.forget {
color: #c348b8;
font-size: 16px;
margin-top: 5px;
cursor: pointer;
transform: translateX(19%);
}
.code-btn {
margin-left: 20px;
background: linear-gradient(to right, #591df5, #c448b8);
color: #fff;
font-weight: bold;
border: 8px;
height: 45px;
min-width: 105px;
}
.read {
display: flex;
justify-content: center;
align-items: center;
margin-top: 30px;
color: #bebebe;
font-size: 15px;
span {
cursor: pointer;
}
}
.login-btn {
margin-top: 10px;
background: linear-gradient(to right, #591df5, #c448b8);
width: 436px;
height: 45px;
border-radius: 8px;
}
.regist {
margin-top: 20px;
color: #bebebe;
font-size: 16px;
span {
cursor: pointer;
margin-left: 10px;
}
}
.regist-box {
backface-visibility: hidden;
transform: rotateY(180deg);
position: absolute;
width: 911px;
height: 591px;
background: #000000;
border-radius: 8px;
padding: 20px;
box-sizing: border-box;
text-align: center;
z-index: 99;
.title {
width: 90px;
height: 57px;
}
}
.re-item {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
z-index: 99;
transition: transform 0.8s;
transform-style: preserve-3d;
perspective: 1000px;
}
.is-flipped {
transform: rotateY(180deg);
}
:deep(.q-tab--active .q-tab__indicator) {
opacity: 1;
width: 50px;
left: 44%;
height: 5px;
background: linear-gradient(to right, #be45ba, #5d1ef1);
}
:deep(.q-tab--active .q-tab__label) {
color: #fff;
font-size: 20px;
font-weight: bold;
text-shadow: 0px 3px 4px #de53ab;
}
:deep(.q-tab--inactive .q-tab__label) {
color: #bebebe;
font-size: 20px;
font-weight: bold;
}
:deep(.q-field__inner) {
background: #212121;
padding: 0 10px 0 10px;
border-radius: 8px;
border: 2px solid transparent;
.q-field__native {
color: #7e7e7e;
}
}
:deep(.q-field__inner::before) {
content: "";
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: -1;
margin: -4px;
border-radius: inherit; /*important*/
background: linear-gradient(270deg, #e44ea8 0%, #5f33f3 49%, #61d2ef 100%);
}
:deep(.q-field--standard .q-field__control:after) {
display: none;
}
:deep(.q-checkbox__svg) {
color: #c348b8;
}
:deep(.q-checkbox__bg) {
background: none;
border: 2px solid #c348b8;
}
</style>