Integrate Vuetify layout and routing
Add a Vuetify-powered application shell (Layout.vue) replacing the previous App.vue, including app bar, navigation drawer, theme toggle, footer and localStorage persistence for theme/drawer. Introduce a routesLayout plugin with a Visibility enum and centralized LayoutRoute definitions; add route components (Home, Impressum, Login, 404NotFound) and update the router to build routes from the new layout definitions. Register Vuetify in main.ts and add dependencies (vuetify, @fontsource/roboto, @mdi/font) in package.json; update tsconfig.app.json to include .ts files. Package-lock.json updated accordingly.
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useTheme } from 'vuetify'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import { Visibility, routes } from '@/plugins/routesLayout'
|
||||
|
||||
const theme = useTheme()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const showDrawer = ref(true)
|
||||
|
||||
function changeTheme() {
|
||||
const nextTheme = theme.global.name.value === 'dark' ? 'light' : 'dark'
|
||||
theme.global.name.value = nextTheme
|
||||
localStorage.setItem('theme', nextTheme)
|
||||
}
|
||||
|
||||
function toggleDrawer() {
|
||||
showDrawer.value = !showDrawer.value
|
||||
localStorage.setItem('drawer', showDrawer.value ? 'Y' : 'N')
|
||||
}
|
||||
|
||||
function changeWebsiteTitle(path: string) {
|
||||
const currentPageInfo = routes.find((x) => x.path === path)
|
||||
if (currentPageInfo) {
|
||||
document.title = `Hoard | ${currentPageInfo.name}`
|
||||
return
|
||||
}
|
||||
|
||||
document.title = 'Hoard'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const storedTheme = localStorage.getItem('theme')
|
||||
const fallbackTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
theme.global.name.value = storedTheme === 'dark' || storedTheme === 'light' ? storedTheme : fallbackTheme
|
||||
|
||||
const storedDrawer = localStorage.getItem('drawer')
|
||||
showDrawer.value = storedDrawer ? storedDrawer.startsWith('Y') : true
|
||||
})
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(newPath) => {
|
||||
changeWebsiteTitle(newPath)
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-app>
|
||||
<v-app-bar elevation="1">
|
||||
<template #prepend>
|
||||
<v-app-bar-nav-icon
|
||||
v-tooltip="!showDrawer ? 'Menue oeffnen' : 'Menue schliessen'"
|
||||
@click="toggleDrawer()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-app-bar-title class="title" @click="router.push({ name: 'Home' })">
|
||||
<span class="pointer">Hoard</span>
|
||||
</v-app-bar-title>
|
||||
|
||||
<v-tooltip v-if="!$vuetify.display.mobile">
|
||||
<template #activator="{ props }">
|
||||
<v-btn icon v-bind="props" to="/login">
|
||||
<v-icon>mdi-account</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
Account
|
||||
</v-tooltip>
|
||||
|
||||
<v-tooltip>
|
||||
<template #activator="{ props }">
|
||||
<v-btn icon @click="changeTheme()" v-bind="props">
|
||||
<v-icon>mdi-brightness-6</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
{{ theme.global.name.value === 'dark' ? 'Hellen Modus aktivieren' : 'Dunklen Modus aktivieren' }}
|
||||
</v-tooltip>
|
||||
</v-app-bar>
|
||||
|
||||
<v-navigation-drawer
|
||||
v-model="showDrawer"
|
||||
:location="$vuetify.display.mobile ? 'bottom' : undefined"
|
||||
:permanent="!$vuetify.display.mobile"
|
||||
>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="item in routes.filter((x) => x.visible === Visibility.Public)"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
:active="route.path === item.path"
|
||||
link
|
||||
:prepend-icon="item.icon"
|
||||
:title="item.name"
|
||||
class="rounded-lg mr-1 ml-1"
|
||||
/>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-main>
|
||||
<router-view />
|
||||
<v-footer class="d-flex align-center justify-center ga-2 flex-wrap flex-grow-1 py-3">
|
||||
<v-btn
|
||||
v-for="link in routes.filter((x) => x.visible === Visibility.Footer)"
|
||||
:key="link.path"
|
||||
:to="link.path"
|
||||
:text="link.name"
|
||||
variant="text"
|
||||
rounded
|
||||
/>
|
||||
<div class="flex-1-0-100 text-center mt-2">
|
||||
{{ new Date().getFullYear() }} - <strong>Hoard</strong>
|
||||
</div>
|
||||
</v-footer>
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user