Vben
2021-04-10 215d8bab380728164d7fe2958c2d2d1151fce892
src/views/sys/login/Login.vue
@@ -1,193 +1,218 @@
<template>
  <div class="login">
    <div class="login-mask" />
    <div class="login-form-wrap">
      <div class="login-form mx-6">
        <div class="login-form__content px-2 py-10">
          <header>
            <img src="/@/assets/images/logo.png" class="mr-4" />
            <h1>{{ title }}</h1>
          </header>
  <div :class="prefixCls" class="relative w-full h-full px-4">
    <AppLocalePicker
      class="absolute top-4 right-4 enter-x text-white xl:text-gray-600"
      :showText="false"
    />
    <AppDarkModeToggle class="absolute top-3 right-7 enter-x" />
          <a-form class="mx-auto mt-10" :model="formData" :rules="formRules" ref="formRef">
            <a-form-item name="account">
              <a-input size="large" v-model:value="formData.account" placeholder="vben" />
            </a-form-item>
            <a-form-item name="password">
              <a-input-password
                autofocus="autofocus"
                size="large"
                visibilityToggle
                v-model:value="formData.password"
                placeholder="123456"
              />
            </a-form-item>
            <a-form-item name="verify" v-if="openLoginVerify">
              <BasicDragVerify v-model:value="formData.verify" ref="verifyRef" />
            </a-form-item>
            <a-form-item>
              <a-button
                type="primary"
                size="large"
                class="rounded-sm"
                block
                @click="login"
                :loading="formState.loading"
                >登录</a-button
              >
            </a-form-item>
          </a-form>
    <span class="-enter-x xl:hidden">
      <AppLogo :alwaysShowTitle="true" />
    </span>
    <div class="container relative h-full py-2 mx-auto sm:px-10">
      <div class="flex h-full">
        <div class="hidden xl:flex xl:flex-col xl:w-6/12 min-h-full mr-4 pl-4">
          <AppLogo class="-enter-x" />
          <div class="my-auto">
            <img
              :alt="title"
              src="../../../assets/svg/login-box-bg.svg"
              class="w-1/2 -mt-16 -enter-x"
            />
            <div class="mt-10 font-medium text-white -enter-x">
              <span class="mt-4 text-3xl inline-block"> {{ t('sys.login.signInTitle') }}</span>
            </div>
            <div class="mt-5 text-md text-white font-normal dark:text-gray-500 -enter-x">
              {{ t('sys.login.signInDesc') }}
            </div>
          </div>
        </div>
        <div class="h-full xl:h-auto flex py-5 xl:py-0 xl:my-0 w-full xl:w-6/12">
          <div
            :class="`${prefixCls}-form`"
            class="my-auto mx-auto xl:ml-20 xl:bg-transparent px-5 py-8 sm:px-8 xl:p-4 rounded-md shadow-md xl:shadow-none w-full sm:w-3/4 lg:w-2/4 xl:w-auto enter-x relative"
          >
            <LoginForm />
            <ForgetPasswordForm />
            <RegisterForm />
            <MobileForm />
            <QrCodeForm />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
  import { defineComponent, reactive, ref, unref, toRaw, computed } from 'vue';
  import { BasicDragVerify, DragVerifyActionType } from '/@/components/Verify/index';
  import { userStore } from '/@/store/modules/user';
  import { appStore } from '/@/store/modules/app';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { useSetting } from '/@/hooks/core/useSetting';
  import { defineComponent, computed } from 'vue';
  import { AppLogo } from '/@/components/Application';
  import { AppLocalePicker, AppDarkModeToggle } from '/@/components/Application';
  import LoginForm from './LoginForm.vue';
  import ForgetPasswordForm from './ForgetPasswordForm.vue';
  import RegisterForm from './RegisterForm.vue';
  import MobileForm from './MobileForm.vue';
  import QrCodeForm from './QrCodeForm.vue';
  import { useGlobSetting } from '/@/hooks/setting';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { useDesign } from '/@/hooks/web/useDesign';
  import { useLocaleStore } from '/@/store/modules/locale';
  export default defineComponent({
    components: { BasicDragVerify },
    name: 'Login',
    components: {
      AppLogo,
      LoginForm,
      ForgetPasswordForm,
      RegisterForm,
      MobileForm,
      QrCodeForm,
      AppLocalePicker,
      AppDarkModeToggle,
    },
    setup() {
      const { globSetting } = useSetting();
      const { notification } = useMessage();
      const formRef = ref<any>(null);
      const verifyRef = ref<RefInstanceType<DragVerifyActionType>>(null);
      const openLoginVerifyRef = computed(() => appStore.getProjectConfig.openLoginVerify);
      const formData = reactive({
        account: '',
        password: '',
        verify: undefined,
      });
      const formState = reactive({
        loading: false,
      });
      const formRules = reactive({
        account: [{ required: true, message: '请输入账号', trigger: 'blur' }],
        password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
        verify: unref(openLoginVerifyRef) ? [{ required: true, message: '请通过验证码校验' }] : [],
      });
      function resetVerify() {
        const verify = unref(verifyRef);
        if (!verify) return;
        formData.verify && verify.$.resume();
        formData.verify = undefined;
      }
      async function handleLogin() {
        const form = unref(formRef);
        if (!form) return;
        formState.loading = true;
        try {
          const data = await form.validate();
          const userInfo = await userStore.login(
            toRaw({
              password: data.password,
              username: data.account,
            })
          );
          if (userInfo) {
            notification.success({
              message: '登录成功',
              description: `欢迎回来: ${userInfo.realName}`,
              duration: 3,
            });
          }
        } catch (error) {
        } finally {
          resetVerify();
          formState.loading = false;
        }
      }
      const globSetting = useGlobSetting();
      const { prefixCls } = useDesign('login');
      const { t } = useI18n();
      const localeStore = useLocaleStore();
      return {
        formRef,
        verifyRef,
        formData,
        formState,
        formRules,
        login: handleLogin,
        openLoginVerify: openLoginVerifyRef,
        title: globSetting && globSetting.title,
        t,
        prefixCls,
        title: computed(() => globSetting?.title ?? ''),
        showLocale: localeStore.getShowPicker,
      };
    },
  });
</script>
<style lang="less" scoped>
  @import (reference) '../../../design/index.less';
<style lang="less">
  @prefix-cls: ~'@{namespace}-login';
  @logo-prefix-cls: ~'@{namespace}-app-logo';
  @countdown-prefix-cls: ~'@{namespace}-countdown-input';
  @dark-bg: #293146;
  .login {
    position: relative;
    height: 100vh;
    background: url(../../../assets/images/login/login-bg.png) no-repeat;
    background-size: 100% 100%;
  html[data-theme='dark'] {
    .@{prefix-cls} {
      background: @dark-bg;
    &-mask {
      display: none;
      height: 100%;
      background: url(../../../assets/images/login/login-in.png) no-repeat;
      background-size: 100% 100%;
      &::before {
        background-image: url(/@/assets/svg/login-bg-dark.svg);
      }
      .respond-to(large, { display: block;});
      .ant-input,
      .ant-input-password {
        background-color: #232a3b;
      }
      .ant-btn:not(.ant-btn-link):not(.ant-btn-primary) {
        border: 1px solid #4a5569;
      }
      &-form {
        background: transparent !important;
      }
      .app-iconify {
        color: #fff;
      }
    }
  }
  .@{prefix-cls} {
    overflow: hidden;
    @media (max-width: @screen-xl) {
      background: #293146;
      .@{prefix-cls}-form {
        background: #fff;
      }
    }
    &-form {
    &::before {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      background: @white;
      border: 10px solid rgba(255, 255, 255, 0.5);
      border-width: 10px;
      border-radius: 4px;
      background-clip: padding-box;
      .respond-to(xlarge, { margin: 0 56px});
      height: 100%;
      margin-left: -48%;
      background-image: url(/@/assets/svg/login-bg.svg);
      background-position: 100%;
      background-repeat: no-repeat;
      background-size: auto 100%;
      content: '';
      @media (max-width: @screen-xl) {
        display: none;
      }
    }
      &-wrap {
        position: absolute;
        top: 0;
        right: 0;
    .@{logo-prefix-cls} {
      position: absolute;
      top: 12px;
      height: 30px;
      &__title {
        font-size: 16px;
        color: #fff;
      }
      img {
        width: 32px;
      }
    }
    .container {
      .@{logo-prefix-cls} {
        display: flex;
        width: 100%;
        height: 100%;
        justify-content: center;
        align-items: center;
        .respond-to(large, { width: 40%;});
        .respond-to(xlarge, { width: 33.3%;});
      }
        width: 60%;
        height: 80px;
      &__content {
        width: 100%;
        height: 100%;
        border: 1px solid #999;
        border-radius: 2px;
        header {
          display: flex;
          justify-content: center;
          align-items: center;
          img {
            display: inline-block;
            width: 80px;
          }
          h1 {
            margin-bottom: 0;
            font-size: 24px;
            color: @primary-color;
            text-align: center;
          }
        &__title {
          font-size: 24px;
          color: #fff;
        }
        form {
          width: 80%;
        img {
          width: 48px;
        }
      }
    }
    &-sign-in-way {
      .anticon {
        font-size: 22px;
        color: #888;
        cursor: pointer;
        &:hover {
          color: @primary-color;
        }
      }
    }
    input:not([type='checkbox']) {
      min-width: 360px;
      @media (max-width: @screen-lg) {
        min-width: 300px;
      }
      @media (max-width: @screen-md) {
        min-width: 280px;
      }
      @media (max-width: @screen-sm) {
        min-width: 180px;
      }
    }
    .@{countdown-prefix-cls} input {
      min-width: unset;
    }
    .ant-divider-inner-text {
      font-size: 12px;
      color: @text-color-secondary;
    }
  }
</style>