优化
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { defineComponent } from 'vue'
|
||||
import { defineComponent, markRaw } from 'vue'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { createMemoryHistory, createRouter } from 'vue-router'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { ElMenu, ElMenuItem, ElSubMenu } from 'element-plus'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { adminNavigation, filterAdminNavigation, type AdminNavigationNode } from '@/core/navigation/adminNavigation'
|
||||
import AdminSidebarMenu from './AdminSidebarMenu.vue'
|
||||
|
||||
@@ -85,6 +88,72 @@ describe('AdminSidebarMenu', () => {
|
||||
expect(wrapper.get('nav').attributes('data-collapsed')).toBe('true')
|
||||
})
|
||||
|
||||
it('fits collapsed icons inside the scrolling rail and keeps hover submenus outside it', async () => {
|
||||
const host = document.createElement('div')
|
||||
host.className = 'app-shell is-collapsed'
|
||||
host.innerHTML = '<aside class="app-sidebar"><nav class="side-nav"></nav></aside>'
|
||||
document.body.append(host)
|
||||
const style = document.createElement('style')
|
||||
style.textContent = readFileSync(resolve(process.cwd(), 'src/styles/shell.css'), 'utf8')
|
||||
document.head.append(style)
|
||||
const rail = host.querySelector<HTMLElement>('.side-nav')!
|
||||
const router = routerPlugin()
|
||||
await router.push('/')
|
||||
const icon = markRaw(adminNavigation[0].icon)
|
||||
const items: AdminNavigationNode[] = [
|
||||
{ key: 'home', title: '首页', icon, path: '/' },
|
||||
{
|
||||
key: 'resources', title: '资源管理', icon,
|
||||
children: [{ key: 'warehouses', title: '仓库管理', icon, path: '/warehouses' }],
|
||||
},
|
||||
]
|
||||
const wrapper = mount(AdminSidebarMenu, {
|
||||
attachTo: rail,
|
||||
props: { items, activePath: '/', collapsed: true },
|
||||
global: { plugins: [router], components: { ElMenu, ElMenuItem, ElSubMenu }, stubs: { transition: false } },
|
||||
})
|
||||
try {
|
||||
await flushPromises()
|
||||
const menu = wrapper.get('.side-nav-menu.el-menu--collapse')
|
||||
const group = wrapper.get('.side-nav-menu > .el-sub-menu')
|
||||
const leafTrigger = wrapper.get('.el-menu-tooltip__trigger')
|
||||
expect(getComputedStyle(host.querySelector('.app-sidebar')!).width).toBe('76px')
|
||||
expect(getComputedStyle(rail).overflowX).toBe('hidden')
|
||||
expect(getComputedStyle(rail).overflowY).toBe('auto')
|
||||
expect(getComputedStyle(menu.element).width).toBe('100%')
|
||||
for (const trigger of [leafTrigger, group.get('.el-sub-menu__title')]) {
|
||||
expect(getComputedStyle(trigger.element).paddingLeft).toBe('0px')
|
||||
expect(getComputedStyle(trigger.element).paddingRight).toBe('0px')
|
||||
expect(getComputedStyle(trigger.element).justifyContent).toBe('center')
|
||||
expect(getComputedStyle(trigger.get('.menu-icon').element).marginRight).toBe('0px')
|
||||
}
|
||||
|
||||
await group.trigger('mouseenter')
|
||||
await vi.waitFor(() => expect(group.attributes('aria-expanded')).toBe('true'))
|
||||
const popup = document.body.querySelector('.shell-nav-popper .el-menu--popup')
|
||||
expect(popup).not.toBeNull()
|
||||
expect(popup?.closest('.side-nav')).toBeNull()
|
||||
expect(popup?.textContent).toContain('仓库管理')
|
||||
|
||||
const destination = popup!.querySelector<HTMLElement>('.el-menu-item')!
|
||||
destination.focus()
|
||||
expect(document.activeElement).toBe(destination)
|
||||
expect(destination.getAttribute('role')).toBe('menuitem')
|
||||
destination.click()
|
||||
await flushPromises()
|
||||
expect(router.currentRoute.value.path).toBe('/warehouses')
|
||||
|
||||
await wrapper.setProps({ collapsed: false })
|
||||
host.classList.remove('is-collapsed')
|
||||
expect(getComputedStyle(host.querySelector('.app-sidebar')!).width).toBe('240px')
|
||||
expect(wrapper.find('.el-menu--collapse').exists()).toBe(false)
|
||||
} finally {
|
||||
wrapper.unmount()
|
||||
style.remove()
|
||||
host.remove()
|
||||
}
|
||||
})
|
||||
|
||||
it('renders only entries included in the filtered navigation input', () => {
|
||||
const items = filterAdminNavigation(adminNavigation, new Set(['platform:permission:list']))
|
||||
const wrapper = mount(AdminSidebarMenu, {
|
||||
|
||||
@@ -10,7 +10,7 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-sub-menu v-if="node.children?.length" :index="node.key">
|
||||
<el-sub-menu v-if="node.children?.length" :index="node.key" popper-class="admin-workbench shell-nav-popper">
|
||||
<template #title>
|
||||
<component
|
||||
v-if="!level"
|
||||
|
||||
@@ -44,6 +44,17 @@ describe('WorkspaceTabs', () => {
|
||||
})
|
||||
afterEach(() => { document.body.innerHTML = '' })
|
||||
|
||||
it('retains its own theme on the teleported management menu', async () => {
|
||||
const { wrapper } = await mountTabs()
|
||||
await wrapper.get('[aria-label="页签管理"]').trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
const popper = document.body.querySelector('.shell-header-popper')
|
||||
expect(popper?.classList.contains('admin-workbench')).toBe(true)
|
||||
expect(popper?.closest('.workspace-tabs')).toBeNull()
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('keeps a dirty tab open when discard confirmation is cancelled', async () => {
|
||||
const { wrapper, workspace, unsaved } = await mountTabs()
|
||||
unsaved.upsert({ key: 'customer-form', label: '客户资料', dirty: true, requiredPermission: null, workspaceTabId: 'customers' })
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import '@/styles/adminDensity.css'
|
||||
import '@/styles/adminWorkbench.css'
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
@@ -125,7 +126,7 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="workspace-tabs" aria-label="页面工作区">
|
||||
<section class="workspace-tabs admin-workbench" aria-label="页面工作区">
|
||||
<div ref="tabListRef" class="workspace-tab-list" role="tablist" aria-label="已打开页面" @wheel="handleTabWheel">
|
||||
<div
|
||||
v-for="(tab, index) in workspace.tabs"
|
||||
@@ -156,7 +157,7 @@ watch(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dropdown trigger="click" placement="bottom-end" @command="handleCommand">
|
||||
<el-dropdown trigger="click" placement="bottom-end" popper-class="admin-workbench shell-header-popper" @command="handleCommand">
|
||||
<el-button class="workspace-tab-more" text aria-label="页签管理" title="页签管理">
|
||||
<MoreHorizontal :size="18" aria-hidden="true" />
|
||||
</el-button>
|
||||
@@ -181,9 +182,8 @@ watch(
|
||||
min-width: 0;
|
||||
height: 44px;
|
||||
align-items: stretch;
|
||||
border-bottom: 1px solid var(--cc-border);
|
||||
background: #f7f9fc;
|
||||
box-shadow: 0 5px 14px rgba(23, 42, 70, .045);
|
||||
border-bottom: 1px solid var(--workbench-border);
|
||||
background: var(--workbench-surface);
|
||||
}
|
||||
.workspace-tab-list {
|
||||
display: flex;
|
||||
@@ -208,10 +208,10 @@ watch(
|
||||
border: 1px solid transparent;
|
||||
border-bottom: 0;
|
||||
border-radius: 8px 8px 0 0;
|
||||
color: #526178;
|
||||
color: var(--workbench-muted);
|
||||
}
|
||||
.workspace-tab:hover { background: #eef3fa; color: #24344d; }
|
||||
.workspace-tab.is-active { border-color: var(--cc-border); background: #fff; color: #172033; }
|
||||
.workspace-tab:hover { background: var(--workbench-surface-soft); color: var(--workbench-text); }
|
||||
.workspace-tab.is-active { border-color: var(--workbench-accent-border); background: var(--workbench-accent-soft); color: var(--workbench-primary); }
|
||||
.workspace-tab-activate {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
@@ -239,19 +239,19 @@ watch(
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: #7b8799;
|
||||
color: var(--workbench-muted);
|
||||
cursor: pointer;
|
||||
}
|
||||
.workspace-tab-close:hover { background: #dde6f2; color: #26364d; }
|
||||
.workspace-tab-close:hover { background: var(--workbench-accent-border); color: var(--workbench-primary); }
|
||||
.workspace-tab-activate:focus-visible,
|
||||
.workspace-tab-close:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: -2px; }
|
||||
.workspace-tab-more { width: 44px; height: 44px; flex: 0 0 44px; border-radius: 0; border-left: 1px solid var(--cc-border); color: #526178; }
|
||||
.workspace-tab-close:focus-visible { outline: 2px solid var(--workbench-primary); outline-offset: -2px; }
|
||||
.workspace-tab-more { width: 44px; height: 44px; flex: 0 0 44px; border-radius: 0; border-left: 1px solid var(--workbench-border); color: var(--workbench-muted); }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.workspace-tabs { top: 64px; height: 48px; }
|
||||
.workspace-tab-list { align-items: center; padding: 4px 6px; }
|
||||
.workspace-tab { min-width: 140px; height: 40px; border-bottom: 1px solid transparent; border-radius: 8px; }
|
||||
.workspace-tab.is-active { border-color: #cbd7e7; }
|
||||
.workspace-tab.is-active { border-color: var(--workbench-accent-border); }
|
||||
.workspace-tab-close { width: 34px; height: 34px; flex-basis: 34px; }
|
||||
.workspace-tab-more { height: 48px; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user