init
This commit is contained in:
388
src/mac/index.vue
Normal file
388
src/mac/index.vue
Normal file
@@ -0,0 +1,388 @@
|
||||
<template>
|
||||
<div class="mac_bg"></div>
|
||||
<div class="desktop">
|
||||
<div class="top">
|
||||
<el-dropdown trigger="click">
|
||||
<div class="logo"><i class="iconfont iconicon_setting"></i></div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>
|
||||
<div>{{ userInfo.userName }}</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<div @click="switchTheme">退出主题</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="logout">{{ $t('navbar.logOut') }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<div class="space"></div>
|
||||
<div class="status">
|
||||
<div class="audio">
|
||||
<i class="iconfont icon-changyongtubiao-xianxingdaochu-zhuanqu-39"></i>
|
||||
</div>
|
||||
<div class="datetime">{{ timeString }}</div>
|
||||
<div class="notification">
|
||||
<i class="iconfont icon-changyongtubiao-xianxingdaochu-zhuanqu-25"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="desktop-app">
|
||||
<template v-for="item in deskTopAppList" :key="item.key">
|
||||
<div class="app-item" @click="openApp(item)" v-if="!item.hideInDesktop">
|
||||
<div class="icon" :style="{ backgroundColor: item.iconBgColor, color: item.iconColor }">
|
||||
<i class="iconfont" :class="item[iconKey]"></i>
|
||||
</div>
|
||||
<div class="title">{{ item[labelKey] }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="space"></div>
|
||||
<div class="dock">
|
||||
<template v-for="item in openAppList" :key="item.key">
|
||||
<div class="item" @click="openApp(item)">
|
||||
<i
|
||||
:style="{ backgroundColor: item.iconBgColor, color: item.iconColor }"
|
||||
class="iconfont"
|
||||
:class="item[iconKey]"
|
||||
></i>
|
||||
<small style="margin-top: 5px; font-size: 10px">{{ item[labelKey] }}</small>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="space"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import $Mode from './mode/index';
|
||||
import index from '@/mixins/index';
|
||||
import topLock from '@/page/index/top/top-lock.vue';
|
||||
|
||||
export default {
|
||||
mixins: [index],
|
||||
components: {
|
||||
topLock,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
app: false,
|
||||
timeString: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['menu', 'tagList', 'tagWel', 'tag', 'userInfo', 'isMacOs']),
|
||||
labelKey() {
|
||||
return this.website.menu.label;
|
||||
},
|
||||
pathKey() {
|
||||
return this.website.menu.path;
|
||||
},
|
||||
hrefKey() {
|
||||
return this.website.menu.href;
|
||||
},
|
||||
childrenKey() {
|
||||
return this.website.menu.children;
|
||||
},
|
||||
queryKey() {
|
||||
return this.website.menu.query;
|
||||
},
|
||||
iconKey() {
|
||||
return this.website.menu.icon;
|
||||
},
|
||||
menuList() {
|
||||
let result = [];
|
||||
const findMenu = list => {
|
||||
list.forEach(ele => {
|
||||
if (ele[this.childrenKey] && ele[this.childrenKey].length !== 0) {
|
||||
findMenu(ele[this.childrenKey]);
|
||||
} else {
|
||||
result.push(ele);
|
||||
}
|
||||
});
|
||||
};
|
||||
findMenu(this.menu);
|
||||
return result;
|
||||
},
|
||||
deskTopAppList() {
|
||||
return this.menuList;
|
||||
},
|
||||
openAppList() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.startTimer();
|
||||
this.$store.dispatch('GetMenu');
|
||||
},
|
||||
methods: {
|
||||
switchTheme() {
|
||||
this.$store.commit('SET_THEME_NAME', '');
|
||||
this.$router.push(this.tagWel);
|
||||
setTimeout(() => location.reload());
|
||||
},
|
||||
logout() {
|
||||
this.$store.commit('SET_THEME_NAME', '');
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
this.$router.push({ path: '/login' });
|
||||
setTimeout(() => location.reload());
|
||||
});
|
||||
},
|
||||
startTimer() {
|
||||
setInterval(() => {
|
||||
this.timeString = this.$dayjs().format('YYYY年MM月DD日 HH:mm');
|
||||
}, 1000);
|
||||
},
|
||||
openApp(item) {
|
||||
$Mode({
|
||||
title: item[this.labelKey],
|
||||
path: item[this.hrefKey] ? item[this.hrefKey] : item[this.pathKey],
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.top .el-dropdown {
|
||||
color: white !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
</style>
|
||||
<style scoped>
|
||||
.desktop {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
text-shadow: 0px 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.top {
|
||||
height: 28px;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
backdrop-filter: blur(20px);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0px 5px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.space {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0px 15px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo .el-select {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.logo .iconfont {
|
||||
font-size: 16px;
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
.menu .item:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.audio .iconfont,
|
||||
.notification .iconfont {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.datetime,
|
||||
.audio,
|
||||
.notification {
|
||||
cursor: pointer;
|
||||
padding: 0px 10px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.datetime:hover,
|
||||
.audio:hover,
|
||||
.notification:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.body {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.footer .dock {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 10px;
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.dock .item {
|
||||
padding: 3px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dock .dot {
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
border-radius: 5px;
|
||||
display: inline-block;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.dock .item .iconfont {
|
||||
cursor: pointer;
|
||||
border-radius: 20px;
|
||||
padding: 2px;
|
||||
display: inline-block;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform 0.3s, margin 0.3s;
|
||||
}
|
||||
|
||||
.dock .item:hover .iconfont {
|
||||
transform: scale(2) translateY(-10px);
|
||||
margin: 0px 15px;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.dock .nearby .iconfont {
|
||||
transform: scale(1.6) translateY(-8px);
|
||||
margin: 0px 12px;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.dock .nearby1 .iconfont {
|
||||
transform: scale(1.2) translateY(-6px);
|
||||
margin: 0px 9px;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.dock .nearby2 .iconfont {
|
||||
transform: scale(1.1) translateY(-5px);
|
||||
margin: 0px 7px;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.desktop-app {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-end;
|
||||
padding: 20px;
|
||||
flex-wrap: wrap-reverse;
|
||||
}
|
||||
|
||||
.app-item {
|
||||
margin: 5px 10px;
|
||||
padding: 5px;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
text-shadow: 0px 0px 2px rgb(0 0 0 / 50%);
|
||||
cursor: pointer;
|
||||
border-radius: 10px;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.app-item .icon {
|
||||
border-radius: 10px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.app-item .iconfont {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.app-item .title {
|
||||
font-size: 12px;
|
||||
width: 50px;
|
||||
margin-top: 5px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-item:hover {
|
||||
border: 2px solid rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
</style>
|
||||
62
src/mac/lock.vue
Normal file
62
src/mac/lock.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="mac_bg"></div>
|
||||
<div class="login animate__animated" :class="{ animate__bounceOut: pass }">
|
||||
<div class="head">
|
||||
<img
|
||||
src="https://bladex.cn/images/logo-small.png"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div class="message">{{ userInfo.username }}</div>
|
||||
<div class="form">
|
||||
<div class="item" style="width: 320px" :class="passwdError ? 'error' : ''">
|
||||
<input class="password" placeholder="password here..." v-model="passwd" type="password" />
|
||||
<i class="iconfont el-icon-unlock" @click="handleLogin"></i>
|
||||
<i class="iconfont icon-tuichu" @click="handleLogout"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
passwdError: false,
|
||||
passwd: '',
|
||||
pass: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'tag', 'lockPasswd']),
|
||||
},
|
||||
methods: {
|
||||
handleLogout() {
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
this.$router.push({ path: '/login' });
|
||||
});
|
||||
},
|
||||
handleLogin() {
|
||||
if (this.passwd !== this.lockPasswd) {
|
||||
this.passwd = '';
|
||||
this.passwdError = true;
|
||||
setTimeout(() => {
|
||||
this.passwdError = false;
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
this.pass = true;
|
||||
setTimeout(() => {
|
||||
this.$store.commit('CLEAR_LOCK');
|
||||
this.$router.push({
|
||||
path: this.tag.value,
|
||||
});
|
||||
}, 1000);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use './login.scss';
|
||||
</style>
|
||||
134
src/mac/login.scss
Normal file
134
src/mac/login.scss
Normal file
@@ -0,0 +1,134 @@
|
||||
@keyframes loginErrorAnimation {
|
||||
0% {
|
||||
margin-left: -30px;
|
||||
}
|
||||
50% {
|
||||
margin-left: 30px;
|
||||
}
|
||||
100% {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.login {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
margin-top: -100px;
|
||||
z-index: 99999;
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.head {
|
||||
padding: 3px;
|
||||
background-size: 40% auto;
|
||||
background-position: center center;
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
border-radius: 100%;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.1);
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.head img {
|
||||
border-radius: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-top: 20px;
|
||||
font-size: 20px;
|
||||
text-shadow: 0px 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||
color: #eee;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.password {
|
||||
transition: width 0.3s;
|
||||
}
|
||||
|
||||
.password-in {
|
||||
width: 155px;
|
||||
}
|
||||
|
||||
.login-button {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: -50px;
|
||||
transition: right 0.3s;
|
||||
}
|
||||
|
||||
.click-enable {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.error {
|
||||
animation: loginErrorAnimation 0.2s ease 3;
|
||||
}
|
||||
|
||||
::-webkit-input-placeholder {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
::-moz-placeholder {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
width: 280px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item input {
|
||||
color: white;
|
||||
outline: none;
|
||||
border: none;
|
||||
margin: 5px 0;
|
||||
font-size: 16px;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
padding: 8px 24px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item .iconfont {
|
||||
margin-left: 10px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
font-size: 18px;
|
||||
border-radius: 100%;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.item .iconfont:hover {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
72
src/mac/login.vue
Normal file
72
src/mac/login.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div class="mac_bg"></div>
|
||||
<div class="login animate__animated" :class="{ animate__bounceOut: pass }">
|
||||
<div class="head">
|
||||
<img
|
||||
src="https://bladex.cn/images/logo-small.png"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div class="message">Login Please</div>
|
||||
<div class="form">
|
||||
<div class="item" :class="isUserNameError ? 'error' : ''">
|
||||
<input class="account" placeholder="account here..." v-model="form.username" type="email" />
|
||||
</div>
|
||||
<div class="item" :class="isUserPasswordError ? 'error' : ''">
|
||||
<input
|
||||
class="password"
|
||||
placeholder="password here..."
|
||||
v-model="form.password"
|
||||
type="password"
|
||||
/>
|
||||
<i class="iconfont icon-send" @click="handleLogin"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
pass: false,
|
||||
isUserNameError: false,
|
||||
isUserPasswordError: false,
|
||||
form: {
|
||||
username: '',
|
||||
password: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['tagWel']),
|
||||
},
|
||||
methods: {
|
||||
handleLogin() {
|
||||
if (this.form.username === '') {
|
||||
this.isUserNameError = true;
|
||||
setTimeout(() => {
|
||||
this.isUserNameError = false;
|
||||
}, 1000);
|
||||
return;
|
||||
} else if (this.form.password === '') {
|
||||
this.isUserPasswordError = true;
|
||||
setTimeout(() => {
|
||||
this.isUserPasswordError = false;
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
this.$store.dispatch('LoginByUsername', this.loginForm).then(() => {
|
||||
this.pass = true;
|
||||
setTimeout(() => {
|
||||
this.$router.push(this.tagWel);
|
||||
}, 1000);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use './login.scss';
|
||||
</style>
|
||||
18
src/mac/mode/index.js
Normal file
18
src/mac/mode/index.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import MessageConstructor from './index.vue';
|
||||
import { createVNode, render } from 'vue';
|
||||
|
||||
export default (function () {
|
||||
return (opts = {}) => {
|
||||
let options = {
|
||||
app: opts,
|
||||
};
|
||||
const parent = document.createElement('div');
|
||||
let instance = createVNode(MessageConstructor, options);
|
||||
instance.props.onDestroy = () => {
|
||||
render(null, parent);
|
||||
};
|
||||
render(instance, parent);
|
||||
document.body.appendChild(parent.firstElementChild);
|
||||
return instance;
|
||||
};
|
||||
})();
|
||||
483
src/mac/mode/index.vue
Normal file
483
src/mac/mode/index.vue
Normal file
@@ -0,0 +1,483 @@
|
||||
<template>
|
||||
<div
|
||||
class="moveBg"
|
||||
v-show="isShow"
|
||||
@click="handleFocus"
|
||||
@mousemove="mouseMove"
|
||||
@mouseup="mouseUp"
|
||||
@mouseleave.stop="mouseLeave"
|
||||
:style="{ pointerEvents: isBoxResizing || isBoxMoving ? 'auto' : 'none' }"
|
||||
>
|
||||
<div
|
||||
class="box"
|
||||
:style="{
|
||||
left: nowRect.left + 'px',
|
||||
top: nowRect.top + 'px',
|
||||
bottom: nowRect.bottom + 'px',
|
||||
right: nowRect.right + 'px',
|
||||
}"
|
||||
:class="getExtBoxClasses()"
|
||||
>
|
||||
<div class="box-top">
|
||||
<div class="box-top-left" @mousedown="resizeMouseDown"></div>
|
||||
<div class="box-top-center" @mousedown="resizeMouseDown"></div>
|
||||
<div class="box-top-right" @mousedown="resizeMouseDown"></div>
|
||||
</div>
|
||||
<div class="box-center">
|
||||
<div class="box-center-left" @mousedown="resizeMouseDown"></div>
|
||||
<div class="box-center-center loader">
|
||||
<div
|
||||
class="app-bar"
|
||||
:style="{ backgroundColor: app.tabbarBgColor }"
|
||||
@mousedown.stop="positionMouseDown"
|
||||
v-on:dblclick="appBarDoubleClicked"
|
||||
>
|
||||
<div class="controll">
|
||||
<div class="close" @click.stop="close"></div>
|
||||
<div
|
||||
class="full"
|
||||
:class="app.disableResize ? 'full-disabled' : ''"
|
||||
@click.stop="switchFullScreen"
|
||||
></div>
|
||||
</div>
|
||||
<div class="title" :style="{ color: app.tabbarTextColor }">{{ app.title }}</div>
|
||||
</div>
|
||||
<iframe :src="location + app.path" frameborder="0" class="iframe"></iframe>
|
||||
</div>
|
||||
<div class="box-center-right" @mousedown="resizeMouseDown"></div>
|
||||
</div>
|
||||
<div class="box-bottom">
|
||||
<div class="box-bottom-left" @mousedown="resizeMouseDown"></div>
|
||||
<div class="box-bottom-center" @mousedown="resizeMouseDown"></div>
|
||||
<div class="box-bottom-right" @mousedown="resizeMouseDown"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isURL } from 'utils/validate';
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
app: Object,
|
||||
onDestroy: Function,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow: true,
|
||||
defaultIndex: 10,
|
||||
activeIndex: 20,
|
||||
isBoxMoving: false,
|
||||
startPosition: { x: 0, y: 0 },
|
||||
nowRect: {
|
||||
left: 100,
|
||||
right: 100,
|
||||
top: 100,
|
||||
bottom: 100,
|
||||
},
|
||||
startRect: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
isBoxResizing: false,
|
||||
moveDirection: false,
|
||||
isMaxShowing: false,
|
||||
isFullScreen: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
location() {
|
||||
return isURL(this.app.path) ? '' : window.location.origin;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.app.width) {
|
||||
this.nowRect.left = this.nowRect.right = (document.body.clientWidth - this.app.width) / 2;
|
||||
}
|
||||
if (this.app.height) {
|
||||
this.nowRect.bottom = (document.body.clientHeight - this.app.height) / 2 + 50;
|
||||
this.nowRect.top = (document.body.clientHeight - this.app.height) / 2 - 50;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleFocus() {
|
||||
let list = document.getElementsByClassName('moveBg');
|
||||
if (list.length == 1) return;
|
||||
let max = 0;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let ele = list[i];
|
||||
let box = ele.getElementsByClassName('box')[0].style;
|
||||
let zIndex = Number(box.zIndex);
|
||||
if (max < zIndex) {
|
||||
max = zIndex;
|
||||
}
|
||||
}
|
||||
max = max + 1;
|
||||
this.$el.getElementsByClassName('box')[0].style.zIndex = max;
|
||||
},
|
||||
close() {
|
||||
this.isShow = false;
|
||||
this.onDestroy();
|
||||
},
|
||||
switchFullScreen() {
|
||||
if (this.app.disableResize) {
|
||||
return;
|
||||
}
|
||||
this.isFullScreen = !this.isFullScreen;
|
||||
if (this.isFullScreen) {
|
||||
this.isMaxShowing = true;
|
||||
} else {
|
||||
this.isMaxShowing = false;
|
||||
}
|
||||
},
|
||||
getExtBoxClasses() {
|
||||
let str = '';
|
||||
if (!this.isBoxResizing && !this.isBoxMoving) {
|
||||
str += 'box-animation ';
|
||||
}
|
||||
if (this.isMaxShowing) {
|
||||
str += 'isMaxShowing ';
|
||||
}
|
||||
if (this.isFullScreen) {
|
||||
str += 'isFullScreen ';
|
||||
}
|
||||
if (this.app.disableResize) {
|
||||
str += 'resize-disabled ';
|
||||
}
|
||||
if (this.app.isTop) {
|
||||
str += 'isTop ';
|
||||
}
|
||||
return str;
|
||||
},
|
||||
appBarDoubleClicked() {
|
||||
if (this.app.disableResize) {
|
||||
return;
|
||||
}
|
||||
this.isMaxShowing = !this.isMaxShowing;
|
||||
if (!this.isMaxShowing) {
|
||||
this.isFullScreen = false;
|
||||
}
|
||||
},
|
||||
positionMouseDown(e) {
|
||||
if (this.isFullScreen || this.isMaxShowing) {
|
||||
return;
|
||||
}
|
||||
this.startRect = {
|
||||
left: this.nowRect.left,
|
||||
right: this.nowRect.right,
|
||||
top: this.nowRect.top,
|
||||
bottom: this.nowRect.bottom,
|
||||
};
|
||||
this.startPosition.x = e.clientX;
|
||||
this.startPosition.y = e.clientY;
|
||||
this.isBoxMoving = true;
|
||||
},
|
||||
mouseUp() {
|
||||
this.isBoxMoving = false;
|
||||
this.isBoxResizing = false;
|
||||
this.moveDirection = false;
|
||||
},
|
||||
mouseLeave() {
|
||||
this.isBoxMoving = false;
|
||||
this.isBoxResizing = false;
|
||||
this.moveDirection = false;
|
||||
},
|
||||
mouseMove(e) {
|
||||
if (this.isBoxResizing) {
|
||||
this.isFullScreen = false;
|
||||
this.isMaxShowing = false;
|
||||
switch (this.moveDirection) {
|
||||
case 'box-top-left':
|
||||
this.nowRect.top = this.startRect.top + (e.clientY - this.startPosition.y);
|
||||
this.nowRect.left = this.startRect.left + (e.clientX - this.startPosition.x);
|
||||
break;
|
||||
case 'box-top-center':
|
||||
this.nowRect.top = this.startRect.top + (e.clientY - this.startPosition.y);
|
||||
break;
|
||||
case 'box-top-right':
|
||||
this.nowRect.top = this.startRect.top + (e.clientY - this.startPosition.y);
|
||||
this.nowRect.right = this.startRect.right - (e.clientX - this.startPosition.x);
|
||||
break;
|
||||
case 'box-center-left':
|
||||
this.nowRect.left = this.startRect.left + (e.clientX - this.startPosition.x);
|
||||
break;
|
||||
case 'box-bottom-left':
|
||||
this.nowRect.left = this.startRect.left + (e.clientX - this.startPosition.x);
|
||||
this.nowRect.bottom = this.startRect.bottom - (e.clientY - this.startPosition.y);
|
||||
break;
|
||||
case 'box-bottom-center':
|
||||
this.nowRect.bottom = this.startRect.bottom - (e.clientY - this.startPosition.y);
|
||||
break;
|
||||
case 'box-center-right':
|
||||
this.nowRect.right = this.startRect.right - (e.clientX - this.startPosition.x);
|
||||
break;
|
||||
case 'box-bottom-right':
|
||||
this.nowRect.right = this.startRect.right - (e.clientX - this.startPosition.x);
|
||||
this.nowRect.bottom = this.startRect.bottom - (e.clientY - this.startPosition.y);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.isBoxMoving) {
|
||||
this.isFullScreen = false;
|
||||
this.isMaxShowing = false;
|
||||
this.nowRect.left = this.startRect.left + (e.clientX - this.startPosition.x);
|
||||
this.nowRect.right = this.startRect.right - (e.clientX - this.startPosition.x);
|
||||
this.nowRect.top = this.startRect.top + (e.clientY - this.startPosition.y);
|
||||
this.nowRect.bottom = this.startRect.bottom - (e.clientY - this.startPosition.y);
|
||||
return;
|
||||
}
|
||||
},
|
||||
resizeMouseDown(e) {
|
||||
if (this.app.disableResize) {
|
||||
return;
|
||||
}
|
||||
if (this.isFullScreen || this.isMaxShowing) {
|
||||
return;
|
||||
}
|
||||
this.startRect = {
|
||||
left: this.nowRect.left,
|
||||
top: this.nowRect.top,
|
||||
right: this.nowRect.right,
|
||||
bottom: this.nowRect.bottom,
|
||||
};
|
||||
this.startPosition.x = e.clientX;
|
||||
this.startPosition.y = e.clientY;
|
||||
this.isBoxResizing = true;
|
||||
this.moveDirection = e.target.className;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.iframe {
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
margin-top: 5px;
|
||||
border-right: 5px;
|
||||
}
|
||||
|
||||
.moveBg {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.box {
|
||||
--resize: 5px;
|
||||
--resize-bg: transparent;
|
||||
--resize-main: transparent;
|
||||
--resize-bg-main: transparent;
|
||||
}
|
||||
|
||||
.box {
|
||||
position: absolute;
|
||||
pointer-events: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.box-top {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.box-top-left {
|
||||
width: var(--resize);
|
||||
height: var(--resize);
|
||||
background: var(--resize-bg);
|
||||
cursor: nw-resize;
|
||||
}
|
||||
|
||||
.box-top-center {
|
||||
height: var(--resize);
|
||||
background: var(--resize-bg-main);
|
||||
cursor: n-resize;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.box-top-right {
|
||||
width: var(--resize);
|
||||
height: var(--resize);
|
||||
background: var(--resize-bg);
|
||||
cursor: ne-resize;
|
||||
}
|
||||
|
||||
.box-center {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.box-center-left {
|
||||
width: var(--resize);
|
||||
height: 100%;
|
||||
background: var(--resize-bg-main);
|
||||
cursor: w-resize;
|
||||
}
|
||||
|
||||
.box-center-center {
|
||||
height: 100%;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 3px #999;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(20px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.isTop .box-center-center {
|
||||
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.5);
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.box-animation {
|
||||
transition: width 0.1s, height 0.1s, left 0.1s, right 0.1s, top 0.1s, bottom 0.1s;
|
||||
}
|
||||
|
||||
.isMaxShowing {
|
||||
left: -5px !important;
|
||||
right: -5px !important;
|
||||
top: 24px !important;
|
||||
bottom: 47px !important;
|
||||
height: calc(100% - 24px);
|
||||
}
|
||||
|
||||
.isFullScreen {
|
||||
position: fixed !important;
|
||||
z-index: 999 !important;
|
||||
bottom: -5px !important;
|
||||
}
|
||||
|
||||
.isMaxShowing .box-center-center,
|
||||
.isFullScreen .box-center-center {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
.box-center-right {
|
||||
width: var(--resize);
|
||||
height: 100%;
|
||||
background: var(--resize-bg-main);
|
||||
cursor: e-resize;
|
||||
}
|
||||
|
||||
.box-bottom {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.box-bottom-left {
|
||||
width: var(--resize);
|
||||
height: var(--resize);
|
||||
background: var(--resize-bg);
|
||||
cursor: sw-resize;
|
||||
}
|
||||
|
||||
.box-bottom-center {
|
||||
height: var(--resize);
|
||||
background: var(--resize-bg-main);
|
||||
cursor: s-resize;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.box-bottom-right {
|
||||
width: var(--resize);
|
||||
height: var(--resize);
|
||||
background: var(--resize-bg);
|
||||
cursor: se-resize;
|
||||
}
|
||||
|
||||
.loader {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.app-bar {
|
||||
height: 40px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
backdrop-filter: blur(20px);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-bar .controll {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.app-bar .controll div {
|
||||
border-radius: 100%;
|
||||
height: 11px;
|
||||
width: 11px;
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.app-bar .title {
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
margin-right: 80px;
|
||||
font-weight: 500;
|
||||
text-shadow: none;
|
||||
font-size: 13px;
|
||||
cursor: move;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.controll .close {
|
||||
background: #fc605c;
|
||||
border: 1px solid #fc635d;
|
||||
}
|
||||
|
||||
.controll .min {
|
||||
background: #fcbb40;
|
||||
border: 1px solid #f8b438;
|
||||
}
|
||||
|
||||
.controll .full {
|
||||
background: #34c648;
|
||||
border: 1px solid #2bc03f;
|
||||
}
|
||||
|
||||
.full-disabled {
|
||||
background: #ccc !important;
|
||||
border: 1px solid #ccc !important;
|
||||
}
|
||||
|
||||
.resize-disabled .box-top-left,
|
||||
.resize-disabled .box-top-center,
|
||||
.resize-disabled .box-top-right,
|
||||
.resize-disabled .box-center-left,
|
||||
.resize-disabled .box-center-right,
|
||||
.resize-disabled .box-bottom-left,
|
||||
.resize-disabled .box-bottom-center,
|
||||
.resize-disabled .box-bottom-right {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.app-body {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user