m2pool_web_frontend/mining-pool/src/views/login/login.vue

755 lines
20 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="loginPage">
<section class="mobileMain" v-if="$isMobile">
<header class="headerBox">
<img
@click="handelJump(`/`)"
src="../../assets/mobile/login/LOGO.svg"
alt="logo"
loading="lazy"
/>
<span class="title">{{ $t(`home.MLogin`) }}</span>
<span></span>
</header>
<div class="imgTop">
<img
src="../../assets/mobile/login/logointop.svg"
alt="Login for mining"
loading="lazy"
/>
</div>
<section class="formInput">
<el-form
:model="loginForm"
status-icon
:rules="loginRules"
ref="ruleForm"
class="demo-ruleForm"
>
<el-form-item prop="userName">
<el-input
prefix-icon="el-icon-user"
v-model="loginForm.userName"
autocomplete="off"
:placeholder="$t('user.Account')"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
type="password"
v-model="loginForm.password"
prefix-icon="el-icon-unlock"
autocomplete="off"
showPassword
:placeholder="$t('user.password')"
></el-input>
</el-form-item>
<el-form-item prop="code">
<div class="verificationCode">
<el-input
type="text"
prefix-icon="el-icon-chat-line-square"
v-model="loginForm.code"
autocomplete="off"
:placeholder="$t(`user.verificationCode`)"
></el-input>
<el-button
class="codeBtn"
:disabled="btnDisabled"
@click="handelCode"
><span v-if="countDownTime < 60 && countDownTime > 0">{{
countDownTime
}}</span>
{{ $t(bthText) }}</el-button
>
</div>
</el-form-item>
<div class="registerBox">
<span class="noAccount">{{ $t("user.noAccount") }}</span>
<span style="color: #661fff" @click="handelJump(`register`)">{{
$t("user.register")
}}</span>
<span
style="color: #661fff"
class="forget"
@click="handelJump(`resetPassword`)"
>{{ $t("user.forgotPassword") }}</span
>
</div>
<el-form-item>
<el-button
style="
width: 100%;
background: #661fff;
color: aliceblue;
margin-top: 6%;
"
:loading="loginLoading"
@click="submitForm('ruleForm')"
>{{ $t("user.login") }}</el-button
>
<div style="text-align: left">
<el-radio @input="handelRadio" v-model="radio" label="zh"
>简体中文</el-radio
>
<el-radio @input="handelRadio" v-model="radio" label="en"
>English</el-radio
>
</div>
</el-form-item>
</el-form>
</section>
</section>
<section class="loginModular" v-else>
<div class="leftBox">
<img
class="logo"
src="../../assets/img/WILOGO.png"
alt="logo"
@click="handleClick"
/>
<img src="../../assets/img/logicon.png" alt="Login for mining" />
</div>
<div class="loginBox">
<div class="closeBox" @click="handleClick">
<i class="iconfont icon-guanbi1 close"></i>
</div>
<el-form
:model="loginForm"
status-icon
:rules="loginRules"
ref="ruleForm"
class="demo-ruleForm"
>
<el-form-item>
<p class="loginTitle">{{ $t(`user.login`) }}</p>
</el-form-item>
<el-form-item prop="userName">
<el-input
prefix-icon="el-icon-user"
v-model="loginForm.userName"
autocomplete="off"
:placeholder="$t('user.Account')"
type="email"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
type="password"
v-model="loginForm.password"
prefix-icon="el-icon-unlock"
autocomplete="off"
showPassword
:placeholder="$t('user.password')"
></el-input>
</el-form-item>
<el-form-item prop="code">
<div class="verificationCode">
<el-input
type="text"
prefix-icon="el-icon-chat-line-square"
v-model="loginForm.code"
autocomplete="off"
:placeholder="$t(`user.verificationCode`)"
></el-input>
<el-button
class="codeBtn"
:disabled="btnDisabled"
@click="handelCode"
><span v-if="countDownTime < 60 && countDownTime > 0">{{
countDownTime
}}</span>
{{ $t(bthText) }}</el-button
>
</div>
</el-form-item>
<div class="registerBox">
<span class="noAccount">{{ $t("user.noAccount") }}</span>
<span style="cursor: pointer" @click="handelJump(`/register`)">{{
$t("user.register")
}}</span>
<span class="forget" @click="handelJump(`/resetPassword`)">{{
$t("user.forgotPassword")
}}</span>
</div>
<el-form-item>
<el-button
style="
width: 100%;
background: #661fff;
color: aliceblue;
margin-top: 6%;
"
:loading="loginLoading"
@click="submitForm('ruleForm')"
>{{ $t("user.login") }}</el-button
>
<div style="text-align: left">
<el-radio @input="handelRadio" v-model="radio" label="zh"
>简体中文</el-radio
>
<el-radio @input="handelRadio" v-model="radio" label="en"
>English</el-radio
>
</div>
</el-form-item>
</el-form>
</div>
</section>
</div>
</template>
<script>
import {
getLogin,
getLoginCode,
getAccountGradeList,
getUserProfile,
} from "../../api/login";
import { encryption } from "../../utils/fun";
import { getAccountList } from "../../api/personalCenter";
export default {
data() {
return {
loginForm: {
userName: "",
password: "",
code: "",
},
loginRules: {
userName: [
{
required: true,
trigger: "blur",
message: this.$t(`user.inputEmail`),
// type: "email",
},
],
password: [
{
required: true,
trigger: "blur",
message: this.$t(`user.inputPassword`),
},
],
code: [
{
required: true,
trigger: "change",
message: this.$t(`user.inputCode`),
},
],
},
radio: `en`,
btnDisabled: false,
bthText: "user.obtainVerificationCode",
time: "",
loginLoading: false,
accountList: [],
loginCodeTime: "",
countDownTime: 60,
timer: null,
lang: "en",
};
},
computed: {
countDown() {
const minutes = Math.floor(this.countDownTime / 60);
const seconds = this.countDownTime % 60;
const m = minutes < 10 ? "0" + minutes : minutes;
const s = seconds < 10 ? "0" + seconds : seconds;
return `${s}`;
},
},
watch: {
"$i18n.locale": function () {
this.translate();
},
},
created() {
if (window.sessionStorage.getItem("exam_time")) {
this.countDownTime = Number(window.sessionStorage.getItem("exam_time"));
this.startCountDown();
this.btnDisabled = true;
this.bthText = `user.again`;
}
// if (window.sessionStorage.getItem("exam_time") == null){
// this.startCountDown()
// }else{
// this.countDownTime = Number(window.sessionStorage.getItem("exam_time"));
// this.startCountDown()
// }
},
mounted() {
this.lang = this.$i18n.locale; // 初始化语言值
this.radio = localStorage.getItem("lang")
? localStorage.getItem("lang")
: "en";
},
methods: {
translate() {
this.loginRules = {
// type: "email",
userName: [
{
required: true,
trigger: "blur",
message: this.$t(`user.inputEmail`),
},
],
password: [
{
required: true,
trigger: "blur",
message: this.$t(`user.inputPassword`),
},
],
code: [
{
required: true,
trigger: "change",
message: this.$t(`user.inputCode`),
},
],
};
},
//账户权限
async fetchJurisdiction() {
const data = await getUserProfile();
this.$addStorageEvent(1, "jurisdiction", JSON.stringify(data.data.role));
this.$addStorageEvent(1, "userEmail", JSON.stringify(data.data.email));
if (data && data.code == 200) {
this.$message({
message: this.$t(`user.loginSuccess`),
type: "success",
showClose: true,
});
this.$router.push(`/${this.lang}`);
}
},
async fetchAccountGradeList() {
const data = await getAccountGradeList();
if (data && data.code == 200) {
this.$addStorageEvent(
1,
`miningAccountList`,
JSON.stringify(data.data)
);
}
},
async fetchAccountList(params) {
const data = await getAccountList(params);
if (data && data.code == 200) {
this.accountList = data.data;
this.$addStorageEvent(
1,
`accountList`,
JSON.stringify(this.accountList)
);
}
},
async fetchLogin(params) {
this.loginLoading = true;
const data = await getLogin(params);
if (data && data.code === 200) {
this.$addStorageEvent(1, "userName", data.data.userName);
this.$addStorageEvent(
1,
"token",
JSON.stringify(data.data.access_token)
);
// 等待一小段时间确保写入完成
await new Promise(resolve => setTimeout(resolve, 50));
// 登录成功后
this.$bus.$emit('user-logged-in'); // 触发登录成功全局事件
this.fetchAccountList();
this.fetchAccountGradeList();
this.fetchJurisdiction();
}
this.loginLoading = false;
},
async fetchCOde(params) {
const data = await getLoginCode(params);
if (data && data.code == 200) {
this.$message({
message: this.$t(`user.codeSuccess`),
type: "success",
showClose: true,
});
}
},
handelCode() {
if (!this.loginForm.userName) {
this.$message({
message: this.$t(`user.inputAccount`),
type: "error",
customClass: "messageClass",
showClose: true,
});
return;
}
//邮箱格式验证 /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/;
let isMailbox = emailRegex.test(this.loginForm.userName);
if (!this.loginForm.userName || !isMailbox) {
this.$message({
message: this.$t(`user.emailVerification`),
type: "error",
showClose: true,
});
return;
}
this.fetchCOde({ account: this.loginForm.userName });
if (window.sessionStorage.getItem("exam_time") == null) {
this.startCountDown();
} else {
this.countDownTime = Number(window.sessionStorage.getItem("exam_time"));
this.startCountDown();
}
// this.time = 60;
// let timer = setInterval(()=>{
// if (this.time) {
// this.time--
// this.btnDisabled = true;
// // this.bthText=this.time+`s后重新获取`
// this.bthText=`user.again`
// }else {
// this.btnDisabled = false;
// this.bthText=`user.obtainVerificationCode`
// this.time = "";
// clearTimeout(timer)
// }
// },1000)
},
startCountDown() {
this.timer = setInterval(() => {
if (this.countDownTime <= 1) {
//当监测到countDownTime为0时,清除计数器并且移除sessionStorage,然后执行提交试卷逻辑
clearInterval(this.timer);
sessionStorage.removeItem("exam_time");
this.countDownTime = 60;
this.btnDisabled = false;
this.bthText = `user.obtainVerificationCode`;
} else if (this.countDownTime > 0) {
//每秒让countDownTime -1秒,并设置到sessionStorage中
this.countDownTime--;
this.btnDisabled = true;
this.bthText = `user.again`;
window.sessionStorage.setItem("exam_time", this.countDownTime);
}
}, 1000);
},
handelJump(url) {
// 移除开头的斜杠
const cleanPath = url.startsWith("/") ? url.slice(1) : url;
this.$router.push(`/${this.lang}/${cleanPath}`);
},
handelRadio(val) {
// 保存旧的语言值用于路径替换
const oldLang = this.lang;
// 更新语言设置
this.lang = val;
this.$i18n.locale = val;
localStorage.setItem("lang", val);
// 更新当前路由的语言部分
const currentPath = this.$route.path;
const newPath = currentPath.replace(`/${oldLang}`, `/${val}`);
// 保持查询参数
this.$router
.push({
path: newPath,
query: this.$route.query,
})
.catch((err) => {
if (err.name !== "NavigationDuplicated") {
console.error("Navigation failed:", err);
}
});
},
submitForm() {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
//去空格
this.loginForm.userName = this.loginForm.userName.trim();
this.loginForm.password = this.loginForm.password.trim();
//邮箱格式验证
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/;
let isMailbox = emailRegex.test(this.loginForm.userName);
if (!isMailbox) {
this.$message({
message: this.$t(`user.emailVerification`),
type: "error",
customClass: "messageClass",
showClose: true,
});
return;
// 用户名规则1.长度限制3<=用户名<=16; 字符限制:仅允许使用字母、数字、下划线 用户名必须以字母开头
// const regex = /^[a-zA-Z][a-zA-Z0-9_]{2,15}$/; // 正则表达式
// const isValid = regex.test(this.loginForm.userName);
// if (!isValid) {
// // 如果输入不符合要求,可以根据具体需求给出错误提示或进行其他处理
// this.$message({
// message: this.$t(`user.accountReminder`),
// type: "error",
// customClass: "messageClass",
// showClose: true
// });
// return;
// }
}
// 密码验证 8<=密码<=32 包含大小写字母、数字和特殊字符(!@#¥%……&.*
const regexPassword =
/^(?!.*[\u4e00-\u9fa5])(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\W_]+$)(?![a-z0-9]+$)(?![a-z\W_]+$)(?![0-9\W_]+$)[a-zA-Z0-9\W_]{8,32}$/; // 正则表达式
const PasswordIsValid = regexPassword.test(this.loginForm.password);
if (!PasswordIsValid) {
// 如果输入不符合要求,可以根据具体需求给出错误提示或进行其他处理
this.$message({
message: this.$t(`user.PasswordReminder`),
type: "error",
showClose: true,
});
return;
}
//加密
const form = { ...this.loginForm };
form.password = encryption(this.loginForm.password);
this.fetchLogin(form);
}
});
},
handleClick() {
this.$router.push(`/${this.lang}`);
},
},
};
</script>
<style scoped lang="scss">
.loginPage {
width: 100%;
height: 100%;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
padding: 100px 0px;
background-image: url(../../assets/img/logBg.png);
background-size: cover;
background-repeat: no-repeat;
.loginModular {
width: 50%;
height: 85%;
display: flex;
border-radius: 10px;
overflow: hidden;
box-shadow: 0px 0px 20px 18px #d6d2ea;
// box-shadow: 0px 0px 20PX 30PX #000;
}
.leftBox {
width: 47%;
height: 100%;
background-image: linear-gradient(150deg, #18b7e6 -20%, #651fff 60%);
position: relative;
.logo {
position: absolute;
left: 30px;
top: 18px;
width: 22%;
}
img {
width: 100%;
position: absolute;
right: -16%;
bottom: 0;
z-index: 99;
}
}
}
.el-form {
width: 90%;
padding: 0px 20px 50px 20px;
}
.el-form-item {
width: 100%;
}
.loginBox {
width: 53%;
// box-shadow: 0px 0px 5PX 1PX #ccc;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 10px;
position: relative;
flex-direction: column;
overflow: hidden;
background: #fff;
position: relative;
padding: 0px 35px;
.demo-ruleForm {
height: 100%;
padding-top: 5%;
}
img {
width: 18%;
position: absolute;
top: 20px;
left: 30px;
cursor: pointer;
}
.closeBox {
position: absolute;
top: 18px;
right: 30px;
cursor: pointer;
.close {
font-size: 1.3em;
}
}
.closeBox:hover {
color: #661fff;
}
}
.loginTitle {
font-size: 20px;
font-weight: 600;
margin-bottom: 30px;
text-align: center;
}
.loginColor {
width: 100%;
height: 15px;
background: #661fff;
}
.langBox {
display: flex;
justify-content: space-between;
align-content: center;
}
.registerBox {
display: flex;
justify-content: start;
margin: 0;
font-size: 12px;
height: 20px;
span {
padding: 5px 0px;
display: inline-block;
height: 100%;
z-index: 99;
}
span:hover {
color: #661fff;
}
.noAccount:hover {
color: #000;
}
.forget {
margin-left: 8px;
cursor: pointer;
}
}
.forget {
margin-left: 10px;
}
.forgotPassword {
display: inline-block;
width: 20%;
}
.verificationCode {
display: flex;
.codeBtn {
font-size: 13px;
margin-left: 2px;
}
}
// 手机端适配
@media screen and (min-width: 220px) and (max-width: 1279px) {
.mobileMain {
width: 100vw;
min-height: 100vh;
background: #fff;
background-image: url("../../assets/mobile/login/bgtop.svg");
background-repeat: no-repeat;
background-size: 115%;
background-position: 49% 47%;
}
.headerBox {
width: 100%;
height: 60px;
// background: palegoldenrod;
// border-bottom: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0px 20px;
box-shadow: 0px 0px 2px 1px #ccc;
img {
width: 30px;
}
.title {
font-weight: 600;
}
}
.imgTop {
width: 100%;
display: flex;
justify-content: center;
margin-top: 2%;
img {
height: 159px;
}
}
.formInput {
width: 100%;
display: flex;
justify-content: center;
}
.footer {
width: 100%;
height: 100px;
background: #651fff;
}
}
</style>