shop-front-end/src/views/login/index.vue

348 lines
11 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 {
onBeforeMount,
onBeforeUnmount,
onMounted,
reactive,
ref,
toRaw,
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 { getTopMenu, initRouter } from "@/router/utils";
import { avatar, bg, illustration } from "./utils/static";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import {
getIsRememberMe,
getPassword,
removePassword,
saveIsRememberMe,
savePassword,
setTokenFromBackend
} 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/login";
import { useUserStoreHook } from "@/store/modules/user";
import { useWxStore } from "@/store/modules/wx";
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 wxStore = useWxStore();
const { initStorage } = useLayout();
initStorage();
const { dataTheme, dataThemeChange } = useDataThemeChange();
dataThemeChange();
// const { title, getDropdownItemStyle, getDropdownItemClass } = useNav();
const { title } = useNav();
const urlParams = new URLSearchParams(window.location.search);
console.log('urlParams', urlParams);
const corpid = urlParams.get('corpid') || undefined;
const code = urlParams.get('code') || undefined;
const state = urlParams.get('state') || undefined;
if (code && corpid) {
wxStore.handleWxCallback({ corpid, code, state })
}
const ruleForm = reactive({
username: "admin",
password: getPassword(),
captchaCode: "",
captchaCodeKey: "",
corpid: wxStore.corpid,
code: wxStore.code,
state: wxStore.state
});
const onLogin = async (formEl: FormInstance | undefined) => {
loading.value = true;
if (!formEl) return;
if (code && corpid) {
CommonAPI.loginByPassword({
username: ruleForm.username,
password: ruleForm.password ? rsaEncrypt(ruleForm.password) : '',
captchaCode: ruleForm.captchaCode,
captchaCodeKey: ruleForm.captchaCodeKey,
corpid: ruleForm.corpid,
code: ruleForm.code,
state: ruleForm.state
})
.then(({ data }) => {
// 登录成功后 将token存储到sessionStorage中
setTokenFromBackend(data);
// 获取后端路由
initRouter().then(() => {
router.push(getTopMenu(true).path);
message("登录成功", { type: "success" });
});
if (isRememberMe.value) {
savePassword(ruleForm.password);
}
})
.catch(() => {
loading.value = false;
//如果登陆失败则重新获取验证码
getCaptchaCode();
});
} else {
await formEl.validate((valid, fields) => {
if (valid) {
CommonAPI.loginByPassword({
username: ruleForm.username,
password: ruleForm.password ? rsaEncrypt(ruleForm.password) : '',
captchaCode: ruleForm.captchaCode,
captchaCodeKey: ruleForm.captchaCodeKey,
corpid: ruleForm.corpid,
code: ruleForm.code,
state: ruleForm.state
})
.then(({ data }) => {
// 登录成功后 将token存储到sessionStorage中
setTokenFromBackend(data);
// 获取后端路由
initRouter().then(() => {
router.push(getTopMenu(true).path);
message("登录成功", { type: "success" });
});
if (isRememberMe.value) {
savePassword(ruleForm.password);
}
})
.catch(() => {
loading.value = false;
//如果登陆失败则重新获取验证码
getCaptchaCode();
});
} else {
loading.value = false;
return fields;
}
});
}
};
/** 使用公共函数,避免`removeEventListener`失效 */
function onkeypress({ code }: KeyboardEvent) {
if (code === "Enter") {
onLogin(ruleFormRef.value);
}
}
async function getCaptchaCode() {
if (isCaptchaOn.value) {
await CommonAPI.getCaptchaCode().then(res => {
captchaCodeBase64.value = `data:image/gif;base64,${res.data.captchaCodeImg}`;
ruleForm.captchaCodeKey = res.data.captchaCodeKey;
});
}
}
watch(isRememberMe, newVal => {
saveIsRememberMe(newVal);
if (newVal === false) {
removePassword();
}
});
onBeforeMount(async () => {
await CommonAPI.getConfig().then(res => {
isCaptchaOn.value = res.data.isCaptchaOn;
useUserStoreHook().SET_DICTIONARY(res.data.dictionary);
});
await getCaptchaCode();
isRememberMe.value = getIsRememberMe();
if (isRememberMe.value) {
ruleForm.password = getPassword();
}
});
onMounted(() => {
window.document.addEventListener("keypress", onkeypress);
// 临时登录
/* if (wxStore.code && wxStore.corpid) {
onLogin(ruleFormRef.value);
} */
});
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" :active-icon="dayIcon" :inactive-icon="darkIcon" inline-prompt
@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 :cursor="false" :speed="150" :values="[title]" />
</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 v-model="ruleForm.username" :prefix-icon="useRenderIcon(User)" clearable placeholder="账号" />
</el-form-item>
</Motion>
<Motion :delay="150">
<el-form-item prop="password">
<el-input v-model="ruleForm.password" :prefix-icon="useRenderIcon(Lock)" clearable placeholder="密码"
show-password />
</el-form-item>
</Motion>
<Motion :delay="200">
<el-form-item v-if="isCaptchaOn" prop="captchaCode">
<el-input v-model="ruleForm.captchaCode" :prefix-icon="useRenderIcon('ri:shield-keyhole-line')"
clearable placeholder="验证码">
<template v-slot:append>
<el-image :src="captchaCodeBase64" style="
justify-content: center;
width: 120px;
height: 40px;
" @click="getCaptchaCode">
<template #error>
<span>Loading</span>
</template>
</el-image>
</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 :loading="loading" class="w-full mt-4" size="default" type="primary"
@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`"
class="text-gray-500 cursor-pointer hover:text-blue-400" width="20" />
</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>