Browse Source

登录页面增加视频背景

develop
fuguobin 1 year ago
parent
commit
b411045e28
  1. BIN
      src/assets/images/login-bg.mp4
  2. 11
      src/settings.ts
  3. 7
      src/store/modules/settings.ts
  4. 2
      src/types/auto-imports.d.ts
  5. 1
      src/types/components.d.ts
  6. 62
      src/views/login/index.scss
  7. 115
      src/views/login/index.vue
  8. 5
      src/views/monitoring/screen/components/main.vue
  9. 4
      src/views/monitoring/screen/components/showTree.vue
  10. 4
      src/views/monitoring/screen/index.scss
  11. 12
      src/views/monitoring/screen/index.vue
  12. 4
      vite.config.ts

BIN
src/assets/images/login-bg.mp4

Binary file not shown.

11
src/settings.ts

@ -25,6 +25,10 @@ interface DefaultSettings {
*
*/
sidebarDark: boolean;
/**
*
*/
showVideo: boolean;
/**
*
*/
@ -46,12 +50,13 @@ interface DefaultSettings {
}
const defaultSettings: DefaultSettings = {
title: '联美运营驾驶舱管理系统',
title: 'vue-vite-project-admin',
showSettings: true,
tagsView: true,
fixedHeader: false,
sidebarLogo: true,
sidebarDark:true,
sidebarDark: true,
showVideo: true,
layout: 'left',
/**
*
@ -64,4 +69,4 @@ const defaultSettings: DefaultSettings = {
language: 'zh-cn' // zh-cn| en
};
export default defaultSettings;
export default defaultSettings;

7
src/store/modules/settings.ts

@ -10,6 +10,7 @@ export const useSettingsStore = defineStore('setting', () => {
const showSettings = ref<boolean>(defaultSettings.showSettings);
const fixedHeader = ref<boolean>(defaultSettings.fixedHeader);
const sidebarLogo = ref<boolean>(defaultSettings.sidebarLogo);
const showVideo = ref<boolean>(defaultSettings.showVideo);
const sidebarDark = ref<boolean>(defaultSettings.sidebarDark);
const layout = useStorage<string>('layout', defaultSettings.layout);
@ -29,6 +30,9 @@ export const useSettingsStore = defineStore('setting', () => {
case 'sidevarLogo':
sidebarLogo.value = value;
break;
case 'showVideo':
showVideo.value = value;
break;
case 'sidebarDark':
sidebarDark.value = value;
break;
@ -46,8 +50,9 @@ export const useSettingsStore = defineStore('setting', () => {
tagsView,
fixedHeader,
sidebarLogo,
showVideo,
sidebarDark,
layout,
changeSetting
};
});
});

2
src/types/auto-imports.d.ts

@ -2,6 +2,7 @@
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const ElForm: typeof import('element-plus/es')['ElForm']
const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
const NEllipsis: typeof import('naive-ui')['NEllipsis']
@ -271,6 +272,7 @@ import { UnwrapRef } from 'vue'
declare module 'vue' {
interface ComponentCustomProperties {
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly ElForm: UnwrapRef<typeof import('element-plus/es')['ElForm']>
readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']>
readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']>
readonly NEllipsis: UnwrapRef<typeof import('naive-ui')['NEllipsis']>

1
src/types/components.d.ts

@ -34,6 +34,7 @@ declare module '@vue/runtime-core' {
MultiUpload: typeof import('./../components/Upload/MultiUpload.vue')['default']
NButton: typeof import('naive-ui')['NButton']
NCard: typeof import('naive-ui')['NCard']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NIcon: typeof import('naive-ui')['NIcon']
NMenu: typeof import('naive-ui')['NMenu']
NModal: typeof import('naive-ui')['NModal']

62
src/views/login/index.scss

@ -5,32 +5,34 @@
align-items: center;
width: 100%;
height: 100%;
background: url('../../assets/images/login-bg.png');
background-size: cover;
overflow: hidden;
.loginCard {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 420px;
height: auto;
padding: 20px 30px;
height: fit-content;
padding: 4rem;
margin: auto;
border-radius: 20px;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(30px);
backdrop-filter: blur(10px);
border: 2px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 0 80px rgba(0, 0, 0, 0.25);
z-index: 99;
overflow: hidden;
.loginForm {
width: 100%;
.title {
padding: 1rem 0;
margin-bottom: 2rem;
span {
font-size: 1.5rem;
// color: #fff;
font-size: 2rem;
}
}
@ -82,13 +84,22 @@
}
}
}
.captcha{
.captcha {
line-height: normal;
}
}
}
}
.copyright{
.videoCover {
img {
z-index: 0;
position: absolute;
}
}
.copyright {
position: fixed;
bottom: 0;
width: 100%;
@ -101,3 +112,30 @@
letter-spacing: 1px;
}
}
.picContainer {
background: url('../../assets/images/login-bg.png');
background-size: cover;
.loginForm {
.title {
.switchLanguage,
span {
color: var(--el-text-color-regular);
}
}
}
}
.videoContainer {
.loginForm {
.title {
.switchLanguage,
span {
color: var(--el-color-white);
}
}
}
}

115
src/views/login/index.vue

@ -1,5 +1,5 @@
<template>
<div class="loginContainer">
<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">
@ -11,14 +11,8 @@
<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-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">
@ -26,16 +20,8 @@
<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-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>
@ -43,16 +29,8 @@
<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"
/>
<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>
@ -63,7 +41,13 @@
</el-button>
</el-form>
</div>
<div class="copyright">Copyright © 2023 Luenmei {{ $t('login.copyright') }}</div>
<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 Daniel {{ $t('login.copyright') }}</div>
</div>
</template>
@ -72,15 +56,22 @@ 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 { getCaptchaApi } from '@/api/auth';
import { useSettingsStore } from '@/store/modules/settings';
// import { getCaptchaApi } from '@/api/auth';
import { LoginData } from '@/api/auth/types';
const settingsStore = useSettingsStore();
const userStore = useUserStore();
const route = useRoute();
const loginForm = ref(ElForm);
const isCapslock = ref(false);//
const resizeStyle = ref()//
const videoCanPlay = ref(false)
const loginData = ref<LoginData>({
username: '',
password: ''
password: '',
// verifyCode: ''
});
const loginRules = {
@ -100,9 +91,6 @@ function validatePassword(rule: any, value: any, callback: any) {
}
}
//
const isCapslock = ref(false);
function checkCapslock(e: any) {
const { key } = e;
isCapslock.value = key && key.length === 1 && key >= 'A' && key <= 'Z';
@ -110,19 +98,28 @@ function checkCapslock(e: any) {
onMounted(() => {
// getCaptcha();
resizeFun()
});
/**
* 获取验证码
*/
function getCaptcha() {
getCaptchaApi().then(({ data }) => {
const { verifyCodeBase64, verifyCodeKey } = data;
loginData.value.verifyCodeKey = verifyCodeKey;
captchaBase64.value = verifyCodeBase64;
});
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) => {
@ -148,8 +145,36 @@ function loginClick() {
}
});
}
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>
</style>

5
src/views/monitoring/screen/components/main.vue

@ -106,8 +106,9 @@ const formRules = ref<VxeFormPropTypes.Rules>({
const userInfo = JSON.parse(localStorage.getItem('userInfo'));
// const loginIp = userInfo.loginIp.split('.').join('');
const api = "http://10.10.10.56:9000"//websocket
const apiUrl = api.replace(/https?:/, '');
const baseApi = "http://10.10.10.56:9000"//websocket
// const baseApi = import.meta.env.VITE_APP_BASE_API
const apiUrl = baseApi.replace(/https?:/, '');
const wsUrl = `ws:${apiUrl}/websocket/${userInfo.userName}`; //websocket
// const wsData = ref(socket.data);

4
src/views/monitoring/screen/components/showTree.vue

@ -14,8 +14,8 @@
</template>
</el-tree>
<div class="treeBtn">
<n-button @click="cancelClick">取消</n-button>
<n-button type="info" @click="confirmClick">确认</n-button>
<el-button @click="cancelClick">取消</el-button>
<el-button type="primary" @click="confirmClick">确认</el-button>
</div>
</div>
</template>

4
src/views/monitoring/screen/index.scss

@ -301,7 +301,7 @@
}
}
.cardClass {
// .cardClass {
.showTree {
.weight {
display: flex;
@ -322,4 +322,4 @@
margin-left: 10px;
}
}
}
// }

12
src/views/monitoring/screen/index.vue

@ -51,9 +51,10 @@
</div>
</section>
<section>
<n-modal v-model:show="showModal">
<n-card class="cardClass" style="width: 600px" title="显示项" :bordered="false" size="huge" role="dialog"
aria-modal="true">
<!-- <n-modal v-model:show="showModal"> -->
<el-dialog v-model="showModal" title="显示项" width="600">
<!-- <n-card class="cardClass" style="width: 600px" title="显示项" :bordered="false" size="huge" role="dialog"
aria-modal="true"> -->
<ShowTree :headerData="headerData" @cancelClick="cancelClick" />
<!-- <template #header-extra>
*
@ -62,8 +63,9 @@
<n-button>取消</n-button>
<n-button type="info"> 确定 </n-button>
</template> -->
</n-card>
</n-modal>
<!-- </n-card> -->
</el-dialog>
<!-- </n-modal> -->
</section>
</div>
</template>

4
vite.config.ts

@ -45,8 +45,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
hmr: true,//配置HMR
proxy: {
[env.VITE_APP_BASE_API]: {
target: 'http://172.1.2.29:9000/',//本地接口地址
// target: 'http://10.10.10.56:9000/',//线上接口地址
// target: 'http://172.1.2.29:9000/',//本地接口地址
target: 'http://10.10.10.56:9000/',//线上接口地址
changeOrigin: true,
rewrite: path =>
path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')

Loading…
Cancel
Save