366 lines
9.1 KiB
Vue
366 lines
9.1 KiB
Vue
<template>
|
||
<div class="login">
|
||
<!--<div class="left">
|
||
<img src="@/assets/image/login/zu2408@3x.png" alt="">
|
||
</div>-->
|
||
<div class="center">
|
||
<div class="content-login">
|
||
<div class="wrap1">
|
||
<div class="wrap1_1">系统登录</div>
|
||
<!-- <div class="wrap1_2">{{ state.title }}!</div> -->
|
||
</div>
|
||
<div class="wrap2">
|
||
<n-form
|
||
ref="formRef"
|
||
:label-width="80"
|
||
:model="formValue"
|
||
size="large"
|
||
>
|
||
<n-form-item label="手机号" path="TelNum">
|
||
<n-input
|
||
v-model:value="formValue.TelNum"
|
||
placeholder="请输入手机号"
|
||
/>
|
||
</n-form-item>
|
||
<n-form-item
|
||
v-if="loginOptions === 'password'"
|
||
label="密码"
|
||
path="Password"
|
||
>
|
||
<n-input
|
||
type="password"
|
||
show-password-on="click"
|
||
v-model:value="formValue.Password"
|
||
placeholder="请输入密码"
|
||
/>
|
||
</n-form-item>
|
||
<n-form-item
|
||
v-if="loginOptions === 'code'"
|
||
label="验证码"
|
||
path="Code"
|
||
>
|
||
<n-input
|
||
:disabled="!formValue.TelNum"
|
||
v-model:value="formValue.Code"
|
||
placeholder="请输入验证码"
|
||
/>
|
||
<n-button
|
||
@click="startCountdown"
|
||
style="margin-left: 10px; width: 162px"
|
||
type="primary"
|
||
>
|
||
{{ countdown === 0 ? "获取验证码" : `${countdown}s` }}
|
||
</n-button>
|
||
</n-form-item>
|
||
</n-form>
|
||
</div>
|
||
<div class="wrap3">
|
||
<n-button
|
||
:loading="loading"
|
||
:disabled="isLoginDisabled"
|
||
@click="handleCheckUserDevice"
|
||
style="width: 100%; height: 56px; font-size: 18px; color: #fff"
|
||
type="primary"
|
||
>
|
||
登 录
|
||
</n-button>
|
||
<n-button
|
||
@click="changeLoginOption"
|
||
style="margin-top: 9px"
|
||
quaternary
|
||
type="primary"
|
||
>
|
||
{{ loginOptions === "code" ? "密码" : "验证码" }}登录
|
||
</n-button>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
<!-- <div class="right">
|
||
<img src="@/assets/image/login/lj475@3x.png" alt="">
|
||
</div>-->
|
||
<n-modal
|
||
v-model:show="state.dialogNewDevice"
|
||
style="width: 600px"
|
||
:mask-closable="false"
|
||
preset="card"
|
||
>
|
||
<template #header>
|
||
<div
|
||
class="col-12 row justify-center relative"
|
||
style="border-bottom: 1px solid #dfd7f2"
|
||
>
|
||
<div style="font-size: 22px; font-weight: bold; color: #1f2225ff">
|
||
警告
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<div class="row justify-center fl-pa-md">
|
||
<div
|
||
class="col-12 row justify-center fl-pa-md font-16"
|
||
style="margin: 60px 0"
|
||
>
|
||
该账号已在其他设备登录,是否下线其他设备并继续本次登录
|
||
</div>
|
||
<div class="col-12 row justify-center">
|
||
<n-button
|
||
tertiary
|
||
style="width: 161px; background: #c7c7c9ff; color: #ffffffff"
|
||
class="fl-mr-md"
|
||
@click="handleDialogCancel"
|
||
>取消</n-button
|
||
>
|
||
<n-button
|
||
tertiary
|
||
style="
|
||
width: 161px;
|
||
height: 34px;
|
||
background: #46299dff;
|
||
color: #fff;
|
||
"
|
||
:loading="loading"
|
||
@click="handleDialogSure"
|
||
>继续</n-button
|
||
>
|
||
</div>
|
||
</div>
|
||
</n-modal>
|
||
</div>
|
||
</template>
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
onUnmounted,
|
||
computed,
|
||
getCurrentInstance,
|
||
reactive,
|
||
watch,
|
||
} from "vue";
|
||
import { useRouter } from "vue-router";
|
||
import { Local } from "@/utils/storage.js";
|
||
import { authStore } from "@/store/modules/authorize.js";
|
||
import { Login, SendCode } from "@/api-norm/auth/index.js";
|
||
// import {destroySubApps} from '@/plugins/wujie/index.js'
|
||
import { processError, processSuccess } from "@/utils/helper/message";
|
||
const currentInstance = getCurrentInstance();
|
||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||
const authStoreData = authStore();
|
||
const router = useRouter();
|
||
const loginOptions = ref("password");
|
||
const loading = ref(false);
|
||
const isLoginDisabled = computed(() => {
|
||
if (loginOptions.value === "password") {
|
||
return !(formValue.value.TelNum && formValue.value.Password);
|
||
} else if (loginOptions.value === "code") {
|
||
return !(formValue.value.TelNum && formValue.value.Code);
|
||
}
|
||
});
|
||
const changeLoginOption = () => {
|
||
loginOptions.value = loginOptions.value === "password" ? "code" : "password";
|
||
};
|
||
const state = reactive({
|
||
dialogNewDevice: false,
|
||
title:
|
||
import.meta.env.VITE_APP_MODE === "main"
|
||
? "体制外管理系统"
|
||
: "管理系统",
|
||
});
|
||
|
||
const formValue = ref({
|
||
Code: "",
|
||
TelNum: "",
|
||
Password: "",
|
||
});
|
||
|
||
// 检测账户在线设备
|
||
const handleCheckUserDevice = () => {
|
||
let url = "user/v2/sample/account";
|
||
let params =
|
||
loginOptions.value === "code"
|
||
? { telNum: formValue.value.TelNum, code: formValue.value.Code }
|
||
: { telNum: formValue.value.TelNum, password: formValue.value.Password };
|
||
loading.value = true;
|
||
$request.HTTP.components.postDataByParams(url, params).then(
|
||
(res) => {
|
||
loading.value = false;
|
||
if (res.status === 0) {
|
||
if (res.data.isNowAlreadyLogin) {
|
||
state.dialogNewDevice = true;
|
||
return;
|
||
} else {
|
||
loginClick();
|
||
}
|
||
} else {
|
||
processError(res.msg);
|
||
}
|
||
},
|
||
(err) => {
|
||
loading.value = false;
|
||
processError(err.response.data.msg);
|
||
}
|
||
);
|
||
};
|
||
const handleDialogCancel = () => {
|
||
state.dialogNewDevice = false;
|
||
};
|
||
const handleDialogSure = () => {
|
||
state.dialogNewDevice = false;
|
||
loginClick();
|
||
};
|
||
const loginClick = async () => {
|
||
loading.value = true;
|
||
const res = await Login(
|
||
loginOptions.value === "code"
|
||
? { TelNum: formValue.value.TelNum, Code: formValue.value.Code }
|
||
: { TelNum: formValue.value.TelNum, Password: formValue.value.Password }
|
||
);
|
||
if (res.status === 0) {
|
||
loading.value = false;
|
||
|
||
Local.set("userInfo", res.data.AccountInfo);
|
||
Local.set("token", res.data.Token);
|
||
Local.set("RefreshToken", res.data.RefreshToken);
|
||
await authStoreData.getMenuList();
|
||
message.success('登录成功')
|
||
router.push("/setting");
|
||
|
||
} else {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
const countdown = ref(0);
|
||
const CODE_TIMEOUT = 60;
|
||
let timer = null;
|
||
const startCountdown = async () => {
|
||
if (!formValue.value.TelNum) {
|
||
message.warning('请输入手机号')
|
||
return;
|
||
}
|
||
|
||
if (countdown.value > 0) {
|
||
return; // 如果还在倒计时,直接返回
|
||
}
|
||
|
||
const res = await SendCode({ TelNum: formValue.value.TelNum });
|
||
if (res.status === 0) {
|
||
countdown.value = CODE_TIMEOUT;
|
||
timer = setInterval(() => {
|
||
countdown.value--;
|
||
if (countdown.value <= 0) {
|
||
clearInterval(timer);
|
||
}
|
||
}, 1000);
|
||
}
|
||
};
|
||
// destroySubApps()
|
||
onUnmounted(() => {
|
||
if (timer) {
|
||
clearInterval(timer);
|
||
}
|
||
});
|
||
watch(
|
||
() => formValue.value.Code,
|
||
(val) => {
|
||
if (val.length > 6) {
|
||
formValue.value.Code = val.slice(0, 6);
|
||
}
|
||
}
|
||
);
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@keyframes move1 {
|
||
from {
|
||
transform: translate(100%, -100%);
|
||
}
|
||
to {
|
||
transform: translate(0, 0);
|
||
}
|
||
}
|
||
@keyframes move {
|
||
from {
|
||
transform: translate(-100%, 100%);
|
||
}
|
||
to {
|
||
transform: translate(0, 0);
|
||
}
|
||
}
|
||
.img-login {
|
||
position: fixed;
|
||
top: 0;
|
||
right: 0;
|
||
left: 0;
|
||
bottom: 0;
|
||
z-index: -1;
|
||
}
|
||
.login {
|
||
background-image: url("@/assets/image/bg1.png");
|
||
overflow: hidden;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-repeat: no-repeat;
|
||
background-size: contain;
|
||
display: flex;
|
||
position: relative;
|
||
justify-content: center;
|
||
.center {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
.content2 {
|
||
margin-top: 67px;
|
||
width: 155px;
|
||
height: 24px;
|
||
}
|
||
.content-login {
|
||
border: 1px solid white;
|
||
box-shadow: 12px 17px 51px rgba(0, 0, 0, 0.07);
|
||
transition: all 0.5s;
|
||
box-sizing: border-box;
|
||
padding: 50px 24px 24px 24px;
|
||
background-color: #fff;
|
||
border-radius: 8px;
|
||
width: 522px;
|
||
height: 633px;
|
||
.wrap3 {
|
||
margin-top: 91px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.wrap2 {
|
||
margin-top: 58px;
|
||
}
|
||
.wrap1 {
|
||
.wrap1_1 {
|
||
color: #000;
|
||
font-size: 32px;
|
||
font-weight: bold;
|
||
}
|
||
.wrap1_2 {
|
||
color: #000;
|
||
font-size: 32px;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.right {
|
||
width: 520px;
|
||
height: 188px;
|
||
position: absolute;
|
||
top: 182px;
|
||
right: 0;
|
||
animation: move1 1s ease-out forwards;
|
||
}
|
||
.left {
|
||
width: 520px;
|
||
height: 188px;
|
||
position: absolute;
|
||
top: 825px;
|
||
left: 0;
|
||
animation: move 1s ease-out forwards;
|
||
}
|
||
}
|
||
</style>
|