755 lines
20 KiB
Vue
755 lines
20 KiB
Vue
<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> |