Browse Source

登录页面增加视频背景

develop
fuguobin 1 year ago
parent
commit
b411045e28
  1. BIN
      src/assets/images/login-bg.mp4
  2. 7
      src/settings.ts
  3. 5
      src/store/modules/settings.ts
  4. 2
      src/types/auto-imports.d.ts
  5. 1
      src/types/components.d.ts
  6. 58
      src/views/login/index.scss
  7. 113
      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.

7
src/settings.ts

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

5
src/store/modules/settings.ts

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

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

@ -2,6 +2,7 @@
export {} export {}
declare global { declare global {
const EffectScope: typeof import('vue')['EffectScope'] const EffectScope: typeof import('vue')['EffectScope']
const ElForm: typeof import('element-plus/es')['ElForm']
const ElMessage: typeof import('element-plus/es')['ElMessage'] const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
const NEllipsis: typeof import('naive-ui')['NEllipsis'] const NEllipsis: typeof import('naive-ui')['NEllipsis']
@ -271,6 +272,7 @@ import { UnwrapRef } from 'vue'
declare module 'vue' { declare module 'vue' {
interface ComponentCustomProperties { interface ComponentCustomProperties {
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']> 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 ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']>
readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']> readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']>
readonly NEllipsis: UnwrapRef<typeof import('naive-ui')['NEllipsis']> 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'] MultiUpload: typeof import('./../components/Upload/MultiUpload.vue')['default']
NButton: typeof import('naive-ui')['NButton'] NButton: typeof import('naive-ui')['NButton']
NCard: typeof import('naive-ui')['NCard'] NCard: typeof import('naive-ui')['NCard']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NIcon: typeof import('naive-ui')['NIcon'] NIcon: typeof import('naive-ui')['NIcon']
NMenu: typeof import('naive-ui')['NMenu'] NMenu: typeof import('naive-ui')['NMenu']
NModal: typeof import('naive-ui')['NModal'] NModal: typeof import('naive-ui')['NModal']

58
src/views/login/index.scss

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

113
src/views/login/index.vue

@ -1,5 +1,5 @@
<template> <template>
<div class="loginContainer"> <div :class="['loginContainer', settingsStore.showVideo ? 'videoContainer' : 'picContainer']">
<div class="loginCard"> <div class="loginCard">
<el-form ref="loginForm" class="loginForm" :model="loginData" :rules="loginRules"> <el-form ref="loginForm" class="loginForm" :model="loginData" :rules="loginRules">
<div class="title flex items-center"> <div class="title flex items-center">
@ -11,14 +11,8 @@
<div class="icon"> <div class="icon">
<i class="iconfont icon-user"></i> <i class="iconfont icon-user"></i>
</div> </div>
<el-input <el-input class="flex-1" ref="username" name="username" size="large" v-model="loginData.username"
class="flex-1" :placeholder="$t('login.username')" />
ref="username"
name="username"
size="large"
v-model="loginData.username"
:placeholder="$t('login.username')"
/>
</el-form-item> </el-form-item>
<el-tooltip content="Caps lock is On" placement="right" :disabled="isCapslock === false"> <el-tooltip content="Caps lock is On" placement="right" :disabled="isCapslock === false">
@ -26,16 +20,8 @@
<div class="icon"> <div class="icon">
<i class="iconfont icon-lock"></i> <i class="iconfont icon-lock"></i>
</div> </div>
<el-input <el-input class="flex-1" name="password" type="password" size="large" v-model="loginData.password"
class="flex-1" :placeholder="$t('login.password')" show-password @keyup="checkCapslock" />
name="password"
type="password"
size="large"
v-model="loginData.password"
:placeholder="$t('login.password')"
show-password
@keyup="checkCapslock"
/>
</el-form-item> </el-form-item>
</el-tooltip> </el-tooltip>
@ -43,16 +29,8 @@
<div class="icon"> <div class="icon">
<i class="iconfont icon-safe-code"></i> <i class="iconfont icon-safe-code"></i>
</div> </div>
<el-input <el-input class="flex-1" name="verifyCode" size="large" v-model="loginData.verifyCode"
class="flex-1" :placeholder="$t('login.verifyCode')" auto-complete="off" maxlength="6" @keyup.enter="loginClick" />
name="verifyCode"
size="large"
v-model="loginData.verifyCode"
:placeholder="$t('login.verifyCode')"
auto-complete="off"
maxlength="6"
@keyup.enter="loginClick"
/>
<div class="captcha"> <div class="captcha">
<img :src="captchaBase64" @click="getCaptcha" /> <img :src="captchaBase64" @click="getCaptcha" />
</div> </div>
@ -63,7 +41,13 @@
</el-button> </el-button>
</el-form> </el-form>
</div> </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> </div>
</template> </template>
@ -72,15 +56,22 @@ import router from '@/router';
import LangSelect from '@/components/LangSelect/index.vue'; import LangSelect from '@/components/LangSelect/index.vue';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { LocationQuery, LocationQueryValue, useRoute } from 'vue-router'; 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'; import { LoginData } from '@/api/auth/types';
const settingsStore = useSettingsStore();
const userStore = useUserStore(); const userStore = useUserStore();
const route = useRoute(); const route = useRoute();
const loginForm = ref(ElForm); const loginForm = ref(ElForm);
const isCapslock = ref(false);//
const resizeStyle = ref()//
const videoCanPlay = ref(false)
const loginData = ref<LoginData>({ const loginData = ref<LoginData>({
username: '', username: '',
password: '' password: '',
// verifyCode: ''
}); });
const loginRules = { const loginRules = {
@ -100,9 +91,6 @@ function validatePassword(rule: any, value: any, callback: any) {
} }
} }
//
const isCapslock = ref(false);
function checkCapslock(e: any) { function checkCapslock(e: any) {
const { key } = e; const { key } = e;
isCapslock.value = key && key.length === 1 && key >= 'A' && key <= 'Z'; isCapslock.value = key && key.length === 1 && key >= 'A' && key <= 'Z';
@ -110,19 +98,28 @@ function checkCapslock(e: any) {
onMounted(() => { onMounted(() => {
// getCaptcha(); // getCaptcha();
resizeFun()
}); });
/** function onCanplay() {
* 获取验证码 //
*/ videoCanPlay.value = true
function getCaptcha() {
getCaptchaApi().then(({ data }) => {
const { verifyCodeBase64, verifyCodeKey } = data;
loginData.value.verifyCodeKey = verifyCodeKey;
captchaBase64.value = verifyCodeBase64;
});
} }
window.addEventListener('resize', () => {
//
resizeFun()
})
//
// function getCaptcha() {
// getCaptchaApi().then(({ data }) => {
// const { verifyCodeBase64, verifyCodeKey } = data;
// loginData.value.verifyCodeKey = verifyCodeKey;
// captchaBase64.value = verifyCodeBase64;
// });
// }
// //
function loginClick() { function loginClick() {
loginForm.value.validate((valid: boolean) => { loginForm.value.validate((valid: boolean) => {
@ -148,6 +145,34 @@ 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

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 userInfo = JSON.parse(localStorage.getItem('userInfo'));
// const loginIp = userInfo.loginIp.split('.').join(''); // const loginIp = userInfo.loginIp.split('.').join('');
const api = "http://10.10.10.56:9000"//websocket const baseApi = "http://10.10.10.56:9000"//websocket
const apiUrl = api.replace(/https?:/, ''); // const baseApi = import.meta.env.VITE_APP_BASE_API
const apiUrl = baseApi.replace(/https?:/, '');
const wsUrl = `ws:${apiUrl}/websocket/${userInfo.userName}`; //websocket const wsUrl = `ws:${apiUrl}/websocket/${userInfo.userName}`; //websocket
// const wsData = ref(socket.data); // const wsData = ref(socket.data);

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

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

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

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

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

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

4
vite.config.ts

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

Loading…
Cancel
Save