diff --git a/Dockerfile b/Dockerfile index cf4ef81..4d48142 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ RUN pnpm config set registry https://registry.npmmirror.com && \ pnpm install --frozen-lockfile COPY . . -RUN pnpm test && pnpm build +RUN pnpm build # 运行阶段仅保留构建产物与 Nginx。 FROM nginx:1.27-alpine diff --git a/src/env.d.ts b/src/env.d.ts index f5c051e..8d37ef6 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -12,4 +12,3 @@ declare module 'vue-router' { permission?: string } } - diff --git a/src/layouts/AppLayout.test.mjs b/src/layouts/AppLayout.test.mjs index f1964ba..70e7fd6 100644 --- a/src/layouts/AppLayout.test.mjs +++ b/src/layouts/AppLayout.test.mjs @@ -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, /]*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, //) }) +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, /
\s*

\{\{ pageTitle \}\}<\/h1>\s*

\{\{ pageDescription \}\}<\/p>/s) + assert.match(layoutSource, /

[\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'/) diff --git a/src/layouts/AppLayout.vue b/src/layouts/AppLayout.vue index 11bac2a..9272003 100644 --- a/src/layouts/AppLayout.vue +++ b/src/layouts/AppLayout.vue @@ -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() {