<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,
|
h,
|
onMounted,
|
defineAsyncComponent,
|
onUnmounted,
|
watch
|
} from "vue";
|
import { debounce } 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 timer: any = null;
|
let phonetimer: any = null;
|
|
// 页面初始化
|
onMounted(async () => {
|
getCaptcha();
|
exRole().then(res => {
|
state.roleList = res.result;
|
const role = route.query;
|
if (role.code) {
|
state.nowRole = state.roleList.find(item => {
|
return item.code == role.code;
|
});
|
} else {
|
state.nowRole = state.roleList[0];
|
}
|
state.ruleForm.exRoleCode = state.nowRole.code;
|
});
|
// 注册验证码过期计时器
|
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: state.ruleForm.exRoleCode,
|
'onUpdate:modelValue': (val) => {
|
state.ruleForm.exRoleCode = val
|
},
|
style: {
|
display: 'flex',
|
flexDirection: 'column',
|
gap: '8px' // 单选框之间的间距
|
}
|
}, [
|
// 循环渲染单选框 - 每个单选框单独占一行
|
state.myEnterpriseList.map(item => h(ElRadio, {
|
key: item.id,
|
value: item.code,
|
style: {
|
display: 'block', // 让每个单选框分行显示
|
padding: '4px 0'
|
}
|
}, item.name),)
|
]),
|
|
// 管理员标签 - 显示在单选框后面
|
|
])
|
])
|
])
|
// <>
|
// <el-form
|
// ref="ruleFormRef"
|
// model={state.ruleForm}
|
// rules={loginRules}
|
// size="large"
|
// >
|
// <Motion delay={150}>
|
// <el-form-item prop="exRoleCode">
|
// <el-radio-group v-model={state.ruleForm.exRoleCode}>
|
// <el-radio
|
// v-for={item in state.myEnterpriseList}
|
// key={item.id}
|
// value={item.code}
|
// >
|
// {item.name}
|
// </el-radio>
|
// </el-radio-group>
|
// </el-form-item>
|
// </Motion>
|
// </el-form>
|
// </>
|
// h(
|
// ElForm,
|
// {
|
// ref: ruleFormRef,
|
// model: state.ruleForm,
|
// rules: loginRules,
|
// size: "large"
|
// },
|
// [
|
// h(Motion, { delay: 150 }, [
|
// h(ElFormItem, { prop: "exRoleCode" }, [
|
// h(
|
// ElRadioGroup,
|
// {
|
// modelValue: state.ruleForm.exRoleCode,
|
// "onUpdate:modelValue": val => {
|
// state.ruleForm.exRoleCode = val;
|
// }
|
// },
|
// [
|
// // 用map实现v-for循环
|
// state.myEnterpriseList.map(item =>
|
// h(ElRow, {}, [
|
// h(
|
// ElRadio,
|
// {
|
// key: item.customerUserID,
|
// value: item.customerUserID
|
// },
|
// item.enterpriseName
|
// ),
|
// item.isManager ? 444 : "" // 标签内容
|
// ])
|
// )
|
// ]
|
// )
|
// ])
|
// ])
|
// ]
|
// ),
|
// jsx 语法 (注意在.vue文件启用jsx语法,需要在script开启lang="tsx")
|
closeCallBack: ({ options, args }) => {
|
// options.props 是响应式的
|
// const { formInline } = options.props as FormProps;
|
// const text = `姓名:${formInline.user} 城市:${formInline.region}`;
|
if (args?.command === "cancel") {
|
// 您点击了取消按钮
|
// active.value -= 1;
|
} else if (args?.command === "sure") {
|
let obj = {
|
phone: state.ruleForm.phone,
|
code: state.ruleForm.phoneVCode,
|
exRuleCode: state.ruleForm.exRoleCode
|
};
|
useUserStoreHook()
|
.loginByUsername(obj)
|
.then(res => {
|
if (res?.code == 200) {
|
message("注册成功!", { type: "success" });
|
router.replace({
|
path: "/RegisterSucess",
|
query: {
|
code: obj.exRoleCode
|
}
|
});
|
} 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;
|
openDialog();
|
}
|
if (res.result.exRoles.length == 0) {
|
disabled.value = true;
|
router
|
.replace({
|
name: "RegisterNav",
|
query: { code: state.ruleForm.exRoleCode }
|
})
|
.then(() => {
|
message("登录成功", { type: "success" });
|
})
|
.finally(() => (disabled.value = false));
|
} else if (res.result.exRoles.length > 0) {
|
let data = res.result.exRoles.find(item => {
|
return item.code == state.ruleForm.exRoleCode;
|
});
|
|
if (!data) {
|
router.replace({
|
name: "RegisterNav",
|
query: { code: state.ruleForm.exRoleCode }
|
});
|
} else if (data?.hasFlsh) {
|
router.replace("index");
|
} else {
|
router.replace("mine");
|
}
|
}
|
useUserStoreHook().getCusExtendInfo();
|
});
|
} else {
|
message(res?.message || "登录失败", { type: "error" });
|
}
|
})
|
.finally(() => (loading.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>
|
<!-- <img :src="bg" class="wave" /> -->
|
<!-- <div class="flex-c absolute right-5 top-3"> -->
|
<!-- 主题 -->
|
<!-- <el-switch
|
v-model="dataTheme"
|
inline-prompt
|
:active-icon="dayIcon"
|
:inactive-icon="darkIcon"
|
@change="dataThemeChange"
|
/>
|
</div> -->
|
<div class="login-container">
|
<div class="img">
|
<!-- <component :is="toRaw(illustration)" /> -->
|
</div>
|
<div class="login-box">
|
<div class="login-form">
|
<!-- <avatar class="avatar" /> -->
|
<Motion>
|
<h2 class="logintitle">{{ state.nowRole.name }}登录</h2>
|
</Motion>
|
|
<el-form
|
ref="ruleFormRef"
|
:model="state.ruleForm"
|
:rules="loginRules"
|
size="large"
|
>
|
<!-- <Motion :delay="150">
|
<el-form-item prop="exRoleCode">
|
<el-radio-group v-model="state.ruleForm.exRoleCode">
|
<el-radio
|
v-for="item in state.roleList"
|
:key="item.id"
|
:value="item.code"
|
>{{ item.name }}</el-radio
|
>
|
</el-radio-group>
|
</el-form-item>
|
</Motion> -->
|
<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>
|
|
<!-- <el-form-item prop="password">
|
<el-input
|
v-model="ruleForm.password"
|
clearable
|
show-password
|
placeholder="密码"
|
:prefix-icon="useRenderIcon(Lock)"
|
/>
|
</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(state.ruleForm.phone)"
|
>
|
获取验证码
|
</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>
|