182 lines
3.5 KiB
Vue
182 lines
3.5 KiB
Vue
<template>
|
|
<Login v-if="!user && !loading" />
|
|
<div class="app-container" v-else>
|
|
<Header :user="user" @logout="doLogout" v-if="user" />
|
|
|
|
<div class="body">
|
|
<div class="loading-container" v-if="loading">
|
|
<Loading />
|
|
</div>
|
|
|
|
<div class="view-container" v-else>
|
|
<RouterView />
|
|
</div>
|
|
</div>
|
|
|
|
<Messages />
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { RouterLink, RouterView } from 'vue-router'
|
|
|
|
import { type Optional } from './models/Types';
|
|
import Api from './mixins/Api.vue';
|
|
import Dropdowns from './mixins/Dropdowns.vue';
|
|
import Header from './components/Header.vue';
|
|
import Loading from './elements/Loading.vue';
|
|
import Login from './views/Login.vue';
|
|
import Messages from './components/Messages.vue'
|
|
import User from './models/User';
|
|
|
|
export default {
|
|
mixins: [
|
|
Api,
|
|
Dropdowns,
|
|
],
|
|
components: {
|
|
Header,
|
|
Loading,
|
|
Login,
|
|
Messages,
|
|
RouterLink,
|
|
RouterView,
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
loading: false,
|
|
user: null as Optional<User>,
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
async doLogout() {
|
|
await this.logout()
|
|
this.user = null
|
|
},
|
|
},
|
|
|
|
async mounted() {
|
|
this.loading = true
|
|
this.installDropdownHandler()
|
|
|
|
try {
|
|
const auth = await this.fetchUser()
|
|
const currentRedirect = this.$route.query.redirect
|
|
this.user = auth?.user
|
|
|
|
if (auth) {
|
|
if (currentRedirect?.length) {
|
|
this.$router.push(currentRedirect as string)
|
|
}
|
|
} else {
|
|
let redirect = '/login'
|
|
if (currentRedirect?.length && currentRedirect !== '/login') {
|
|
redirect += `?redirect=${currentRedirect}`
|
|
} else if (this.$route.path !== '/login' && this.$route.path !== '/logout') {
|
|
redirect += `?redirect=${this.$route.path}${this.$route.hash}`
|
|
} else {
|
|
redirect += '?redirect=/'
|
|
}
|
|
|
|
this.$router.push(redirect)
|
|
}
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@use "@/styles/common.scss" as *;
|
|
</style>
|
|
|
|
<style lang="scss" scoped>
|
|
@use "@/styles/common.scss" as *;
|
|
|
|
.app-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: var(--color-background);
|
|
|
|
header {
|
|
width: 100%;
|
|
height: $header-height;
|
|
margin-bottom: 0.2rem;
|
|
padding: 0.5rem 0;
|
|
box-shadow: 0 0 0.5rem 0 rgba(0, 0, 0, 0.5);
|
|
|
|
nav {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
li {
|
|
display: inline-block;
|
|
list-style: none;
|
|
border-radius: 0.25rem;
|
|
|
|
&.main {
|
|
font-size: 1.25rem;
|
|
margin-left: 0.5rem;
|
|
|
|
:deep(a) {
|
|
&:hover {
|
|
background: none;
|
|
font-size: 1.5rem;
|
|
}
|
|
}
|
|
|
|
&:hover {
|
|
margin-top: -0.25rem;
|
|
}
|
|
}
|
|
|
|
&:not(.main) {
|
|
:deep(a) {
|
|
color: var(--color-text);
|
|
text-decoration: none;
|
|
}
|
|
}
|
|
|
|
&.right {
|
|
margin-right: 0.5rem;
|
|
}
|
|
}
|
|
|
|
.spacer {
|
|
flex: 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
.body {
|
|
width: 100%;
|
|
height: calc(100% - #{$header-height});
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
position: relative;
|
|
}
|
|
|
|
.loading-container {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
z-index: 1000;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.view-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
}
|
|
</style>
|