2023-06-29 23:00:44 +08:00

341 lines
9.8 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.

<script setup lang="ts">
import {
ref,
toRaw,
reactive,
onMounted,
onBeforeUnmount,
onBeforeMount,
watch
} from "vue";
import Motion from "./utils/motion";
import { useRouter } from "vue-router";
import { message } from "@/utils/message";
import { loginRules } from "./utils/rule";
import phone from "./components/phone.vue";
import TypeIt from "@/components/ReTypeit";
import qrCode from "./components/qrCode.vue";
import register from "./components/register.vue";
import resetPassword from "./components/resetPassword.vue";
import { useNav } from "@/layout/hooks/useNav";
import type { FormInstance } from "element-plus";
import { operates, thirdParty } from "./utils/enums";
import { useLayout } from "@/layout/hooks/useLayout";
import { rsaEncrypt } from "@/utils/crypt";
import { initRouter, getTopMenu } from "@/router/utils";
import { bg, avatar, illustration } from "./utils/static";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import {
saveIsRememberMe,
getIsRememberMe,
savePassword,
getPassword,
removePassword,
setToken,
DataInfo
} from "@/utils/auth";
import dayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component";
import Lock from "@iconify-icons/ri/lock-fill";
import User from "@iconify-icons/ri/user-3-fill";
import * as CommonAPI from "@/api/common";
defineOptions({
name: "Login"
});
// TODO 当请求验证码过于频繁的话 服务器会报错 但是前端没有反应 这块需要处理一下, 通过axios处理一下
const captchaCodeBase64 = ref("");
const isCaptchaOn = ref(false);
const router = useRouter();
const loading = ref(false);
const isRememberMe = ref(false);
const ruleFormRef = ref<FormInstance>();
// 判断登录页面显示哪个组件0登录默认、1手机登录、2二维码登录、3注册、4忘记密码
const currentPage = ref(0);
const { initStorage } = useLayout();
initStorage();
const { dataTheme, dataThemeChange } = useDataThemeChange();
dataThemeChange();
// const { title, getDropdownItemStyle, getDropdownItemClass } = useNav();
const { title } = useNav();
const ruleForm = reactive({
username: "admin",
password: getPassword(),
captchaCode: "",
captchaCodeKey: ""
});
const onLogin = async (formEl: FormInstance | undefined) => {
loading.value = true;
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
CommonAPI.loginByPassword({
username: ruleForm.username,
password: rsaEncrypt(ruleForm.password),
captchaCode: ruleForm.captchaCode,
captchaCodeKey: ruleForm.captchaCodeKey
}).then(res => {
if (res.code === 0) {
// 登录成功后 将token存储到sessionStorage中
const tokenData: DataInfo<Number> = {
accessToken: res.data.token,
expires: undefined,
refreshToken: ""
};
setToken(tokenData);
// 获取后端路由
initRouter().then(() => {
router.push(getTopMenu(true).path);
message("登录成功", { type: "success" });
});
if (isRememberMe.value) {
savePassword(ruleForm.password);
}
}
});
} else {
loading.value = false;
return fields;
}
});
};
/** 使用公共函数,避免`removeEventListener`失效 */
function onkeypress({ code }: KeyboardEvent) {
if (code === "Enter") {
onLogin(ruleFormRef.value);
}
}
async function getCaptchaCode() {
await CommonAPI.getCaptchaCode().then(res => {
isCaptchaOn.value = res.data.isCaptchaOn;
captchaCodeBase64.value = `data:image/gif;base64,${res.data.captchaCodeImg}`;
ruleForm.captchaCodeKey = res.data.captchaCodeKey;
console.log(ruleForm);
});
}
watch(isRememberMe, newVal => {
saveIsRememberMe(newVal);
if (newVal === false) {
removePassword();
}
});
onBeforeMount(() => {
getCaptchaCode();
isRememberMe.value = getIsRememberMe();
if (isRememberMe.value) {
ruleForm.password = getPassword();
}
});
onMounted(() => {
window.document.addEventListener("keypress", onkeypress);
getCaptchaCode();
});
onBeforeUnmount(() => {
window.document.removeEventListener("keypress", onkeypress);
});
</script>
<template>
<div class="select-none">
<img :src="bg" class="wave" />
<div class="absolute flex-c 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">
<!-- 登录窗口上面的LOGO -->
<avatar class="avatar" />
<Motion>
<h2 class="outline-none">
<TypeIt :values="[title]" :cursor="false" :speed="150" />
</h2>
</Motion>
<el-form
v-if="currentPage === 0"
ref="ruleFormRef"
:model="ruleForm"
:rules="loginRules"
size="large"
>
<Motion :delay="100">
<el-form-item
:rules="[
{
required: true,
message: '请输入账号',
trigger: 'blur'
}
]"
prop="username"
>
<el-input
clearable
v-model="ruleForm.username"
placeholder="账号"
:prefix-icon="useRenderIcon(User)"
/>
</el-form-item>
</Motion>
<Motion :delay="150">
<el-form-item prop="password">
<el-input
clearable
show-password
v-model="ruleForm.password"
placeholder="密码"
:prefix-icon="useRenderIcon(Lock)"
/>
</el-form-item>
</Motion>
<Motion :delay="200">
<el-form-item v-if="isCaptchaOn" prop="captchaCode">
<el-input
clearable
v-model="ruleForm.captchaCode"
placeholder="验证码"
:prefix-icon="useRenderIcon('ri:shield-keyhole-line')"
>
<template v-slot:append>
<img
width="120"
height="40"
class="cursor-pointer"
:src="captchaCodeBase64"
@click="getCaptchaCode"
/>
</template>
</el-input>
</el-form-item>
</Motion>
<Motion :delay="250">
<el-form-item>
<div class="w-full h-[20px] flex justify-between items-center">
<el-checkbox v-model="isRememberMe"> 记住密码 </el-checkbox>
<el-button link type="primary" @click="currentPage = 4">
忘记密码
</el-button>
</div>
<el-button
class="w-full mt-4"
size="default"
type="primary"
:loading="loading"
@click="onLogin(ruleFormRef)"
>
登录
</el-button>
</el-form-item>
</Motion>
<Motion :delay="300">
<el-form-item>
<div class="w-full h-[20px] flex justify-between items-center">
<el-button
v-for="(item, index) in operates"
:key="index"
class="w-full mt-4"
size="default"
@click="currentPage = item.page"
>
{{ item.title }}
</el-button>
</div>
</el-form-item>
</Motion>
</el-form>
<Motion v-if="currentPage === 0" :delay="350">
<el-form-item>
<el-divider>
<p class="text-xs text-gray-500">{{ "第三方登录" }}</p>
</el-divider>
<div class="flex w-full justify-evenly">
<span
v-for="(item, index) in thirdParty"
:key="index"
:title="item.title"
>
<IconifyIconOnline
:icon="`ri:${item.icon}-fill`"
width="20"
class="text-gray-500 cursor-pointer hover:text-blue-400"
/>
</span>
</div>
</el-form-item>
</Motion>
<!-- 手机号登录 -->
<phone v-if="currentPage === 1" v-model:current-page="currentPage" />
<!-- 二维码登录 -->
<qrCode v-if="currentPage === 2" v-model:current-page="currentPage" />
<!-- 注册 -->
<register
v-if="currentPage === 3"
v-model:current-page="currentPage"
/>
<!-- 忘记密码 -->
<resetPassword
v-if="currentPage === 4"
v-model:current-page="currentPage"
/>
</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;
}
.translation {
::v-deep(.el-dropdown-menu__item) {
padding: 5px 40px;
}
.check-zh {
position: absolute;
left: 20px;
}
.check-en {
position: absolute;
left: 20px;
}
}
</style>