| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- <script setup lang="ts">
- // @ts-check
- import Motion from "./utils/motion";
- import { useRoute, useRouter } from "vue-router";
- import { message } from "@/utils/message";
- import { loginRules, smsRules } from "./utils/rule";
- import { onMounted, reactive, ref, toRaw } 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 { ElMessage } from "element-plus";
- import { useLayout } from "@/layout/hooks/useLayout";
- import { useUserStoreHook } from "@/store/modules/user";
- import { addPathMatch, 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 dayIcon from "@/assets/svg/day.svg?component";
- import darkIcon from "@/assets/svg/dark.svg?component";
- import Lock from "~icons/ri/lock-fill";
- import User from "~icons/ri/user-3-fill";
- import MaterialSymbolsLightDomainVerificationRounded from "~icons/material-symbols-light/domain-verification-rounded";
- import EpIphone from "~icons/ep/iphone";
- import { getVerificationCode, getVerifySmsApi } from "@/api/user";
- import { usePermissionStoreHook } from "@/store/modules/permission";
- import { AesEncode } from "@/utils/crypto";
- import { md5 } from "js-md5";
- import { rule } from "postcss";
- defineOptions({
- name: "Login"
- });
- const route = useRoute();
- const router = useRouter();
- const loading = ref(false);
- const disabled = ref(false);
- const showVerifySms = ref(false);
- const canSendSecond = ref(true);
- const ruleFormRef = ref<FormInstance>();
- const smsFormRef = ref<FormInstance>();
- const sendVerifySmsCodeText = ref<string>("点击发送验证码");
- const sendInterval = ref();
- const countdown = ref<number>(60);
- const imgSrc = ref<string>(null);
- const { initStorage } = useLayout();
- initStorage();
- const { dataTheme, overallStyle, dataThemeChange } = useDataThemeChange();
- dataThemeChange(overallStyle.value);
- const { title } = useNav();
- const ruleForm = reactive({
- username: "admin",
- password: "admin123",
- code: "",
- uuid: ""
- });
- const smsRuleForm = reactive({
- verifySmsCode: ""
- });
- const uuid = ref("");
- const getVerifyCode = async () => {
- try {
- let res = await getVerificationCode();
- if (res.code === 200) {
- ruleForm.code = "";
- imgSrc.value = `data:image/png;base64,${res.img}`;
- ruleForm.uuid = res.uuid;
- }
- } catch (e) {
- console.log(e);
- }
- };
- //发送短信验证码
- const getVerifySmsCode = () => {
- if (!canSendSecond.value) return;
- canSendSecond.value = false;
- getVerifySmsApi({
- username: ruleForm.username,
- password: ruleForm.password
- })
- .then(() => {
- sendInterval.value = setInterval(function () {
- if (countdown.value === 0) {
- clearInterval(sendInterval.value);
- sendVerifySmsCodeText.value = "点击发送验证码";
- canSendSecond.value = true;
- countdown.value = 60;
- sendInterval.value = null;
- } else {
- countdown.value = countdown.value - 1;
- sendVerifySmsCodeText.value = `${countdown.value}秒后重试`;
- }
- }, 1000);
- })
- .catch(() => {
- canSendSecond.value = true;
- });
- };
- const onLogin = async (formEl: FormInstance | undefined) => {
- if (!formEl) return;
- const submitType = showVerifySms.value ? 2 : 1;
- // 1普通登录 2短信登录
- const ref = submitType === 1 ? ruleFormRef : smsFormRef;
- await ref.value.validate(valid => {
- if (valid) {
- loading.value = true;
- let params = {
- ...ruleForm
- };
- useUserStoreHook()
- .loginByUsername(params, submitType)
- .then(res => {
- console.log(res);
- let { code, msg } = res;
- if (code === 200 || code === 3) {
- // 存储输入信息
- /*const { storageExpires } = commonSetting;
- if (formData.agree) {
- storage.setCookie(
- "loginRemember",
- "1",
- storageExpires * 24 * 60 * 60
- );
- storage.setCookie(
- "loginRememberName",
- username,
- storageExpires * 24 * 60 * 60
- );
- storage.setCookie(
- "loginRememberPwd",
- password,
- storageExpires * 24 * 60 * 60
- );
- } else {
- storage.setCookie(
- "loginRemember",
- "0",
- storageExpires * 24 * 60 * 60
- );
- }*/
- message("登录成功,即将进入系统", {
- type: "success",
- duration: 1000,
- onClose() {
- if (code === 3) {
- ElMessage.warning(
- "为了保障您的账号信息安全,请修改登录密码,需包含8位数字、字母、特殊字符"
- );
- // 去修改密码页
- router.replace("/system/pwd_reset");
- } else {
- const toPath = decodeURIComponent(
- (route.query?.redirect || "/") as string
- );
- console.log(route.name);
- if (route.name === "Login") {
- initRouter().then(() => {
- router.push(getTopMenu(true).path);
- });
- // 全部采取静态路由模式
- /*usePermissionStoreHook().handleWholeMenus([]);
- addPathMatch();
- router.push(getTopMenu(true).path);*/
- } else router.replace(toPath);
- }
- }
- });
- return;
- }
- if (submitType === 1) {
- // 普通登录
- if (
- msg === "短信锁已过期" ||
- msg === "不是常用IP登录" ||
- msg === "上次登录时间超过7天" ||
- msg === "错误次数超过3次"
- ) {
- showVerifySms.value = true;
- canSendSecond.value = true;
- } else {
- ElMessage.warning(msg);
- getVerifyCode();
- }
- } else {
- // sms登录
- ElMessage.warning(msg);
- getVerifyCode();
- showVerifySms.value = false;
- }
- })
- .catch(e => {
- ElMessage.error("登录失败");
- getVerifyCode();
- })
- .finally(() => (loading.value = false));
- }
- });
- };
- 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);
- });
- onMounted(() => {
- getVerifyCode();
- });
- </script>
- <template>
- <div class="select-none">
- <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="outline-hidden">{{ title }}</h2>
- </Motion>
- <el-form
- v-show="!showVerifySms"
- ref="ruleFormRef"
- :model="ruleForm"
- :rules="loginRules"
- size="large"
- >
- <Motion :delay="100">
- <el-form-item prop="username">
- <el-input
- v-model="ruleForm.username"
- clearable
- placeholder="账号"
- :prefix-icon="useRenderIcon(User)"
- />
- </el-form-item>
- </Motion>
- <Motion :delay="150">
- <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="200">
- <el-form-item prop="code">
- <el-row class="w-full">
- <el-col :span="17">
- <el-input
- v-model="ruleForm.code"
- clearable
- :prefix-icon="
- useRenderIcon(
- MaterialSymbolsLightDomainVerificationRounded
- )
- "
- />
- </el-col>
- <el-col :span="6" :offset="1">
- <div
- v-optimize="{
- event: 'click',
- fn: getVerifyCode,
- immediate: true,
- timeout: 1000
- }"
- class="verify-code"
- >
- <img class="w-full! h-full!" :src="imgSrc" alt="" />
- </div>
- </el-col>
- </el-row>
- </el-form-item>
- </Motion>
- <Motion :delay="250">
- <el-form-item>
- <el-checkbox v-model="ruleForm.agree">
- 下次登录记住我的身份
- </el-checkbox>
- </el-form-item>
- </Motion>
- <Motion :delay="300">
- <el-button
- class="w-full"
- size="default"
- type="primary"
- :loading="loading"
- :disabled="disabled"
- @click="onLogin(ruleFormRef)"
- >
- 登录
- </el-button>
- </Motion>
- </el-form>
- <!-- 短信验证码表单 -->
- <el-form
- v-show="showVerifySms"
- ref="smsFormRef"
- :model="smsRuleForm"
- :rules="smsRules"
- size="large"
- @submit.prevent
- >
- <Motion :delay="100">
- <el-form-item prop="username">
- <el-row class="w-full">
- <el-col :span="15">
- <el-input
- v-model="smsRuleForm.verifySmsCode"
- clearable
- placeholder="请输入验证码"
- :prefix-icon="useRenderIcon(EpIphone)"
- />
- </el-col>
- <el-col :span="8" :offset="1">
- <el-button
- v-optimize="{
- event: 'click',
- fn: getVerifySmsCode,
- immediate: true,
- timeout: 1000
- }"
- class="w-full"
- type="primary"
- :disabled="!canSendSecond"
- >{{ sendVerifySmsCodeText }}
- </el-button>
- </el-col>
- </el-row>
- </el-form-item>
- </Motion>
- <Motion :delay="150">
- <el-form-item class="default-color">
- <el-checkbox v-model="ruleForm.agree">
- 下次登录记住我的身份
- </el-checkbox>
- </el-form-item>
- </Motion>
- <Motion :delay="200">
- <el-button
- class="w-full"
- size="default"
- type="primary"
- :loading="loading"
- :disabled="disabled"
- @click="onLogin(smsFormRef)"
- >
- 登录
- </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;
- }
- .verify-code {
- float: right;
- width: 100%;
- height: 40px;
- margin-top: 1px;
- font-size: 16px;
- line-height: 40px;
- text-align: center;
- cursor: pointer;
- user-select: none;
- user-select: none;
- user-select: none;
- background: #d8d8d8;
- border-radius: 5px;
- }
- </style>
|