|
|
|
<template>
|
|
|
|
<div :class="['loginContainer', settingsStore.showVideo ? 'videoContainer' : 'picContainer']">
|
|
|
|
<div class="loginCard">
|
|
|
|
<el-form ref="loginForm" class="loginForm" :model="loginData" :rules="loginRules">
|
|
|
|
<div class="title flex items-center">
|
|
|
|
<span class="flex-1 text-center">{{ $t('login.title') }}</span>
|
|
|
|
<lang-select />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<el-form-item prop="username">
|
|
|
|
<div class="icon">
|
|
|
|
<i class="iconfont icon-user"></i>
|
|
|
|
</div>
|
|
|
|
<el-input
|
|
|
|
class="flex-1"
|
|
|
|
ref="username"
|
|
|
|
name="username"
|
|
|
|
size="large"
|
|
|
|
v-model="loginData.username"
|
|
|
|
:placeholder="$t('login.username')"
|
|
|
|
/>
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-tooltip content="Caps lock is On" placement="right" :disabled="isCapslock === false">
|
|
|
|
<el-form-item prop="password">
|
|
|
|
<div class="icon">
|
|
|
|
<i class="iconfont icon-lock"></i>
|
|
|
|
</div>
|
|
|
|
<el-input
|
|
|
|
class="flex-1"
|
|
|
|
name="password"
|
|
|
|
type="password"
|
|
|
|
size="large"
|
|
|
|
v-model="loginData.password"
|
|
|
|
:placeholder="$t('login.password')"
|
|
|
|
show-password
|
|
|
|
@keyup="checkCapslock"
|
|
|
|
/>
|
|
|
|
</el-form-item>
|
|
|
|
</el-tooltip>
|
|
|
|
|
|
|
|
<!-- <el-form-item prop="verifyCode">
|
|
|
|
<div class="icon">
|
|
|
|
<i class="iconfont icon-safe-code"></i>
|
|
|
|
</div>
|
|
|
|
<el-input class="flex-1" name="verifyCode" size="large" v-model="loginData.verifyCode"
|
|
|
|
:placeholder="$t('login.verifyCode')" auto-complete="off" maxlength="6" @keyup.enter="loginClick" />
|
|
|
|
<div class="captcha">
|
|
|
|
<img :src="captchaBase64" @click="getCaptcha" />
|
|
|
|
</div>
|
|
|
|
</el-form-item> -->
|
|
|
|
|
|
|
|
<el-form-item class="remember">
|
|
|
|
<el-checkbox v-model="remember" size="large">记住密码</el-checkbox>
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-button class="width-100" size="default" type="primary" :loading="loading" @click.prevent="loginClick">
|
|
|
|
{{ $t('login.login') }}
|
|
|
|
</el-button>
|
|
|
|
</el-form>
|
|
|
|
</div>
|
|
|
|
<video
|
|
|
|
class="video"
|
|
|
|
:style="resizeStyle"
|
|
|
|
autoplay
|
|
|
|
loop
|
|
|
|
muted
|
|
|
|
v-on:canplay="onCanplay"
|
|
|
|
v-if="settingsStore.showVideo"
|
|
|
|
>
|
|
|
|
<source src="../../assets/images/login-bg.mp4" type="video/mp4" />
|
|
|
|
</video>
|
|
|
|
<div class="videoCover" v-if="!videoCanPlay && settingsStore.showVideo">
|
|
|
|
<img :style="resizeStyle" src="../../assets/images/login-bg.png" alt="视频封面" />
|
|
|
|
</div>
|
|
|
|
<div class="copyright">Copyright © 2023 {{ $t('login.group') }} {{ $t('login.copyright') }}</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
import router from '@/router';
|
|
|
|
import LangSelect from '@/components/LangSelect/index.vue';
|
|
|
|
import { useUserStore } from '@/store/modules/user';
|
|
|
|
import { LocationQuery, LocationQueryValue, useRoute } from 'vue-router';
|
|
|
|
import { useSettingsStore } from '@/store/modules/settings';
|
|
|
|
// import { getCaptchaApi } from '@/api/auth';
|
|
|
|
import { LoginData } from '@/api/auth/types';
|
|
|
|
import { setLocalStorage, getLocalStorage } from '@/utils/storage';
|
|
|
|
import { encryption, decryption } from '@/utils/crypto';
|
|
|
|
|
|
|
|
const settingsStore = useSettingsStore();
|
|
|
|
const userStore = useUserStore();
|
|
|
|
const route = useRoute();
|
|
|
|
const loginForm = ref(ElForm);
|
|
|
|
const isCapslock = ref(false); // 是否大写锁定
|
|
|
|
const remember = ref(false);
|
|
|
|
const resizeStyle = ref(); //视频背景自适应样式
|
|
|
|
const videoCanPlay = ref(false);
|
|
|
|
|
|
|
|
const loginData = ref<LoginData>({
|
|
|
|
username: '',
|
|
|
|
password: ''
|
|
|
|
// verifyCode: ''
|
|
|
|
});
|
|
|
|
|
|
|
|
const loginRules = {
|
|
|
|
username: [{ required: true, trigger: 'blur', message: '请输入用户名' }],
|
|
|
|
password: [{ required: true, trigger: 'blur', validator: validatePassword }],
|
|
|
|
verifyCode: [{ required: true, trigger: 'blur', message: '请输入验证码' }]
|
|
|
|
};
|
|
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
// const captchaBase64 = ref();
|
|
|
|
|
|
|
|
function validatePassword(rule: any, value: any, callback: any) {
|
|
|
|
if (value.length < 6) {
|
|
|
|
callback(new Error('密码不能少于6位'));
|
|
|
|
} else {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function checkCapslock(e: any) {
|
|
|
|
const { key } = e;
|
|
|
|
isCapslock.value = key && key.length === 1 && key >= 'A' && key <= 'Z';
|
|
|
|
}
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
// getCaptcha();
|
|
|
|
resizeFun();
|
|
|
|
const loginInfo = getLocalStorage('loginInfo');
|
|
|
|
if (loginInfo != null && Object.keys(loginInfo).length >= 2) {
|
|
|
|
remember.value = true;
|
|
|
|
loginData.value.username = loginInfo.username;
|
|
|
|
loginData.value.password = decryption(loginInfo.password);
|
|
|
|
} else {
|
|
|
|
remember.value = false;
|
|
|
|
}
|
|
|
|
resizeStyle.value = {
|
|
|
|
height: '100vh',
|
|
|
|
width: '100vw'
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
function onCanplay() {
|
|
|
|
//视频播放事件
|
|
|
|
videoCanPlay.value = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
window.addEventListener('resize', () => {
|
|
|
|
//监听窗口变化以改变视频背景大小自适应
|
|
|
|
resizeFun();
|
|
|
|
});
|
|
|
|
|
|
|
|
//获取验证码
|
|
|
|
// function getCaptcha() {
|
|
|
|
// getCaptchaApi().then(({ data }) => {
|
|
|
|
// const { verifyCodeBase64, verifyCodeKey } = data;
|
|
|
|
// loginData.value.verifyCodeKey = verifyCodeKey;
|
|
|
|
// captchaBase64.value = verifyCodeBase64;
|
|
|
|
// });
|
|
|
|
// }
|
|
|
|
|
|
|
|
//登录
|
|
|
|
function loginClick() {
|
|
|
|
loginForm.value.validate((valid: boolean) => {
|
|
|
|
if (valid) {
|
|
|
|
loading.value = true;
|
|
|
|
userStore
|
|
|
|
.login(loginData.value)
|
|
|
|
.then(() => {
|
|
|
|
if (remember.value) {
|
|
|
|
const loginRemember = loginData.value;
|
|
|
|
loginRemember.password = encryption(loginData.value.password);
|
|
|
|
setLocalStorage('loginInfo', loginRemember);
|
|
|
|
} else {
|
|
|
|
setLocalStorage('loginInfo', {});
|
|
|
|
}
|
|
|
|
const query: LocationQuery = route.query;
|
|
|
|
const redirect = (query.redirect as LocationQueryValue) ?? '/';
|
|
|
|
const otherQueryParams = Object.keys(query).reduce((account: any, current: string) => {
|
|
|
|
if (current !== 'redirect') {
|
|
|
|
account[current] = query[current];
|
|
|
|
}
|
|
|
|
return account;
|
|
|
|
}, {});
|
|
|
|
router.push({ path: redirect, query: otherQueryParams });
|
|
|
|
})
|
|
|
|
.finally(() => {
|
|
|
|
loading.value = false;
|
|
|
|
// getCaptcha();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function resizeFun() {
|
|
|
|
//视频背景自适应处理
|
|
|
|
const windowWidth = document.body.clientWidth;
|
|
|
|
const windowHeight = document.body.clientHeight;
|
|
|
|
const windowAspectRatio = windowHeight / windowWidth;
|
|
|
|
let videoWidth;
|
|
|
|
let videoHeight;
|
|
|
|
if (windowAspectRatio < 0.5625) {
|
|
|
|
videoWidth = windowWidth;
|
|
|
|
videoHeight = videoWidth * 0.5625;
|
|
|
|
resizeStyle.value = {
|
|
|
|
height: windowWidth * 0.5625 + 'px',
|
|
|
|
width: windowWidth + 'px',
|
|
|
|
'margin-bottom': (windowHeight - videoHeight) / 2 + 'px',
|
|
|
|
'margin-left': 'initial'
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
videoHeight = windowHeight;
|
|
|
|
videoWidth = videoHeight / 0.5625;
|
|
|
|
resizeStyle.value = {
|
|
|
|
height: windowHeight + 'px',
|
|
|
|
width: windowHeight / 0.5625 + 'px',
|
|
|
|
'margin-left': (windowWidth - videoWidth) / 2 + 'px',
|
|
|
|
'margin-bottom': 'initial'
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
@import './index.scss';
|
|
|
|
</style>
|