This commit is contained in:
oneao committed 2026-10-10 17:33:59 +08:00
1 parent 5f28a10d89
commit 32d423b759
59 files changed
+5101 -1098

No files matched your search

@@ -0,0 +1,135 @@
import { expect, test } from '@playwright/test'
/**
* 双列菜单布局的头部区域回归 —— 针对 ERP 应用。
*
* 结构对照 vben(packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue):
* 第一列(layout-mixed-rail,width = mixedWidth):
* · 背景 bg-sidebar-deep(本项目按需求用白色)
* · <slot name="logo"> 在这一列(:410,高度 = headerHeight)
* 第二列(layout-mixed-column,extraWidth):
* · 背景 bg-sidebar
* · <slot name="extra-title"> 在这一列(:446,高度 = extraTitleHeight ?? headerHeight - 1)
*
* ⚠️ 注意区分:vben 的 `isMixedNav` = layout === 'mixed-nav'(顶栏混合),
* 与本布局 `sidebar-mixed-nav` 不是一回事。vben-layout.vue:619 的
* `isSideMode && !isMixedNav && sidebarLogoVisible` 对 sidebar-mixed-nav 成立,
* 所以 logo 渲染在第一列。
*
* 即:logo 在第一列,标题在第二列。
*/
const ERP_URL = process.env.ERP_URL ?? 'http://localhost:5090'
async function useMixedNavLayout(page: import('@playwright/test').Page) {
await page.goto(`${ERP_URL}/`)
await page.evaluate(() => {
const KEY = 'g3erp:preferences'
const raw = localStorage.getItem(KEY)
const pref: Record<string, Record<string, unknown>> = raw ? JSON.parse(raw) : {}
const app = pref.app || {}
app.layout = 'sidebar-mixed-nav'
pref.app = app
const sidebar = pref.sidebar || {}
sidebar.enable = true
sidebar.extraCollapse = false
pref.sidebar = sidebar
const logo = pref.logo || {}
logo.enable = true
logo.showText = true
pref.logo = logo
localStorage.setItem(KEY, JSON.stringify(pref))
for (const key of Object.keys(localStorage)) {
if (key.indexOf('g3erp:app') === 0) localStorage.removeItem(key)
}
})
await page.reload()
await page.waitForTimeout(400)
}
test.describe('ERP 双列菜单 - 头部结构', () => {
test('logo 在第一列(layout-mixed-rail)', async ({ page }) => {
await useMixedNavLayout(page)
await page.goto(`${ERP_URL}/ocean/booking`)
await page.waitForTimeout(400)
const rail = page.locator('.layout-mixed-rail')
await expect(rail).toBeVisible()
// 第一列顶部有 logo 方块
const mark = rail.locator('.layout-mixed-rail__logo-mark')
await expect(mark, '第一列应有 logo 方块').toBeVisible()
await expect(mark).toHaveText('G3')
// 有实际尺寸,不是被压成 0
const box = await mark.boundingBox()
expect(box, 'logo 方块应有实际尺寸').not.toBeNull()
expect(box!.width).toBeGreaterThan(10)
expect(box!.height).toBeGreaterThan(10)
// 第二列不应再出现 logo
await expect(page.locator('.layout-mixed-column__logo')).toHaveCount(0)
// 顶栏也不应出现 logo(双列布局下由第一列承载)
await expect(page.locator('.layout-header__logo')).toHaveCount(0)
})
test('标题行展示的是 logo 文字(不是分组名)', async ({ page }) => {
await useMixedNavLayout(page)
await page.goto(`${ERP_URL}/ocean/booking`)
await page.waitForTimeout(400)
const title = page.locator('.layout-mixed-column__title')
await expect(title, '第二列标题行应可见').toBeVisible()
// 应是 logo 文字(默认 G3Soft ERP),而不是当前分组名「海运」
const text = (await title.innerText()).trim()
expect(text, '标题行应是 logo 文字').not.toBe('海运')
expect(text.length, '标题行应有内容').toBeGreaterThan(0)
// 与单列侧栏/顶栏的 logo 文字取自同一偏好项
const logoText = await page.evaluate(() => {
const raw = localStorage.getItem('g3erp:preferences')
const pref = raw ? JSON.parse(raw) : {}
return pref.logo ? pref.logo.text : null
})
expect(logoText, '应能读到 logo.text 偏好').toBeTruthy()
expect(text, '标题行文本应等于 logo.text').toBe(logoText)
const box = await title.boundingBox()
expect(box).not.toBeNull()
expect(box!.height).toBeGreaterThan(20)
})
test('切换分组不影响标题行(它显示 logo 文字,与分组无关)', async ({ page }) => {
await useMixedNavLayout(page)
await page.goto(`${ERP_URL}/ocean/booking`)
await page.waitForTimeout(400)
const before = (await page.locator('.layout-mixed-column__title').innerText()).trim()
await page.locator('.layout-mixed-rail__item', { hasText: '财务' }).click()
await page.waitForTimeout(500)
const after = (await page.locator('.layout-mixed-column__title').innerText()).trim()
expect(after, '标题行是 logo 文字,切分组不应变化').toBe(before)
// 但列表内容确实换成了财务的子项
const columnText = await page.locator('.layout-mixed-column').innerText()
expect(columnText, '第二列列表应切换为财务子项').toContain('应收费用')
})
test('第一列背景为白色', async ({ page }) => {
await useMixedNavLayout(page)
await page.goto(`${ERP_URL}/ocean/booking`)
await page.waitForTimeout(400)
const rail = page.locator('.layout-mixed-rail')
const bg = await rail.evaluate((el) => getComputedStyle(el).backgroundColor)
expect(bg, `图标栏背景应为白色,实际 ${bg}`).toMatch(/^rgba?\(255,\s*255,\s*255(,\s*1)?\)$/)
})
})