branchErp/src/views/login.vue
2025-09-24 15:49:11 +08:00

366 lines
9.1 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="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>