<script setup lang="tsx">
|
import Motion from "./utils/motion";
|
import { useRouter } from "vue-router";
|
import { message } from "@/utils/message";
|
import { loginRules } from "./utils/rule";
|
import {
|
reactive,
|
computed,
|
ref,
|
Ref,
|
h,
|
onMounted,
|
defineAsyncComponent,
|
onUnmounted,
|
watch
|
} from "vue";
|
import { debounce, storageLocal } from "@pureadmin/utils";
|
import { useNav } from "@/layout/hooks/useNav";
|
import { useEventListener } from "@vueuse/core";
|
import type { FormInstance } from "element-plus";
|
import {
|
ElForm,
|
ElFormItem,
|
ElRadioGroup,
|
ElRadio,
|
ElRow,
|
ElTag
|
} from "element-plus";
|
import { useLayout } from "@/layout/hooks/useLayout";
|
import { useUserStoreHook } from "@/store/modules/user";
|
import { initRouter, getTopMenu } from "@/router/utils";
|
import { bg, avatar, logo1 } from "./utils/static";
|
// import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
|
import { useRoute } from "vue-router";
|
|
const route = useRoute();
|
import { captcha, phoneNumberCode, exRole } from "@/api/register/index";
|
|
defineOptions({
|
name: "Login"
|
});
|
|
const router = useRouter();
|
const loading = ref(false);
|
const disabled = ref(false);
|
const ruleFormRef = ref<FormInstance>();
|
|
const { initStorage } = useLayout();
|
initStorage();
|
|
const { dataTheme, overallStyle, dataThemeChange } = useDataThemeChange();
|
dataThemeChange(overallStyle.value);
|
const { title } = useNav();
|
// 获取验证码
|
const getCaptcha = async () => {
|
// if (!state.captchaEnabled) return;
|
state.ruleForm.code = "";
|
const res = await captcha();
|
state.captchaImage = "data:text/html;base64," + res.result?.img;
|
state.expirySeconds = res.result?.expirySeconds;
|
state.ruleForm.codeId = res.result?.id;
|
};
|
import {
|
addDialog,
|
closeDialog,
|
updateDialog,
|
closeAllDialog
|
} from "@/components/ReDialog";
|
const state = reactive({
|
isShowPassword: false,
|
ruleForm: {
|
account: "",
|
nickName: "",
|
phone: "",
|
phoneVCode: "",
|
code: "",
|
codeId: "",
|
email: "",
|
exRoleCode: ""
|
},
|
rules: {
|
code: [
|
{
|
required: true,
|
message: "请输入手机验证码",
|
trigger: "blur"
|
}
|
],
|
phone: [
|
{
|
required: true,
|
message: "请输入您的手机号码",
|
trigger: "blur"
|
}
|
],
|
exRoleCode: [
|
{
|
required: true,
|
message: "请选择角色",
|
trigger: "blur"
|
}
|
]
|
// code: [{ required: true, message: t('message.account.placeholder4'), trigger: 'blur' }],
|
},
|
loading: {
|
signIn: false
|
},
|
captchaImage: "",
|
rotateVerifyVisible: false,
|
// rotateVerifyImg: verifyImg,
|
// rotateVerifyImg: themeConfig.value.logoUrl,
|
secondVerEnabled: false,
|
// captchaEnabled: false,
|
isPassRotate: false,
|
capsLockVisible: false,
|
hideTenantForLogin: false,
|
expirySeconds: 60, // 验证码过期时间
|
phoneSeconds: 0, // 手机验证码倒计时
|
roleList: [],
|
myEnterpriseList: [],
|
nowRole: {}
|
});
|
let secondCode = "";
|
let secondId: Ref<string | number | boolean> = ref("");
|
// 验证码过期计时器
|
let timer: any = null;
|
let phonetimer: any = null;
|
|
// 页面初始化
|
onMounted(async () => {
|
getCaptcha();
|
// 注册验证码过期计时器
|
timer = setInterval(() => {
|
if (state.expirySeconds > 0) state.expirySeconds -= 1;
|
}, 1000);
|
});
|
// 页面卸载
|
onUnmounted(() => {
|
// 销毁验证码过期计时器
|
clearInterval(timer);
|
timer = null;
|
clearInterval(phonetimer);
|
phonetimer = null;
|
});
|
const openDialog = () => {
|
addDialog({
|
width: "30%",
|
title: "选择登录公司",
|
contentRenderer: () =>
|
h(
|
ElForm,
|
{
|
ref: ruleFormRef,
|
model: state.ruleForm,
|
rules: loginRules,
|
size: "large"
|
},
|
[
|
h(Motion, { delay: 150 }, [
|
h(
|
ElFormItem,
|
{
|
prop: "exRoleCode",
|
label: ""
|
},
|
[
|
// 单选框组 - 纵向排列
|
h(
|
ElRadioGroup,
|
{
|
modelValue: secondId.value,
|
"onUpdate:modelValue": val => {
|
secondId.value = val;
|
}
|
},
|
[
|
// 循环渲染单选框 - 根据每项的isManager显示标签
|
state.myEnterpriseList.map(item =>
|
h(
|
"el-row",
|
{
|
style: {
|
width: "100%"
|
}
|
},
|
[
|
h(
|
"el-col",
|
{
|
span: 24
|
},
|
[
|
h(
|
ElRadio,
|
{
|
key: item.customerUserID,
|
value: item.customerUserID,
|
label: item.enterpriseName
|
},
|
[
|
// 单选框文本
|
// 根据当前项的isManager显示标签(放在文本后面)
|
item.isManger
|
? h(
|
ElTag,
|
{
|
type: "primary",
|
size: "small",
|
style: {
|
marginLeft: "8px",
|
alignSelf: "center"
|
}
|
},
|
"管理员"
|
)
|
: null
|
]
|
)
|
]
|
)
|
]
|
)
|
)
|
]
|
)
|
]
|
)
|
])
|
]
|
),
|
// jsx 语法 (注意在.vue文件启用jsx语法,需要在script开启lang="tsx")
|
closeCallBack: ({ options, args }) => {
|
if (args?.command === "cancel") {
|
// 您点击了取消按钮
|
} else if (args?.command === "sure") {
|
let obj = {
|
phone: state.ruleForm.phone,
|
code: secondCode,
|
id: secondId.value
|
};
|
useUserStoreHook()
|
.loginByUsername(obj)
|
.then(res => {
|
if (res?.code == 200) {
|
message("登录成功!", { type: "success" });
|
router.replace({
|
path: "/Index"
|
});
|
} else {
|
message(res?.message, { type: "warning" });
|
}
|
});
|
} else {
|
}
|
}
|
});
|
};
|
|
// 点击登录
|
const onLogin = async (formEl: FormInstance | undefined) => {
|
if (!formEl) return;
|
await formEl.validate(valid => {
|
if (valid) {
|
let obj = {
|
phone: state.ruleForm.phone,
|
code: state.ruleForm.phoneVCode,
|
exRuleCode: state.ruleForm.exRoleCode
|
};
|
loading.value = true;
|
useUserStoreHook()
|
.loginByUsername(obj)
|
.then(res => {
|
if (res.code == 200) {
|
// 获取后端路由
|
return initRouter().then(() => {
|
let obj = res.result;
|
if (obj.theLastLogo) {
|
router
|
.replace({
|
name: "Index"
|
})
|
.then(() => {
|
message("登录成功", { type: "success" });
|
})
|
.finally(() => (disabled.value = false));
|
} else {
|
state.myEnterpriseList = obj.customerExs;
|
secondCode = obj.code;
|
openDialog();
|
}
|
});
|
} else {
|
message(res?.message || "登录失败", { type: "error" });
|
}
|
})
|
.finally(() => {
|
loading.value = false;
|
disabled.value = false;
|
});
|
}
|
});
|
};
|
|
// 发送手机验证码
|
const sendValidationCode = async () => {
|
if (!state.ruleForm.phone) {
|
return message("请先输入手机号", { type: "warning" });
|
}
|
if (!state.ruleForm.code) {
|
return message("请先输入验证码", { type: "warning" });
|
}
|
const res = await phoneNumberCode(state.ruleForm);
|
if (res?.code != 200) {
|
return message(res?.message, { type: "warning" });
|
}
|
state.phoneSeconds = 60;
|
phonetimer = setInterval(() => {
|
if (state.phoneSeconds > 0) state.phoneSeconds -= 1;
|
}, 1000);
|
};
|
const immediateDebounce: any = debounce(
|
formRef => onLogin(formRef),
|
1000,
|
true
|
);
|
|
useEventListener(document, "keydown", ({ code }) => {
|
if (
|
["Enter", "NumpadEnter"].includes(code) &&
|
!disabled.value &&
|
!loading.value
|
)
|
immediateDebounce(ruleFormRef.value);
|
});
|
</script>
|
|
<template>
|
<div class="select-none">
|
<div class="wave">
|
<img width="400px" :src="logo1" class="logo1" />
|
</div>
|
<div class="login-container">
|
<div class="img" />
|
<div class="login-box">
|
<div class="login-form">
|
<Motion>
|
<h2 class="logintitle">登录</h2>
|
</Motion>
|
|
<el-form
|
ref="ruleFormRef"
|
:model="state.ruleForm"
|
:rules="loginRules"
|
size="large"
|
>
|
<Motion :delay="100">
|
<el-form-item
|
:rules="[
|
{
|
required: true,
|
message: '请输入手机号',
|
trigger: 'blur'
|
}
|
]"
|
prop="phone"
|
>
|
<el-input
|
v-model="state.ruleForm.phone"
|
clearable
|
placeholder="手机号"
|
/>
|
</el-form-item>
|
</Motion>
|
|
<Motion :delay="150">
|
<el-form-item class="login-animation3" prop="code">
|
<el-col :span="15">
|
<el-input
|
ref="codeRef"
|
v-model="state.ruleForm.code"
|
text
|
maxlength="4"
|
placeholder="请输入验证码"
|
clearable
|
autocomplete="off"
|
/>
|
</el-col>
|
<el-col :span="1" />
|
<el-col :span="8">
|
<div
|
:class="[
|
state.expirySeconds > 0
|
? 'login-content-code'
|
: 'login-content-code-expired'
|
]"
|
@click="getCaptcha"
|
>
|
<img
|
class="login-content-code-img"
|
width="130px"
|
height="38px"
|
:src="state.captchaImage"
|
style="cursor: pointer"
|
/>
|
</div>
|
</el-col>
|
</el-form-item>
|
</Motion>
|
<Motion :delay="150">
|
<el-form-item prop="phoneVCode">
|
<el-input
|
v-model.number="state.ruleForm.phoneVCode"
|
class="form-input"
|
placeholder="请输入验证码"
|
>
|
<template #suffix>
|
<span v-if="state.phoneSeconds == 0" id="suffix-span">
|
<span
|
id="suffix-span-2"
|
ref="spanRef"
|
@click="sendValidationCode"
|
>
|
获取验证码
|
</span>
|
</span>
|
<span v-else id="suffix-span">
|
<span id="suffix-span-2" ref="spanRef">
|
{{ state.phoneSeconds }}秒后重新获取
|
</span>
|
</span>
|
</template>
|
</el-input>
|
</el-form-item>
|
</Motion>
|
<Motion :delay="250">
|
<el-button
|
class="w-full mt-4!"
|
size="default"
|
type="primary"
|
:loading="loading"
|
:disabled="disabled"
|
@click="onLogin(ruleFormRef)"
|
>
|
登录
|
</el-button>
|
</Motion>
|
</el-form>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<style scoped>
|
@import url("@/style/login.css");
|
</style>
|
|
<style lang="scss" scoped>
|
:deep(.el-input-group__append, .el-input-group__prepend) {
|
padding: 0;
|
}
|
#suffix-span {
|
cursor: pointer;
|
}
|
.login-content-code {
|
display: flex;
|
align-items: center;
|
justify-content: space-around;
|
position: relative;
|
|
.login-content-code-img {
|
width: 100%;
|
height: 40px;
|
line-height: 40px;
|
background-color: #ffffff;
|
// /* border: 1px solid rgb(220, 223, 230); */
|
cursor: pointer;
|
transition: all ease 0.2s;
|
border-radius: 4px;
|
user-select: none;
|
|
&:hover {
|
border-color: #c0c4cc;
|
transition: all ease 0.2s;
|
}
|
}
|
}
|
|
.login-content-code-expired {
|
@extend .login-content-code;
|
&::before {
|
content: "验证码已过期";
|
position: absolute;
|
top: 0;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
border-radius: 4px;
|
background-color: rgba(0, 0, 0, 0.5);
|
color: #ffffff;
|
text-align: center;
|
line-height: 40px;
|
}
|
}
|
</style>
|