优化
This commit is contained in:
@@ -3,13 +3,15 @@ import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
const layoutSource = readFileSync(new URL('./AppLayout.vue', import.meta.url), 'utf8')
|
||||
const shellStyles = readFileSync(new URL('../styles/shell.css', import.meta.url), 'utf8')
|
||||
const globalStyles = readFileSync(new URL('../styles/index.css', import.meta.url), 'utf8')
|
||||
const routerSource = readFileSync(new URL('../router/index.ts', import.meta.url), 'utf8')
|
||||
const sidebarMenuSource = readFileSync(new URL('../components/ClientSidebarMenu.vue', import.meta.url), 'utf8')
|
||||
const sidebarMenuNodeSource = readFileSync(new URL('../components/ClientSidebarMenuNode.vue', import.meta.url), 'utf8')
|
||||
|
||||
test('source contract: renders the grouped sidebar menu and mobile drawer controls', () => {
|
||||
assert.match(layoutSource, /import \{ computed, nextTick, onBeforeUnmount, onMounted, ref, watch \} from 'vue'/)
|
||||
assert.match(layoutSource, /import \{[\s\S]*?\bX,[\s\S]*?\} from '@lucide\/vue'/)
|
||||
assert.match(layoutSource, /import \{[\s\S]*?\bX\s*\} from '@lucide\/vue'/)
|
||||
assert.match(layoutSource, /import ClientSidebarMenu from '@\/components\/ClientSidebarMenu\.vue'/)
|
||||
assert.match(layoutSource, /:class="\{ 'mobile-menu-open': mobileMenuOpen \}"/)
|
||||
assert.match(layoutSource, /<button\s+v-if="mobileMenuOpen"[^>]*class="client-sidebar-scrim"[^>]*aria-label="关闭导航"[^>]*@click="closeMobileMenu"/s)
|
||||
@@ -42,6 +44,24 @@ test('source contract: removes the header and main content from the accessibilit
|
||||
assert.match(layoutSource, /<main\s+class="client-main"\s+:inert="mobileMenuOpen"\s+:aria-hidden="mobileMenuOpen \? 'true' : undefined"\s*>/)
|
||||
})
|
||||
|
||||
test('source contract: keeps route context and user actions in the compact header without tenant identity', () => {
|
||||
assert.match(layoutSource, /const pageTitle = computed\(\(\) => route\.meta\.title \|\| '业务总览'\)/)
|
||||
assert.match(layoutSource, /const pageDescription = computed\(\(\) => route\.meta\.description \|\| ''\)/)
|
||||
assert.match(layoutSource, /<div class="client-header-heading">\s*<h1>\{\{ pageTitle \}\}<\/h1>\s*<p v-if="pageDescription">\{\{ pageDescription \}\}<\/p>/s)
|
||||
assert.match(layoutSource, /<div class="client-user">[\s\S]*\{\{ auth\.user\?\.name \}\}[\s\S]*aria-label="退出登录"/)
|
||||
assert.doesNotMatch(layoutSource, /Building2|tenant-name|tenantName/)
|
||||
assert.doesNotMatch(routerSource, /showPageHeading|showTenantName/)
|
||||
})
|
||||
|
||||
test('source contract: uses compact desktop padding and a bounded mobile header', () => {
|
||||
assert.match(shellStyles, /\.top-header \{[^}]*padding: 0 20px;/)
|
||||
assert.match(shellStyles, /\.client-main \{[^}]*padding: 88px 20px 40px;/)
|
||||
assert.match(globalStyles, /@media \(max-width: 720px\) \{[\s\S]*\.top-header \{ padding: 0 14px; \}/)
|
||||
assert.match(globalStyles, /\.client-brand \{ display: none; \}/)
|
||||
assert.match(globalStyles, /\.client-header-menu-button \{ display: grid; \}/)
|
||||
assert.match(globalStyles, /\.client-header-heading p, \.client-user-copy \{ display: none; \}/)
|
||||
})
|
||||
|
||||
test('source contract: closes the mobile menu on route changes and Escape', () => {
|
||||
assert.match(layoutSource, /watch\(\(\) => route\.fullPath, closeMobileMenu\)/)
|
||||
assert.match(layoutSource, /event\.key === 'Escape'/)
|
||||
|
||||
+17
-25
@@ -2,12 +2,7 @@
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
Building2,
|
||||
LogOut,
|
||||
Menu,
|
||||
X,
|
||||
} from '@lucide/vue'
|
||||
import { LogOut, Menu, X } from '@lucide/vue'
|
||||
import baiyueLogoDark from '@/assets/brand/baiyue-logo-dark.png'
|
||||
import ClientSidebarMenu from '@/components/ClientSidebarMenu.vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
@@ -100,15 +95,25 @@ async function handleLogout() {
|
||||
|
||||
<template>
|
||||
<div class="client-shell" :class="{ 'mobile-menu-open': mobileMenuOpen }">
|
||||
<header
|
||||
class="top-header"
|
||||
:inert="mobileMenuOpen"
|
||||
:aria-hidden="mobileMenuOpen ? 'true' : undefined"
|
||||
>
|
||||
<header class="top-header" :inert="mobileMenuOpen" :aria-hidden="mobileMenuOpen ? 'true' : undefined">
|
||||
<RouterLink class="client-brand" to="/">
|
||||
<img class="client-brand-logo" :src="baiyueLogoDark" alt="柏悦物流" />
|
||||
</RouterLink>
|
||||
<div class="tenant-name"><Building2 :size="17" /> {{ auth.user?.tenantName }}</div>
|
||||
<button
|
||||
ref="menuButtonRef"
|
||||
type="button"
|
||||
class="client-header-menu-button"
|
||||
aria-label="打开导航"
|
||||
aria-controls="client-sidebar"
|
||||
:aria-expanded="mobileMenuOpen"
|
||||
@click="openMobileMenu"
|
||||
>
|
||||
<Menu :size="20" />
|
||||
</button>
|
||||
<div class="client-header-heading">
|
||||
<h1>{{ pageTitle }}</h1>
|
||||
<p v-if="pageDescription">{{ pageDescription }}</p>
|
||||
</div>
|
||||
<div class="client-user">
|
||||
<span class="client-avatar">{{ auth.user?.name.slice(0, 1) }}</span>
|
||||
<span class="client-user-copy"><strong>{{ auth.user?.name }}</strong><small>{{ auth.user?.roles.join('、') }}</small></span>
|
||||
@@ -156,19 +161,6 @@ async function handleLogout() {
|
||||
:inert="mobileMenuOpen"
|
||||
:aria-hidden="mobileMenuOpen ? 'true' : undefined"
|
||||
>
|
||||
<div class="client-page-heading">
|
||||
<button
|
||||
ref="menuButtonRef"
|
||||
type="button"
|
||||
aria-label="打开导航"
|
||||
aria-controls="client-sidebar"
|
||||
:aria-expanded="mobileMenuOpen"
|
||||
@click="openMobileMenu"
|
||||
>
|
||||
<Menu :size="20" />
|
||||
</button>
|
||||
<div><h1>{{ pageTitle }}</h1><p>{{ pageDescription }}</p></div>
|
||||
</div>
|
||||
<RouterView />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user