Files
Common_Platform/web/src/components/AppMenu.vue
T
2026-04-28 16:27:16 +08:00

97 lines
2.2 KiB
Vue

<template>
<n-menu
:collapsed="authStore.layoutCollapsed"
:collapsed-width="72"
:collapsed-icon-size="18"
:options="menuOptions"
:value="activePath"
:indent="18"
@update:value="handleSelect"
/>
</template>
<script setup lang="ts">
import { computed, h, type Component } from 'vue'
import { NIcon, type MenuOption } from 'naive-ui'
import * as LucideIcons from 'lucide-vue-next'
import { useRoute, useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import type { MenuNode } from '@/types/auth'
const route = useRoute()
const router = useRouter()
const authStore = useAuthStore()
function renderIcon(icon?: string | null) {
if (!icon) return undefined
const IconComp = LucideIcons[icon as keyof typeof LucideIcons] as unknown as Component | undefined
if (!IconComp) return undefined
return () => h(NIcon, null, { default: () => h(IconComp) })
}
function toOption(menu: MenuNode): MenuOption | null {
if (!menu.visible) return null
if (menu.type === 'BUTTON') return null
const children = (menu.children ?? [])
.map((item) => toOption(item))
.filter((item): item is MenuOption => Boolean(item))
const key = menu.path || `catalog-${menu.id}`
return {
key,
label: menu.title,
icon: renderIcon(menu.icon),
children: children.length > 0 ? children : undefined
}
}
const menuOptions = computed(() =>
authStore.menus.map((item) => toOption(item)).filter((item): item is MenuOption => Boolean(item))
)
const activePath = computed(() => route.path)
function handleSelect(key: string) {
if (key.startsWith('catalog-')) return
router.push(key)
}
</script>
<style scoped>
:deep(.n-menu) {
overflow-x: hidden;
font-size: 13px;
}
:deep(.n-menu-item) {
margin: 2px 0;
}
:deep(.n-menu-item-content) {
color: #64748b;
transition:
background 0.18s ease,
color 0.18s ease;
}
:deep(.n-menu-item-content-header),
:deep(.n-menu-item-content__arrow) {
min-width: 0;
}
:deep(.n-menu-item-content-header) {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
:deep(.n-menu-item-content--selected::before) {
background: #eff6ff;
}
:deep(.n-menu-item-content--selected) {
font-weight: 600;
}
</style>