优化
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { defineComponent } from 'vue'
|
||||
import { createMemoryHistory, createRouter } from 'vue-router'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { adminNavigation, filterAdminNavigation, type AdminNavigationNode } from '@/core/navigation/adminNavigation'
|
||||
import AdminSidebarMenu from './AdminSidebarMenu.vue'
|
||||
@@ -19,10 +21,12 @@ const visibleNavigation = filterAdminNavigation(adminNavigation, new Set([
|
||||
]))
|
||||
|
||||
const stubs = {
|
||||
'el-menu': {
|
||||
'el-menu': defineComponent({
|
||||
props: ['defaultActive', 'defaultOpeneds', 'collapse'],
|
||||
template: '<nav :data-active="defaultActive" :data-openeds="JSON.stringify(defaultOpeneds)" :data-collapsed="String(collapse)"><slot /></nav>',
|
||||
},
|
||||
emits: ['select'],
|
||||
methods: { updateActiveIndex() {} },
|
||||
template: '<nav :data-active="defaultActive" :data-openeds="JSON.stringify(defaultOpeneds)" :data-collapsed="String(collapse)" @click="$emit(\'select\', \'/warehouses\')"><slot /></nav>',
|
||||
}),
|
||||
'el-sub-menu': {
|
||||
props: ['index'],
|
||||
template: '<section data-kind="group" :data-index="index"><header><slot name="title" /></header><slot /></section>',
|
||||
@@ -34,10 +38,20 @@ const stubs = {
|
||||
}
|
||||
|
||||
describe('AdminSidebarMenu', () => {
|
||||
function routerPlugin() {
|
||||
return createRouter({
|
||||
history: createMemoryHistory(),
|
||||
routes: [
|
||||
{ path: '/', component: { template: '<p />' } },
|
||||
{ path: '/warehouses', component: { template: '<p />' } },
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
it('renders the visible phase-one modules and forwards the active route', () => {
|
||||
const wrapper = mount(AdminSidebarMenu, {
|
||||
props: { items: visibleNavigation, activePath: '/system/users', collapsed: false },
|
||||
global: { stubs },
|
||||
global: { stubs, plugins: [routerPlugin()] },
|
||||
})
|
||||
|
||||
expect(wrapper.get('nav').attributes('data-active')).toBe('/system/users')
|
||||
@@ -62,7 +76,7 @@ describe('AdminSidebarMenu', () => {
|
||||
it('forwards desktop collapse state to Element Plus', () => {
|
||||
const wrapper = mount(AdminSidebarMenu, {
|
||||
props: { items: visibleNavigation, activePath: '/', collapsed: true },
|
||||
global: { stubs },
|
||||
global: { stubs, plugins: [routerPlugin()] },
|
||||
})
|
||||
|
||||
expect(wrapper.get('nav').attributes('data-collapsed')).toBe('true')
|
||||
@@ -72,7 +86,7 @@ describe('AdminSidebarMenu', () => {
|
||||
const items = filterAdminNavigation(adminNavigation, new Set(['platform:permission:list']))
|
||||
const wrapper = mount(AdminSidebarMenu, {
|
||||
props: { items, activePath: '/', collapsed: false },
|
||||
global: { stubs },
|
||||
global: { stubs, plugins: [routerPlugin()] },
|
||||
})
|
||||
|
||||
expect(wrapper.text()).toContain('菜单权限')
|
||||
@@ -100,7 +114,7 @@ describe('AdminSidebarMenu', () => {
|
||||
}]
|
||||
const wrapper = mount(AdminSidebarMenu, {
|
||||
props: { items, activePath: '/tracking', collapsed: false },
|
||||
global: { stubs },
|
||||
global: { stubs, plugins: [routerPlugin()] },
|
||||
})
|
||||
|
||||
expect(wrapper.findAll('[data-kind="group"]')).toHaveLength(2)
|
||||
@@ -110,7 +124,7 @@ describe('AdminSidebarMenu', () => {
|
||||
it('recreates the menu when asynchronously loaded groups change', async () => {
|
||||
const wrapper = mount(AdminSidebarMenu, {
|
||||
props: { items: [adminNavigation[0]], activePath: '/dashboard', collapsed: false },
|
||||
global: { stubs },
|
||||
global: { stubs, plugins: [routerPlugin()] },
|
||||
})
|
||||
const initialMenu = wrapper.get('nav').element
|
||||
|
||||
@@ -119,4 +133,17 @@ describe('AdminSidebarMenu', () => {
|
||||
expect(wrapper.get('nav').element).not.toBe(initialMenu)
|
||||
expect(wrapper.text()).toContain('系统管理')
|
||||
})
|
||||
|
||||
it('navigates explicitly when a leaf menu item is selected', async () => {
|
||||
const router = routerPlugin()
|
||||
await router.push('/')
|
||||
const wrapper = mount(AdminSidebarMenu, {
|
||||
props: { items: visibleNavigation, activePath: '/', collapsed: false },
|
||||
global: { stubs, plugins: [router] },
|
||||
})
|
||||
|
||||
await wrapper.get('nav').trigger('click')
|
||||
await flushPromises()
|
||||
expect(router.currentRoute.value.path).toBe('/warehouses')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { useRouter, type NavigationFailure } from 'vue-router'
|
||||
import type { MenuInstance } from 'element-plus'
|
||||
import type { AdminNavigationNode } from '@/core/navigation/adminNavigation'
|
||||
import AdminSidebarMenuNode from './AdminSidebarMenuNode.vue'
|
||||
|
||||
@@ -8,6 +10,8 @@ const props = defineProps<{
|
||||
activePath: string
|
||||
collapsed: boolean
|
||||
}>()
|
||||
const router = useRouter()
|
||||
const menuRef = ref<MenuInstance>()
|
||||
|
||||
function getGroupKeys(nodes: AdminNavigationNode[]): string[] {
|
||||
return nodes.flatMap((node) => node.children?.length
|
||||
@@ -18,16 +22,36 @@ function getGroupKeys(nodes: AdminNavigationNode[]): string[] {
|
||||
const groupKeys = computed(() => getGroupKeys(props.items))
|
||||
const defaultOpeneds: string[] = []
|
||||
const menuInstanceKey = computed(() => `${props.collapsed ? 'collapsed' : 'expanded'}:${groupKeys.value.join('|')}`)
|
||||
|
||||
async function restoreActivePath() {
|
||||
await nextTick()
|
||||
menuRef.value?.updateActiveIndex(props.activePath)
|
||||
}
|
||||
|
||||
async function navigate(path: string) {
|
||||
if (!path.startsWith('/') || path === props.activePath) return
|
||||
let failure: NavigationFailure | void
|
||||
try {
|
||||
failure = await router.push(path)
|
||||
} catch {
|
||||
await restoreActivePath()
|
||||
return
|
||||
}
|
||||
if (failure) await restoreActivePath()
|
||||
}
|
||||
|
||||
watch(() => props.activePath, restoreActivePath)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-menu
|
||||
ref="menuRef"
|
||||
:key="menuInstanceKey"
|
||||
class="side-nav-menu"
|
||||
:default-active="activePath"
|
||||
:collapse="collapsed"
|
||||
:default-openeds="defaultOpeneds"
|
||||
router
|
||||
@select="navigate"
|
||||
>
|
||||
<AdminSidebarMenuNode v-for="item in items" :key="item.key" :node="item" :level="0" />
|
||||
</el-menu>
|
||||
|
||||
Reference in New Issue
Block a user