chore: add components
This commit is contained in:
parent
f52da5044e
commit
324a14e055
251
src/components/navbar/Navbar.vue
Normal file
251
src/components/navbar/Navbar.vue
Normal file
@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<nav class="flex items-center justify-between pt-1 dark:bg-cool-gray-800 dark:text-white">
|
||||
<!-- Navbar left -->
|
||||
<div class="flex items-center space-x-3">
|
||||
<span class="cursor-pointer p-2 text-xl font-semibold tracking-wider uppercase lg:hidden" @click="on_logo_app">
|
||||
<img class="-mt-1 h-11 w-auto rounded" src="/assets/images/app_w.svg" alt="App">
|
||||
</span>
|
||||
<!-- Toggle sidebar button -->
|
||||
<button class="p-2 rounded-md focus:outline-none focus:ring dark:bg-cool-gray-600" @click="toggleSidebar">
|
||||
<span class="sr-only">Toggle sidebar</span>
|
||||
<ChevronDoubleRight
|
||||
aria-hidden="true"
|
||||
class="w-4 h-4 text-gray-600 dark:text-white"
|
||||
:class="{ 'transform transition-transform -rotate-180': isSidebarOpen }"
|
||||
/>
|
||||
</button>
|
||||
<Breadcrump :bc-path="bcPath" @on-book-selec="bookSelec" />
|
||||
<span class="lg:ml-3 flex">
|
||||
<span
|
||||
class="hidden lg:flex flex-grow"
|
||||
:class="`${navTitle && navTitle.cmpnt === 'boxmenu' ? 'mt-2': ''} ${navTitle.textclick ? 'cursor-pointer': ''}`"
|
||||
@click="onNavTitleClick"
|
||||
>{{ navTitle && navTitle.text || '' }}
|
||||
</span>
|
||||
<BoxMenu
|
||||
v-if="navTitle && navTitle.text !== '' && navTitle.cmpnt === 'boxmenu'"
|
||||
class="-ml-4 lg:ml-0 flex-grow-0"
|
||||
:menu-options="navTitle.ops"
|
||||
:title="navTitle.title"
|
||||
:btn-type="navTitle.btntype"
|
||||
@on-menu-option="onNavTitleMenuOption"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Navbar right -->
|
||||
<nav aria-label="Secondary" class="flex items-center space-x-3">
|
||||
<span class="cursor-pointer p-2 text-xl font-semibold tracking-wider uppercase" @click="on_logo_app">
|
||||
<img class="-mt-1 h-11 w-auto rounded" src="/assets/images/logo.svg" alt="App">
|
||||
</span>
|
||||
<NavbarIconButton
|
||||
v-show="authData.auth === ''"
|
||||
label="Login"
|
||||
@click="router.push('/login')"
|
||||
>
|
||||
<carbon-login /> {{ t('button.login','Login') }}
|
||||
</NavbarIconButton>
|
||||
<NavbarIconButton
|
||||
v-show="useSettings"
|
||||
label="Open setting panel"
|
||||
@click="isSettingsPanelOpen = !isSettingsPanelOpen"
|
||||
>
|
||||
<carbon-settings />
|
||||
</NavbarIconButton>
|
||||
<!-- Search button -->
|
||||
<NavbarIconButton v-if="navbar_right.search" label="Open search panel" @click="isSearchPanelOpen = true">
|
||||
<!-- SearchIcon aria-hidden="true" class="w-6 h-6" -->
|
||||
<carbon-search />
|
||||
</NavbarIconButton>
|
||||
|
||||
<!-- Notification Button -->
|
||||
<NavbarIconButton v-if="navbar_right.notify" label="Open notifications panel" @click="isNotificationsPanelOpen = true">
|
||||
<!--BellIcon aria-hidden="true" class="w-6 h-6" /-->
|
||||
<carbon-notification />
|
||||
</NavbarIconButton>
|
||||
<!-- User menu -->
|
||||
<Menu v-slot="{ open }" as="div" class="relative pr-2">
|
||||
<MenuButton
|
||||
id="user-menu"
|
||||
aria-haspopup="true"
|
||||
:aria-expanded="open ? 'true' : 'false'"
|
||||
class="relative flex items-center overflow-hidden rounded-full group focus:outline-none focus:ring"
|
||||
v-if="authData.auth !== ''"
|
||||
>
|
||||
<span class="sr-only">{{ `${navbar_right.title || 'Open menu'}` }}</span>
|
||||
<img
|
||||
class="object-cover w-8 h-8 rounded-full group-hover:opacity-90"
|
||||
src="/assets/images/jesus.jpg"
|
||||
alt="User image"
|
||||
>
|
||||
<!-- <div class="absolute right-0 p-1 bg-green-400 rounded-full top-1 animate-ping"></div>
|
||||
<div class="absolute right-0 p-1 bg-green-400 border border-white rounded-full top-1"></div> -->
|
||||
</MenuButton>
|
||||
<transition
|
||||
enter-active-class="transition duration-100 ease-out"
|
||||
enter-from-class="transform scale-95 opacity-0"
|
||||
enter-to-class="transform scale-100 opacity-100"
|
||||
leave-active-class="transition duration-75 ease-out"
|
||||
leave-from-class="transform scale-100 opacity-100"
|
||||
leave-to-class="transform scale-95 opacity-0"
|
||||
>
|
||||
<MenuItems
|
||||
class="absolute right-0 z-999 w-48 py-1 mt-2 origin-top-righti dark:bg-gray-600 bg-white rounded-md shadow-lg focus:outline-none"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="user-menu"
|
||||
>
|
||||
<MenuItem
|
||||
v-for="(itm,index) in navbar_right.items"
|
||||
v-slot="{ active }"
|
||||
:key="`${String(index)}-${itm.title}`"
|
||||
>
|
||||
<span class="block text-sm dark:text-gray-300 text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-500">
|
||||
<a
|
||||
v-if="itm && itm.type === 'app_link'"
|
||||
:class="{ 'bg-gray-100': active }"
|
||||
class="px-4 py-2 flex items-center"
|
||||
role="menuitem"
|
||||
@click.prevent="onMenuItemClick(itm)"
|
||||
>
|
||||
{{ t(itm.title) }}
|
||||
</a>
|
||||
<router-link
|
||||
v-if="itm && itm.type === 'router_link'"
|
||||
:title="t(itm.title)"
|
||||
:to="{ name: itm.name_to }"
|
||||
class="px-4 py-2 flex items-center"
|
||||
>
|
||||
{{ t(itm.title) }}
|
||||
</router-link>
|
||||
</span>
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</transition>
|
||||
</Menu>
|
||||
</nav>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/*
|
||||
// https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
|
||||
<MenuItem v-slot="{ active }">
|
||||
<a
|
||||
href="#"
|
||||
class="block px-4 py-2 text-sm text-gray-700"
|
||||
:class="{ 'bg-gray-100': active }"
|
||||
role="menuitem"
|
||||
>
|
||||
{{ t('menu.your-profile') }}
|
||||
</a>
|
||||
</MenuItem>
|
||||
<MenuItem v-slot="{ active }">
|
||||
<a
|
||||
href="#"
|
||||
class="block px-4 py-2 text-sm text-gray-700"
|
||||
:class="{ 'bg-gray-100': active }"
|
||||
role="menuitem"
|
||||
>
|
||||
{{ t('menu.settings') }}
|
||||
</a>
|
||||
</MenuItem>
|
||||
<MenuItem v-slot="{ active }">
|
||||
<a
|
||||
href="#"
|
||||
class="block px-4 py-2 text-sm text-gray-700"
|
||||
:class="{ 'bg-gray-100': active }"
|
||||
role="menuitem"
|
||||
>
|
||||
{{ t('menu.sign-out') }}
|
||||
</a>
|
||||
</MenuItem>
|
||||
*/
|
||||
import {
|
||||
computed,
|
||||
} from 'vue'
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
import ChevronDoubleRight from '@/icons/ChevronDoubleRight.vue'
|
||||
// import SearchIcon from '@/icons/SearchIcon.vue'
|
||||
// import BellIcon from '@/icons/BellIcon.vue'
|
||||
import Breadcrump from '@/Breadcrump.vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import NavbarIconButton from './NavbarIconButton.vue'
|
||||
import BoxMenu from '~/views/BoxMenu.vue'
|
||||
import useState from '~/hooks/useState'
|
||||
import useComponent from '~/hooks/useComponent'
|
||||
// import { NavItemType } from '~/typs'
|
||||
// import { SideMenuItemType } from '~/typs/cmpnts'
|
||||
import { auth_data } from '~/hooks/utils'
|
||||
import { SideMenuItemType } from '~/typs/cmpnts'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useStore()
|
||||
|
||||
const open_menu = ref(false)
|
||||
const { isSidebarOpen, isSearchPanelOpen, isSettingsPanelOpen, toggleSidebar, isNotificationsPanelOpen, bcPath, navTitle } = useState()
|
||||
const { t } = useI18n()
|
||||
|
||||
const authData = computed(() => auth_data())
|
||||
|
||||
const map_key = router.currentRoute.value.meta.uiMapkey || 'ui'
|
||||
|
||||
const defs = computed(() => store.state.app_defs.main.get(map_key) || {})
|
||||
|
||||
const user_image = computed(() => {
|
||||
const image = defs.value.profile && defs.value.profile.image ? defs.value.profile.image : ''
|
||||
if (image === '') {
|
||||
return '/assets/images/user.jpg'
|
||||
}
|
||||
if (image.charAt(0) === '/' || image.includes('http')) {
|
||||
return image
|
||||
} else {
|
||||
return `/assets/images/${image}`
|
||||
}
|
||||
})
|
||||
|
||||
const useSettings = computed(() => Object.entries(useComponent().settingsComponent.value).length !== 0)
|
||||
|
||||
const navbar_right = computed(() => {
|
||||
return defs.value && defs.value.header && defs.value.header.navbar && defs.value.header.navbar.menuright
|
||||
? defs.value.header.navbar.menuright
|
||||
: { title: '', notify: false, search: false, items: [] }
|
||||
})
|
||||
|
||||
const bookSelec = (target: string) => {
|
||||
switch (target) {
|
||||
case 'home':
|
||||
useState().bcPath.value = ''
|
||||
useState().dfltNavTitle()
|
||||
router.push('/')
|
||||
break
|
||||
default:
|
||||
useState().bookSelec(target)
|
||||
}
|
||||
}
|
||||
const on_logo_app = () => {
|
||||
useState().bcPath.value = ''
|
||||
useState().dfltNavTitle()
|
||||
useState().isSidebarOpen.value = false
|
||||
if (useState().app_home_click.value) {
|
||||
useState().app_home_click.value()
|
||||
}
|
||||
router.push('/')
|
||||
}
|
||||
const onMenuItemClick = (itm: SideMenuItemType ) => {
|
||||
const ustate: any = useState()
|
||||
const ky=`show_${itm.click}`
|
||||
if (ustate[ky])
|
||||
ustate[ky].value = !ustate[ky].value
|
||||
}
|
||||
const onNavTitleMenuOption = (data: any) => {
|
||||
if (navTitle.value.cllbck)
|
||||
navTitle.value.cllbck(data)
|
||||
}
|
||||
const onNavTitleClick = () => {
|
||||
if (navTitle.value.textclick)
|
||||
navTitle.value.textclick()
|
||||
}
|
||||
</script>
|
18
src/components/navbar/NavbarIconButton.vue
Normal file
18
src/components/navbar/NavbarIconButton.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center justify-center p-2 text-gray-400 transition-colors bg-gray-100 rounded-full focus:outline-none focus:ring hover:bg-gray-200 hover:text-gray-500 dark:bg-cool-gray-600 dark:text-white"
|
||||
>
|
||||
<span class="sr-only">{{ label }}</span>
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
</script>
|
236
src/components/navbar/v.vue
Normal file
236
src/components/navbar/v.vue
Normal file
@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<nav class="flex items-center justify-between pt-1 dark:bg-cool-gray-800 dark:text-white">
|
||||
<!-- Navbar left -->
|
||||
<div class="flex items-center space-x-3">
|
||||
<span class="cursor-pointer p-2 text-xl font-semibold tracking-wider uppercase lg:hidden" @click="on_logo_app">
|
||||
<img class="-mt-1 h-11 w-auto rounded" src="/assets/images/app_w.svg" alt="App">
|
||||
</span>
|
||||
<!-- Toggle sidebar button -->
|
||||
<button class="p-2 rounded-md focus:outline-none focus:ring dark:bg-cool-gray-600" @click="toggleSidebar">
|
||||
<span class="sr-only">Toggle sidebar</span>
|
||||
<ChevronDoubleRight
|
||||
aria-hidden="true"
|
||||
class="w-4 h-4 text-gray-600 dark:text-white"
|
||||
:class="{ 'transform transition-transform -rotate-180': isSidebarOpen }"
|
||||
/>
|
||||
</button>
|
||||
<Breadcrump :bc-path="bcPath" @on-book-selec="bookSelec" />
|
||||
<span class="lg:ml-3 flex">
|
||||
<span
|
||||
class="hidden lg:flex flex-grow"
|
||||
:class="`${navTitle && navTitle.cmpnt === 'boxmenu' ? 'mt-2': ''} ${navTitle.textclick ? 'cursor-pointer': ''}`"
|
||||
@click="onNavTitleClick"
|
||||
>{{ navTitle && navTitle.text || '' }}
|
||||
</span>
|
||||
<BoxMenu
|
||||
v-if="navTitle && navTitle.text !== '' && navTitle.cmpnt === 'boxmenu'"
|
||||
class="-ml-4 lg:ml-0 flex-grow-0"
|
||||
:menu-options="navTitle.ops"
|
||||
:title="navTitle.title"
|
||||
:btn-type="navTitle.btntype"
|
||||
@on-menu-option="onNavTitleMenuOption"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Navbar right -->
|
||||
<nav aria-label="Secondary" class="flex items-center space-x-3">
|
||||
<span class="cursor-pointer p-2 text-xl font-semibold tracking-wider uppercase" @click="on_logo_app">
|
||||
<img class="-mt-1 h-11 w-auto rounded" src="/assets/images/logo.svg" alt="App">
|
||||
</span>
|
||||
<NavbarIconButton
|
||||
v-show="useSettings"
|
||||
label="Open setting panel"
|
||||
@click="isSettingsPanelOpen = !isSettingsPanelOpen"
|
||||
>
|
||||
<carbon-settings />
|
||||
</NavbarIconButton>
|
||||
<!-- Search button -->
|
||||
<NavbarIconButton v-if="navbar_right.search" label="Open search panel" @click="isSearchPanelOpen = true">
|
||||
<!-- SearchIcon aria-hidden="true" class="w-6 h-6" -->
|
||||
<carbon-search />
|
||||
</NavbarIconButton>
|
||||
|
||||
<!-- Notification Button -->
|
||||
<NavbarIconButton v-if="navbar_right.notify" label="Open notifications panel" @click="isNotificationsPanelOpen = true">
|
||||
<!--BellIcon aria-hidden="true" class="w-6 h-6" /-->
|
||||
<carbon-notification />
|
||||
</NavbarIconButton>
|
||||
<!-- User menu -->
|
||||
<Menu v-slot="{ open }" as="div" class="relative pr-2">
|
||||
<span> {{ navbar_right.items}} </span>
|
||||
<MenuButton
|
||||
id="user-menu"
|
||||
aria-haspopup="true"
|
||||
:aria-expanded="open ? 'true' : 'false'"
|
||||
class="relative flex items-center overflow-hidden rounded-full group focus:outline-none focus:ring"
|
||||
>
|
||||
<span class="sr-only">{{ `${navbar_right.title || 'Open menu'}` }}</span>
|
||||
<img
|
||||
class="object-cover w-8 h-8 rounded-full group-hover:opacity-90"
|
||||
src="/assets/images/jesus.jpg"
|
||||
alt="User image"
|
||||
>
|
||||
<!-- <div class="absolute right-0 p-1 bg-green-400 rounded-full top-1 animate-ping"></div>
|
||||
<div class="absolute right-0 p-1 bg-green-400 border border-white rounded-full top-1"></div> -->
|
||||
</MenuButton>
|
||||
<transition
|
||||
enter-active-class="transition duration-100 ease-out"
|
||||
enter-from-class="transform scale-95 opacity-0"
|
||||
enter-to-class="transform scale-100 opacity-100"
|
||||
leave-active-class="transition duration-75 ease-out"
|
||||
leave-from-class="transform scale-100 opacity-100"
|
||||
leave-to-class="transform scale-95 opacity-0"
|
||||
>
|
||||
<MenuItems
|
||||
class="absolute right-0 z-999 w-48 py-1 mt-2 origin-top-righti dark:bg-gray-600 bg-white rounded-md shadow-lg focus:outline-none"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="user-menu"
|
||||
>
|
||||
<MenuItem
|
||||
v-for="(itm,index) in navbar_right.items"
|
||||
v-slot="{ active }"
|
||||
:key="`${String(index)}-${itm.title}`"
|
||||
>
|
||||
<span class="block text-sm dark:text-gray-300 text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-500">
|
||||
<a
|
||||
v-if="itm && itm.type === 'app_link'"
|
||||
:href="itm.link"
|
||||
:class="{ 'bg-gray-100': active }"
|
||||
class="px-4 py-2"
|
||||
role="menuitem"
|
||||
>
|
||||
{{ t(itm.title) }}
|
||||
</a>
|
||||
<router-link
|
||||
v-if="itm && itm.type === 'router_link'"
|
||||
:title="t(itm.title)"
|
||||
:to="{ name: itm.name_to }"
|
||||
class="px-4 py-2 flex items-center"
|
||||
>
|
||||
{{ t(itm.title) }}
|
||||
</router-link>
|
||||
</span>
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</transition>
|
||||
</Menu>
|
||||
</nav>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/*
|
||||
// https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
|
||||
<MenuItem v-slot="{ active }">
|
||||
<a
|
||||
href="#"
|
||||
class="block px-4 py-2 text-sm text-gray-700"
|
||||
:class="{ 'bg-gray-100': active }"
|
||||
role="menuitem"
|
||||
>
|
||||
{{ t('menu.your-profile') }}
|
||||
</a>
|
||||
</MenuItem>
|
||||
<MenuItem v-slot="{ active }">
|
||||
<a
|
||||
href="#"
|
||||
class="block px-4 py-2 text-sm text-gray-700"
|
||||
:class="{ 'bg-gray-100': active }"
|
||||
role="menuitem"
|
||||
>
|
||||
{{ t('menu.settings') }}
|
||||
</a>
|
||||
</MenuItem>
|
||||
<MenuItem v-slot="{ active }">
|
||||
<a
|
||||
href="#"
|
||||
class="block px-4 py-2 text-sm text-gray-700"
|
||||
:class="{ 'bg-gray-100': active }"
|
||||
role="menuitem"
|
||||
>
|
||||
{{ t('menu.sign-out') }}
|
||||
</a>
|
||||
</MenuItem>
|
||||
*/
|
||||
import {
|
||||
computed,
|
||||
} from 'vue'
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
import ChevronDoubleRight from '@/icons/ChevronDoubleRight.vue'
|
||||
// import SearchIcon from '@/icons/SearchIcon.vue'
|
||||
// import BellIcon from '@/icons/BellIcon.vue'
|
||||
import Breadcrump from '@/Breadcrump.vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import NavbarIconButton from './NavbarIconButton.vue'
|
||||
import BoxMenu from '~/views/BoxMenu.vue'
|
||||
import useState from '~/hooks/useState'
|
||||
import useComponent from '~/hooks/useComponent'
|
||||
// import { NavItemType } from '~/typs'
|
||||
// import { SideMenuItemType } from '~/typs/cmpnts'
|
||||
import { auth_data } from '~/hooks/utils'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useStore()
|
||||
|
||||
const { isSidebarOpen, isSearchPanelOpen, isSettingsPanelOpen, toggleSidebar, isNotificationsPanelOpen, bcPath, navTitle } = useState()
|
||||
const { t } = useI18n()
|
||||
|
||||
const authData = computed(() => auth_data())
|
||||
|
||||
const map_key = router.currentRoute.value.meta.uiMapkey || 'ui'
|
||||
|
||||
const defs = computed(() => store.state.app_defs.main.get(map_key) || {})
|
||||
|
||||
const user_image = computed(() => {
|
||||
const image = defs.value.profile && defs.value.profile.image ? defs.value.profile.image : ''
|
||||
if (image === '') {
|
||||
return '/assets/images/user.jpg'
|
||||
}
|
||||
if (image.charAt(0) === '/' || image.includes('http')) {
|
||||
return image
|
||||
} else {
|
||||
return `/assets/images/${image}`
|
||||
}
|
||||
})
|
||||
|
||||
const useSettings = computed(() => Object.entries(useComponent().settingsComponent.value).length !== 0)
|
||||
|
||||
const navbar_right = computed(() => {
|
||||
return defs.value && defs.value.header && defs.value.header.navbar && defs.value.header.navbar.menuright
|
||||
? defs.value.header.navbar.menuright
|
||||
: { title: '', notify: false, search: false, items: [] }
|
||||
})
|
||||
|
||||
const bookSelec = (target: string) => {
|
||||
switch (target) {
|
||||
case 'home':
|
||||
useState().bcPath.value = ''
|
||||
useState().dfltNavTitle()
|
||||
router.push('/')
|
||||
break
|
||||
default:
|
||||
useState().bookSelec(target)
|
||||
}
|
||||
}
|
||||
const on_logo_app = () => {
|
||||
useState().bcPath.value = ''
|
||||
useState().dfltNavTitle()
|
||||
useState().isSidebarOpen.value = false
|
||||
if (useState().app_home_click.value) {
|
||||
useState().app_home_click.value()
|
||||
}
|
||||
router.push('/')
|
||||
}
|
||||
const onNavTitleMenuOption = (data: any) => {
|
||||
if (navTitle.value.cllbck)
|
||||
navTitle.value.cllbck(data)
|
||||
}
|
||||
const onNavTitleClick = () => {
|
||||
if (navTitle.value.textclick)
|
||||
navTitle.value.textclick()
|
||||
}
|
||||
</script>
|
45
src/components/panels/AsidePanel.vue
Normal file
45
src/components/panels/AsidePanel.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<Panel :show="show" :title="title" id="aside" @close="close">
|
||||
<div class="flex-1 max-h-full p-4 overflow-hidden hover:overflow-y-auto">
|
||||
<keep-alive>
|
||||
<component :is="asideComponent" />
|
||||
</keep-alive>
|
||||
<slot />
|
||||
</div>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Panel from './Panel.vue'
|
||||
import useComponent, { DynComponent } from '~/hooks/useComponent'
|
||||
|
||||
defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: () => '',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['close'])
|
||||
const cmpnt = useComponent()
|
||||
const close = () => {
|
||||
emit('close')
|
||||
}
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', (event) => {
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
emit('close')
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
const { t, locale } = useI18n()
|
||||
const asideComponent = computed(() => cmpnt.asideComponent.value)
|
||||
</script>
|
40
src/components/panels/NotificationsPanel.vue
Normal file
40
src/components/panels/NotificationsPanel.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<Panel :show="show" :title="t(title || 'notifications.notifications')" id="notifycation" @close="close">
|
||||
<div class="flex-1 max-h-full p-4 overflow-hidden hover:overflow-y-auto">
|
||||
<span>{{ t('notifications.content') }}</span>
|
||||
<!-- Notifications Panel Content ... -->
|
||||
</div>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Panel from './Panel.vue'
|
||||
|
||||
defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: () => '',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['close'])
|
||||
const close = () => {
|
||||
emit('close')
|
||||
}
|
||||
const { t, locale } = useI18n()
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', (event) => {
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
emit('close')
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
73
src/components/panels/Panel.vue
Normal file
73
src/components/panels/Panel.vue
Normal file
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<!-- Backdrop -->
|
||||
<Backdrop :show="show" :blur="backdrop_blur" @close="close" />
|
||||
|
||||
<!-- Panel -->
|
||||
<transition
|
||||
enter-active-class="transition duration-300 ease-in-out transform"
|
||||
:enter-from-class="left ? '-translate-x-full' : 'translate-x-full'"
|
||||
:enter-to-class="left ? '-translate-x-0' : 'translate-x-0'"
|
||||
leave-active-class="transition duration-300 ease-in-out transform"
|
||||
:leave-from-class="left ? '-translate-x-0' : 'translate-x-0'"
|
||||
:leave-to-class="left ? '-translate-x-full' : 'translate-x-full'"
|
||||
>
|
||||
<section
|
||||
v-if="show"
|
||||
:aria-labelledby="title"
|
||||
class="fixed z-999 border-left-2 border-gray-400 max-w-xs bg-white sm:max-w-md ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-cool-gray-800 dark:text-white"
|
||||
:class="`${left ? 'left-0' : 'right-0'} bg-opacity-${back_opacity} ${panel_style}`"
|
||||
>
|
||||
<div class="flex items-center justify-between flex-shrink-0 p-2">
|
||||
<h6 class="p-2 text-lg">
|
||||
{{ title }}
|
||||
</h6>
|
||||
<!-- Close button -->
|
||||
<button class="p-2 rounded-md focus:outline-none focus:ring dark:bg-cool-gray-600" @click="close">
|
||||
<CloseIcon class="w-6 h-6 text-gray-600 dark:text-white" />
|
||||
</button>
|
||||
</div>
|
||||
<!-- Panel content -->
|
||||
<slot />
|
||||
</section>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Backdrop from '../global/Backdrop.vue'
|
||||
import CloseIcon from '../icons/CloseIcon.vue'
|
||||
import useState from '~/hooks/useState'
|
||||
|
||||
const props = defineProps<{
|
||||
show: {
|
||||
type: boolean,
|
||||
required: true,
|
||||
},
|
||||
left: {
|
||||
type: boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
title: {
|
||||
type: string,
|
||||
required: true,
|
||||
},
|
||||
id: {
|
||||
type: string,
|
||||
required: true,
|
||||
},
|
||||
}>()
|
||||
|
||||
const backdrop_blur = useState().backdrop_blur
|
||||
const back_opacity = useState().back_opacity
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
const panel_style = computed(() => {
|
||||
const key: any = props.id || ''
|
||||
return useState().panels.value[key] && useState().panels.value[key].style ? useState().panels.value[key].style : 'inset-y-0 w-full'
|
||||
})
|
||||
|
||||
const close = () => {
|
||||
emit('close')
|
||||
}
|
||||
</script>
|
51
src/components/panels/SearchPanel.vue
Normal file
51
src/components/panels/SearchPanel.vue
Normal file
@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<Panel :show="show" :title="t(title || 'search.search')" id="search" @close="close">
|
||||
<div class="flex-1 max-h-full p-4 space-y-4 overflow-hidden hover:overflow-y-auto dark:text-gray-600 dark:text-white">
|
||||
<form @submit.prevent="close" >
|
||||
<input
|
||||
type="text"
|
||||
v-model="search"
|
||||
:placeholder="`${t('search.search')}...`"
|
||||
class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring dark:text-gray-600 dark:text-white"
|
||||
>
|
||||
</form>
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Panel from './Panel.vue'
|
||||
import useState from '~/hooks/useState'
|
||||
|
||||
const search = useState().search
|
||||
|
||||
defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: () => '',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['close'])
|
||||
const close = () => {
|
||||
emit('close')
|
||||
}
|
||||
const { t, locale } = useI18n()
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', (event) => {
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
emit('close')
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
47
src/components/panels/SettingsPanel.vue
Normal file
47
src/components/panels/SettingsPanel.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<Panel :show="show" :title="title || t('settings.settings')" id="setting" @close="close">
|
||||
<div class="flex-1 max-h-full p-4 overflow-hidden hover:overflow-y-auto">
|
||||
<keep-alive>
|
||||
<component :is="currentComponent" />
|
||||
</keep-alive>
|
||||
<slot />
|
||||
<!-- Settings Panel Content ... -->
|
||||
</div>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Panel from './Panel.vue'
|
||||
import useComponent, { DynComponent } from '~/hooks/useComponent'
|
||||
|
||||
defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: () => '',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['close'])
|
||||
const cmpnt = useComponent()
|
||||
const close = () => {
|
||||
emit('close')
|
||||
}
|
||||
const { t, locale } = useI18n()
|
||||
const currentComponent = computed(() => cmpnt.settingsComponent.value)
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', (event) => {
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
emit('close')
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
170
src/components/sidebar/Sidebar.vue
Normal file
170
src/components/sidebar/Sidebar.vue
Normal file
@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<Backdrop :show="isSidebarOpen" :blur="backdrop_blur" class="lg:hidden" @close="isSidebarOpen = false" />
|
||||
<aside
|
||||
class="fixed inset-y-0 z-999 flex flex-col flex-shrink-0 w-64 max-h-screen transition-all transform border-r dark:border-gray-700 border-gray-200 bg-white shadow-lg lg:z-auto lg:static lg:shadow-none"
|
||||
:class="{ '-translate-x-full lg:translate-x-0 lg:w-20': !isSidebarOpen }"
|
||||
>
|
||||
<!-- sidebar header -->
|
||||
<SidebarHeader />
|
||||
<!-- Sidebar links -->
|
||||
<nav aria-label="Main" class="dark:bg-cool-gray-800 dark:text-white bg-white flex-1 overflow-hidden hover:overflow-y-auto">
|
||||
<!-- Sidebar Links... -->
|
||||
<ul class="p-2">
|
||||
<li
|
||||
v-for="(itm,index) in sidebarMenuItems"
|
||||
:key="`${String(index)}-${itm.title}`"
|
||||
dark:border-gray-700
|
||||
border-gray-200
|
||||
>
|
||||
<hr v-if="itm.type === NavItemType.separator" class=" dark:border-gray-600 border-gray-300">
|
||||
<router-link
|
||||
v-if="itm.type === NavItemType.router_link"
|
||||
:title="t(itm.title)"
|
||||
:to="{ name: itm.name_to }"
|
||||
class="flex items-center p-2 overflow-hidden rounded-md hover:bg-gray-100 dark:hover:bg-gray-500"
|
||||
:class="{ 'justify-center': !isSidebarOpen }"
|
||||
>
|
||||
<IconLink :typ="itm.type" :icon="itm.icon_on" :mode="itm.mode||''" :open="isSidebarOpen" :pfx="itm.pfx" :title="itm.title" :show_to="itm.show_to" :name="itm.name_to" />
|
||||
</router-link>
|
||||
<span
|
||||
v-if="itm.type === NavItemType.module_label"
|
||||
class="border-bottom-1 dark:border-gray-700 border-gray-200 flex text-xs color-gray-100"
|
||||
:class="isSidebarOpen ? 'justify-end' : 'justify-start'"
|
||||
>
|
||||
<span class="float-right text-gray-400" :class="{ 'lg:hidden': !isSidebarOpen }"> {{ t(`menu.${itm.label}`,'') }}</span>
|
||||
</span>
|
||||
<a
|
||||
v-if="itm.type == NavItemType.a_blank"
|
||||
class="flex items-center p-2 overflow-hidden rounded-md hover:bg-gray-100 dark:hover:bg-gray-500"
|
||||
:class="{ 'justify-center': !isSidebarOpen }"
|
||||
@click="itemClick(itm.click || '')"
|
||||
>
|
||||
<IconLink :typ="itm.type" :icon="itm.icon_on" :mode="itm.mode || ''" :open="isSidebarOpen" :pfx="itm.pfx" :title="itm.title" :show_to="itm.show_to" :name="itm.name_to" />
|
||||
</a>
|
||||
<a
|
||||
v-if="itm.type == NavItemType.a_link"
|
||||
rel="noreferrer"
|
||||
:href="itm.href || '#'"
|
||||
target="_blank"
|
||||
:title="t(itm.title)"
|
||||
>
|
||||
<span v-if="itm.icon_on === 'carbon-launch'"> <carbon-launch /> </span>
|
||||
<span class="ml-2" :class="{ 'lg:hidden': !isSidebarOpen }"> {{ t(itm.title) }}</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="itm.type == NavItemType.app_link"
|
||||
:title="t(itm.title)"
|
||||
class="flex items-center p-2 overflow-hidden rounded-md hover:bg-gray-100 dark:hover:bg-gray-500"
|
||||
:class="{ 'justify-center': !isSidebarOpen, 'ml-5': itm.name_to && itm.name_to === 'items' }"
|
||||
@click.prevent="appItemClick(itm)"
|
||||
>
|
||||
<IconLink :typ="itm.type" :icon="itm.icon_on" :mode="itm.mode || ''" :open="isSidebarOpen" :pfx="itm.pfx" :title="itm.title" :show_to="itm.show_to" :name="itm.name_to" />
|
||||
</a>
|
||||
<a
|
||||
v-if="itm.type == NavItemType.cloud_link && !isSidebarOpen && itm.name_to && itm.name_to !== 'items'"
|
||||
:title="t(itm.title)"
|
||||
class="flex items-center justify-center p-2 overflow-hidden rounded-md hover:bg-gray-300 dark:hover:text-gray-100 dark:hover:bg-gray-500"
|
||||
@click.prevent="appItemClick(itm)"
|
||||
>
|
||||
<span v-if="itm.name_to && itm.name_to !== 'cloud'" class="flex -ml-2 text-gray-500 dark:text-gray-500 dark:hover:text-gray-100 dark:hover:bg-gray-500">
|
||||
<span v-if="isSidebarOpen && itm.pfx" class="-ml-2 mr-2 font-light">
|
||||
<small class="text-gray-400">{{ itm.pfx }}</small>
|
||||
</span>
|
||||
<span class="ml-2 text-xs font-light"> {{ itm.name || '' }}</span>
|
||||
</span>
|
||||
<span v-else class="pt-1 pb-1 border-b border-t border-gray-200 dark:border-gray-500">
|
||||
<span v-if="itm.icon_on === 'carbon-cloud'" class="flex justify-center items-center"> <carbon-cloud /> </span>
|
||||
<span class="text-xs flex justify-center items-center "> {{ itm.name || '' }}</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<span @click="on_logo_app" class="cursor-pointer text-xl font-semibold tracking-wider uppercase dark:bg-cool-gray-800 dark:text-white bg-white">
|
||||
<div class="flex">
|
||||
<img class="-mt-1 h-11 w-auto rounded" src="/assets/images/logo.svg" alt="App">
|
||||
<span :class="{ 'lg:hidden': !isSidebarOpen }" class="ml-4 mt-2 text-gray-400">{{ t('app.title','') }}</span>
|
||||
</div>
|
||||
</span>
|
||||
<SidebarFooter />
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Backdrop from '@/global/Backdrop.vue'
|
||||
import IconLink from '@/IconLink.vue'
|
||||
import SidebarFooter from './SidebarFooter.vue'
|
||||
import SidebarHeader from './SidebarHeader.vue'
|
||||
import useState from '~/hooks/useState'
|
||||
import { SideMenuItemType } from '~/typs/cmpnts'
|
||||
import { NavItemType } from '~/typs'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useStore()
|
||||
const { isSidebarOpen } = useState()
|
||||
const { t } = useI18n()
|
||||
|
||||
const map_key = router.currentRoute.value.meta.uiMapkey || 'ui'
|
||||
|
||||
const defs = computed(() => store.state.app_defs.main.get(map_key) || {})
|
||||
|
||||
const backdrop_blur = useState().backdrop_blur
|
||||
|
||||
const sidebarMenuItems = computed((): SideMenuItemType[] => {
|
||||
const ctx = router.currentRoute.value.meta.ctx || ''
|
||||
const defsMenuItems = defs.value && defs.value.sidebar && defs.value.sidebar.menu_items ? defs.value.sidebar.menu_items : [] as SideMenuItemType[]
|
||||
const all_items = [].concat(defsMenuItems || []) // .concat(useState().sidebarMenuItems.value as SideMenuItemType[] | any)
|
||||
useState().sidebarMenuItems.value = all_items.filter((itm: SideMenuItemType) => {
|
||||
if (itm.ctx && itm.ctx === ctx) {
|
||||
return true
|
||||
} else if (itm.ctx && itm.ctx !== ctx) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
return useState().sidebarMenuItems.value
|
||||
})
|
||||
|
||||
const itemClick = (target: string) => {
|
||||
switch (target) {
|
||||
case 'home':
|
||||
useState().bcPath.value = ''
|
||||
// useState().navTitle.value = ''
|
||||
router.push('/')
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
const appItemClick = (itm: SideMenuItemType) => {
|
||||
if (itm.cllbck) {
|
||||
itm.cllbck(itm)
|
||||
isSidebarOpen.value = false
|
||||
} else {
|
||||
if (useState().side_menu_click.value) {
|
||||
try {
|
||||
useState().side_menu_click.value(itm)
|
||||
isSidebarOpen.value.value = false
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
}
|
||||
} else if (itm.click === 'tophome') {
|
||||
router.push('/')
|
||||
}
|
||||
}
|
||||
}
|
||||
const on_logo_app = () => {
|
||||
useState().bcPath.value = ''
|
||||
useState().dfltNavTitle()
|
||||
useState().isSidebarOpen.value = false
|
||||
if (useState().app_home_click.value) {
|
||||
useState().app_home_click.value()
|
||||
}
|
||||
router.push('/')
|
||||
}
|
||||
</script>
|
62
src/components/sidebar/SidebarFooter.vue
Normal file
62
src/components/sidebar/SidebarFooter.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="flex-shrink-0 p-4 border-t dark:border-gray-700 border-gray-200 max-h-14 dark:bg-cool-gray-800 dark:text-white">
|
||||
<Button
|
||||
v-if="sidebarFooter.itm.type === NavItemType.a_link"
|
||||
class="flex w-full text-gray-700 bg-gray-100 hover:bg-gray-700 dark:bg-cool-gray-800 dark:text-gray-100 dark:hover:bg-gray-700"
|
||||
:class="{'justify-start': isSidebarOpen}"
|
||||
@click="itemClick(sidebarFooter.itm.click || '')"
|
||||
>
|
||||
<!-- LogoutIcon aria-hidden="true" class="w-6 h-6" -->
|
||||
<span v-if="sidebarFooter.itm.icon_on === 'carbon-logout'"> <carbon-logout /> </span>
|
||||
<span :class="{ 'lg:hidden': !isSidebarOpen }" class="ml-2"> {{ t(sidebarFooter.itm.title) }} </span>
|
||||
</Button>
|
||||
<router-link
|
||||
v-if="sidebarFooter.itm.type === NavItemType.router_link"
|
||||
:to="{ name: `${sidebarFooter.itm.name_to === 'Logout' && authData.auth === '' ? 'Login' : 'Logout'}` }"
|
||||
class="flex w-full text-gray-700 bg-gray-100 dark:bg-cool-gray-800 dark:text-gray-100 dark:hover:bg-gray-700"
|
||||
:class="{'justify-start': isSidebarOpen}"
|
||||
>
|
||||
<span v-if="sidebarFooter.itm.icon_on === 'carbon-logout' && authData.auth !== ''" class="mt-1"> <carbon-logout /> </span>
|
||||
<span v-else class="mt-1"> <carbon-login /> </span>
|
||||
<span :class="{ 'lg:hidden': !isSidebarOpen }" class="ml-2"> {{ t(sidebarFooter.itm.title) }} </span>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from '@/global/Button.vue'
|
||||
// import LogoutIcon from '@/icons/LogoutIcon.vue'
|
||||
import { computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import useState from '~/hooks/useState'
|
||||
import { NavItemType } from '~/typs'
|
||||
import { auth_data } from '~/hooks/utils'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useStore()
|
||||
const { isSidebarOpen } = useState()
|
||||
const { t } = useI18n()
|
||||
|
||||
const map_key = router.currentRoute.value.meta.uiMapkey || 'ui'
|
||||
|
||||
const defs = computed(() => store.state.app_defs.main.get(map_key) || {})
|
||||
|
||||
const authData = computed(() => auth_data())
|
||||
|
||||
const sidebarFooter = computed(() => {
|
||||
return defs.value && defs.value.sidebar && defs.value.sidebar.footer ? defs.value.sidebar.footer : { itm: {} }
|
||||
})
|
||||
|
||||
const itemClick = (target: string) => {
|
||||
switch (target) {
|
||||
case 'home':
|
||||
useState().bcPath.value = ''
|
||||
useState().navTitle.value = {} as any
|
||||
router.push('/')
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
58
src/components/sidebar/SidebarHeader.vue
Normal file
58
src/components/sidebar/SidebarHeader.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-between flex-shrink-0 p-2 dark:bg-cool-gray-800 dark:text-white" :class="{ 'lg:justify-center': !isSidebarOpen }">
|
||||
<span
|
||||
class="cursor-pointer text-lg lg:text-xl font-semibold leading-8 tracking-wider uppercase whitespace-nowrap dark:bg-cool-gray-800 dark:text-white"
|
||||
@click="on_logo_app"
|
||||
>
|
||||
<div class="flex">
|
||||
<img class="h-11 w-auto rounded" :src="`${header.logo && header.logo !== '' ? header.logo : '/assets/images/app_w.svg'}`" alt="App">
|
||||
<span :class="{ 'lg:hidden': !isSidebarOpen }" class="ml-4 mt-2 text-gray-400">{{ header.title || '' }}</span>
|
||||
</div>
|
||||
</span>
|
||||
<button class="rounded-md lg:hidden" @click="isSidebarOpen = false">
|
||||
<svg
|
||||
class="w-6 h-6 text-gray-600"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import useState from '~/hooks/useState'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useStore()
|
||||
|
||||
const { isSidebarOpen } = useState()
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
const map_key = router.currentRoute.value.meta.uiMapkey || 'ui'
|
||||
|
||||
const defs = computed(() => store.state.app_defs.main.get(map_key) || {})
|
||||
|
||||
const header = computed(() => {
|
||||
return defs.value && defs.value.header ? defs.value.header : {}
|
||||
})
|
||||
|
||||
const on_logo_app = () => {
|
||||
useState().bcPath.value = ''
|
||||
useState().dfltNavTitle()
|
||||
useState().isSidebarOpen.value = false
|
||||
if (useState().app_home_click.value) {
|
||||
useState().app_home_click.value()
|
||||
}
|
||||
router.push('/')
|
||||
}
|
||||
|
||||
</script>
|
7
src/layouts/SimpleLayout.vue
Normal file
7
src/layouts/SimpleLayout.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div class="antialiased text-gray-900 bg-white">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
Loading…
Reference in New Issue
Block a user