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:
@@ -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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user