Extrahiere Sidebar-Routen-Logik in Composable und entferne toten Counter-Store
Layout.vue trug die Sidebar-Filterlogik (Visibility/Rolle/Pfadabgleich) inline. Sie liegt jetzt in composables/useSidebarRoutes.ts; Layout.vue verliert ~85 Zeilen Skript ohne Verhaltensänderung. Der ungenutzte stores/counter.ts (Vite-Boilerplate) wird entfernt.
This commit is contained in:
+3
-87
@@ -5,12 +5,11 @@ import { useDisplay, useTheme } from 'vuetify'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import iconImage from '@/assets/images/icon.svg'
|
||||
import { Visibility, routes } from '@/plugins/routesLayout'
|
||||
import { useSidebarRoutes } from '@/composables/useSidebarRoutes'
|
||||
import { routes } from '@/plugins/routesLayout'
|
||||
import {
|
||||
AuthRequestError,
|
||||
ROLE_ADMIN,
|
||||
fetchCurrentUser,
|
||||
hasRole,
|
||||
logout,
|
||||
type CurrentUser,
|
||||
} from '@/services/authSession'
|
||||
@@ -28,90 +27,7 @@ const isLoggingOut = ref(false)
|
||||
const appBannersStore = useAppBannersStore()
|
||||
const { banners } = storeToRefs(appBannersStore)
|
||||
|
||||
function normalizeRoutePath(path: string) {
|
||||
if (!path || path === '/') {
|
||||
return '/'
|
||||
}
|
||||
|
||||
return path.endsWith('/') ? path.slice(0, -1) : path
|
||||
}
|
||||
|
||||
function isWithinRoutePath(currentPath: string, targetPath: string) {
|
||||
const normalizedCurrentPath = normalizeRoutePath(currentPath)
|
||||
const normalizedTargetPath = normalizeRoutePath(targetPath)
|
||||
|
||||
if (normalizedTargetPath === '/') {
|
||||
return normalizedCurrentPath === '/'
|
||||
}
|
||||
|
||||
return (
|
||||
normalizedCurrentPath === normalizedTargetPath ||
|
||||
normalizedCurrentPath.startsWith(`${normalizedTargetPath}/`)
|
||||
)
|
||||
}
|
||||
|
||||
function resolveVisibilityRoutes(path: string, visibilityRoute?: string | string[]) {
|
||||
if (Array.isArray(visibilityRoute)) {
|
||||
return visibilityRoute.map((entry) => entry.trim()).filter((entry) => entry.length > 0)
|
||||
}
|
||||
|
||||
const normalizedVisibilityRoute = visibilityRoute?.trim()
|
||||
if (normalizedVisibilityRoute && normalizedVisibilityRoute.length > 0) {
|
||||
return [normalizedVisibilityRoute]
|
||||
}
|
||||
|
||||
return [path]
|
||||
}
|
||||
|
||||
const sidebarRoutes = computed(() =>
|
||||
routes.filter((item) => {
|
||||
if (item.visible === Visibility.Public) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (item.visible === Visibility.Authenticated) {
|
||||
return currentUser.value !== null
|
||||
}
|
||||
|
||||
if (item.visible === Visibility.Unauthenticated) {
|
||||
return currentUser.value === null
|
||||
}
|
||||
|
||||
if (item.visible === Visibility.Authorized) {
|
||||
if (!currentUser.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!item.requiredRoles || item.requiredRoles.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return item.requiredRoles.every((role) => hasRole(currentUser.value, role))
|
||||
}
|
||||
|
||||
if (item.visible !== Visibility.Route) {
|
||||
return false
|
||||
}
|
||||
|
||||
return resolveVisibilityRoutes(item.path, item.visibilityRoute).some((targetPath) =>
|
||||
isWithinRoutePath(route.path, targetPath),
|
||||
)
|
||||
}),
|
||||
)
|
||||
const adminSidebarRoutes = computed(() =>
|
||||
sidebarRoutes.value.filter(
|
||||
(item) =>
|
||||
item.visible === Visibility.Authorized &&
|
||||
Array.isArray(item.requiredRoles) &&
|
||||
item.requiredRoles.some((role) => role.trim().toLowerCase() === ROLE_ADMIN),
|
||||
),
|
||||
)
|
||||
const primarySidebarRoutes = computed(() =>
|
||||
sidebarRoutes.value.filter(
|
||||
(item) => !adminSidebarRoutes.value.some((adminItem) => adminItem.path === item.path),
|
||||
),
|
||||
)
|
||||
const footerRoutes = computed(() => routes.filter((x) => x.visible === Visibility.Footer))
|
||||
const { adminSidebarRoutes, primarySidebarRoutes, footerRoutes } = useSidebarRoutes(currentUser)
|
||||
|
||||
const activeRoute = computed(() => {
|
||||
const byName = routes.find((x) => x.meta?.name === route.name)
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { computed, type Ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
import { Visibility, routes, type LayoutRoute } from '@/plugins/routesLayout'
|
||||
import { ROLE_ADMIN, hasRole, type CurrentUser } from '@/services/authSession'
|
||||
|
||||
function normalizeRoutePath(path: string) {
|
||||
if (!path || path === '/') {
|
||||
return '/'
|
||||
}
|
||||
|
||||
return path.endsWith('/') ? path.slice(0, -1) : path
|
||||
}
|
||||
|
||||
function isWithinRoutePath(currentPath: string, targetPath: string) {
|
||||
const normalizedCurrent = normalizeRoutePath(currentPath)
|
||||
const normalizedTarget = normalizeRoutePath(targetPath)
|
||||
|
||||
if (normalizedTarget === '/') {
|
||||
return normalizedCurrent === '/'
|
||||
}
|
||||
|
||||
return (
|
||||
normalizedCurrent === normalizedTarget ||
|
||||
normalizedCurrent.startsWith(`${normalizedTarget}/`)
|
||||
)
|
||||
}
|
||||
|
||||
function resolveVisibilityRoutes(path: string, visibilityRoute?: string | string[]) {
|
||||
if (Array.isArray(visibilityRoute)) {
|
||||
return visibilityRoute.map((entry) => entry.trim()).filter((entry) => entry.length > 0)
|
||||
}
|
||||
|
||||
const normalized = visibilityRoute?.trim()
|
||||
if (normalized && normalized.length > 0) {
|
||||
return [normalized]
|
||||
}
|
||||
|
||||
return [path]
|
||||
}
|
||||
|
||||
function isVisible(item: LayoutRoute, currentPath: string, user: CurrentUser | null) {
|
||||
switch (item.visible) {
|
||||
case Visibility.Public:
|
||||
return true
|
||||
case Visibility.Authenticated:
|
||||
return user !== null
|
||||
case Visibility.Unauthenticated:
|
||||
return user === null
|
||||
case Visibility.Authorized:
|
||||
if (!user) {
|
||||
return false
|
||||
}
|
||||
if (!item.requiredRoles || item.requiredRoles.length === 0) {
|
||||
return true
|
||||
}
|
||||
return item.requiredRoles.every((role) => hasRole(user, role))
|
||||
case Visibility.Route:
|
||||
return resolveVisibilityRoutes(item.path, item.visibilityRoute).some((target) =>
|
||||
isWithinRoutePath(currentPath, target),
|
||||
)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function isAdminRoute(item: LayoutRoute) {
|
||||
return (
|
||||
item.visible === Visibility.Authorized &&
|
||||
Array.isArray(item.requiredRoles) &&
|
||||
item.requiredRoles.some((role) => role.trim().toLowerCase() === ROLE_ADMIN)
|
||||
)
|
||||
}
|
||||
|
||||
export function useSidebarRoutes(currentUser: Ref<CurrentUser | null>) {
|
||||
const route = useRoute()
|
||||
|
||||
const sidebarRoutes = computed(() =>
|
||||
routes.filter((item) => isVisible(item, route.path, currentUser.value)),
|
||||
)
|
||||
|
||||
const adminSidebarRoutes = computed(() => sidebarRoutes.value.filter(isAdminRoute))
|
||||
|
||||
const primarySidebarRoutes = computed(() =>
|
||||
sidebarRoutes.value.filter((item) => !isAdminRoute(item)),
|
||||
)
|
||||
|
||||
const footerRoutes = computed(() =>
|
||||
routes.filter((item) => item.visible === Visibility.Footer),
|
||||
)
|
||||
|
||||
return {
|
||||
sidebarRoutes,
|
||||
adminSidebarRoutes,
|
||||
primarySidebarRoutes,
|
||||
footerRoutes,
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment }
|
||||
})
|
||||
Reference in New Issue
Block a user