ai-ground-quasar/src/pages/Login.vue

1017 lines
27 KiB
Vue
Raw Normal View History

2024-05-23 02:20:16 +00:00
<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"
2024-05-30 03:33:50 +00:00
@keyup="
() => {
telNum = telNum.trim();
}
"
2024-05-23 02:20:16 +00:00
placeholder="请输入账号"
/>
</div>
<div class="account-box">
<span class="account">密码</span>
<q-input
:type="isPwd ? 'password' : 'text'"
maxlength="8"
class="input-box"
v-model="password"
2024-05-30 03:33:50 +00:00
@keyup="
() => {
password = password.trim();
}
"
2024-05-23 02:20:16 +00:00
: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>
2024-05-30 03:33:50 +00:00
<div class="forget">
<span @click="forgetPwd">忘记密码?</span>
</div>
2024-05-23 02:20:16 +00:00
</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"
2024-05-30 03:33:50 +00:00
@keyup="
() => {
telNum = telNum.trim();
}
"
2024-05-23 02:20:16 +00:00
:dense="true"
placeholder="请输入账号"
/>
</div>
<div class="account-box">
<span class="account" style="transform: translateX(-100%)"
>验证码</span
>
<q-input
style="width: 250px"
2024-05-30 03:33:50 +00:00
:type="'text'"
2024-05-23 02:20:16 +00:00
class="input-box"
v-model="code"
2024-05-30 03:33:50 +00:00
@keyup="
() => {
code = code.trim();
}
"
2024-05-23 02:20:16 +00:00
: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>
2024-05-30 03:33:50 +00:00
<span style="color: #c348b8" @click="goAgreement"> 用户协议</span>
<span style="color: #c348b8" @click="goPolicy">隐私政策</span>
2024-05-23 02:20:16 +00:00
</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"
2024-05-30 03:33:50 +00:00
@keyup="
() => {
telNum = telNum.trim();
}
"
2024-05-23 02:20:16 +00:00
:dense="true"
placeholder="请输入账号"
/>
</div>
<div class="account-box">
<span class="account" style="transform: translateX(-100%)"
>验证码</span
>
<q-input
style="width: 238px"
2024-05-30 03:33:50 +00:00
:type="'text'"
@keyup="
() => {
code = code.trim();
}
"
2024-05-23 02:20:16 +00:00
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"
2024-05-30 03:33:50 +00:00
@keyup="
() => {
password = password.trim();
}
"
2024-05-23 02:20:16 +00:00
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"
2024-05-30 03:33:50 +00:00
@keyup="
() => {
verfyPassword = verfyPassword.trim();
}
"
2024-05-23 02:20:16 +00:00
: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"
2024-05-30 03:33:50 +00:00
@keyup="
() => {
telNum = telNum.trim();
}
"
2024-05-23 02:20:16 +00:00
placeholder="请输入账号"
/>
</div>
<div class="account-box">
<span class="account" style="transform: translateX(-100%)"
>验证码</span
>
<q-input
style="width: 232px"
2024-05-30 03:33:50 +00:00
:type="'text'"
2024-05-23 02:20:16 +00:00
class="input-box"
v-model="code"
2024-05-30 03:33:50 +00:00
@keyup="
() => {
code = code.trim();
}
"
2024-05-23 02:20:16 +00:00
: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"
2024-05-30 03:33:50 +00:00
@keyup="
() => {
password = password.trim();
}
"
2024-05-23 02:20:16 +00:00
: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"
2024-05-30 03:33:50 +00:00
@keyup="
() => {
verfyPassword = verfyPassword.trim();
}
"
2024-05-23 02:20:16 +00:00
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);
}
2024-05-30 03:33:50 +00:00
const inputNum = (val) => {
console.log(val, 123);
};
const goAgreement = () => {
router.push("/agreement");
};
const goPolicy = () => {
router.push("/policy");
};
2024-05-23 02:20:16 +00:00
// 登录
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 || !password.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 = "";
2024-05-30 03:33:50 +00:00
isFlipped.value = false;
2024-05-23 02:20:16 +00:00
} 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 = "";
2024-05-30 03:33:50 +00:00
password.value = "";
telNum.value = "";
verfyPassword.value = "";
isErr.value = false;
errMsg.value = "";
2024-05-23 02:20:16 +00:00
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;
}
2024-05-30 03:33:50 +00:00
// 密码不允许有空格
if (password.value.indexOf(" ") !== -1) {
2024-05-23 02:20:16 +00:00
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 = () => {
2024-05-30 03:33:50 +00:00
code.value = "";
password.value = "";
telNum.value = "";
verfyPassword.value = "";
isErr.value = false;
errMsg.value = "";
2024-05-23 02:20:16 +00:00
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);
2024-05-30 03:33:50 +00:00
height: 207%;
2024-05-23 02:20:16 +00:00
position: relative;
top: -437px;
2024-05-30 03:33:50 +00:00
width: 123%;
2024-05-23 02:20:16 +00:00
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>