import { chromium } from 'playwright' /** * 全站「页脚/侧栏」布局审计 * 检查点: * A) 页脚内容左边界是否与无侧栏页面(首页)一致 —— 不一致说明被侧栏挤了 * B) 页脚可视区域是否被 fixed 层(.VPSidebar 等)遮住(按面积估算) * C) 横向溢出 */ const BASE = process.env.AUDIT_BASE || 'http://127.0.0.1:18075' const PAGES = [ '/', '/demo/', '/about/', '/projects/', '/projects/iotlcm/', '/projects/iotlcm/compare/', '/projects/iotlcm/guide/quickstart/', '/projects/iotlcm/guide/deploy/', '/projects/xiu-dtu/', '/projects/xiu-admin/', '/guide/portal-deploy/', ] const VPS = [1920, 1600, 1440, 1280, 1024, 820, 390] const browser = await chromium.launch({ executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', }) const rows = [] for (const w of VPS) { const page = await browser.newPage({ viewport: { width: w, height: 900 } }) for (const path of PAGES) { await page.goto(BASE + path, { waitUntil: 'domcontentloaded' }) await page.waitForFunction(() => !!document.querySelector('footer.pf-foot'), null, { timeout: 60000 }) await page.waitForTimeout(900) const r = await page.evaluate(() => { const f = document.querySelector('footer.pf-foot') const fr = f.getBoundingClientRect() const wrap = f.querySelector('.p-wrap') const wr = wrap.getBoundingClientRect() const side = document.querySelector('.VPSidebar') let sideR = null if (side) { const s = side.getBoundingClientRect() const cs = getComputedStyle(side) if (s.width > 0 && cs.display !== 'none' && cs.visibility !== 'hidden') sideR = { left: s.left, right: s.right, width: s.width, z: cs.zIndex } } // 页脚与侧栏的横向重叠宽度(仅当侧栏层级高于页脚时才算遮挡) const overlap = sideR ? Math.max(0, Math.min(sideR.right, fr.right) - Math.max(sideR.left, fr.left)) : 0 return { vp: document.documentElement.clientWidth, footW: Math.round(fr.width), wrapLeft: Math.round(wr.left), wrapW: Math.round(wr.width), cls: f.className, sideW: sideR ? Math.round(sideR.width) : 0, overlap: sideR && Number(sideR.z) > Number(getComputedStyle(f).zIndex) ? Math.round(overlap) : 0, footZ: getComputedStyle(f).zIndex, sideZ: sideR?.z ?? '-', hOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth, } }) rows.push({ w, path, ...r }) } await page.close() } // 基线:每个宽度下无侧栏页面的 wrapLeft const baseline = {} for (const r of rows) if (!r.cls.includes('has-sidebar')) baseline[r.w] ??= r.wrapLeft let bad = 0 console.log('w\tpath\twrapLeft\tbaseline\tsideW\toverlap\thOverflow') for (const r of rows) { const base = baseline[r.w] const issues = [] if (base !== undefined && Math.abs(r.wrapLeft - base) > 8) issues.push('页脚内容左偏') if (r.overlap > 0) issues.push(`侧栏覆盖页脚 ${r.overlap}px`) if (r.hOverflow > 2) issues.push(`横向溢出 ${r.hOverflow}px`) if (issues.length) { bad++ console.log(`${r.w}\t${r.path}\t${r.wrapLeft}\t${base}\t${r.sideW}\t${r.overlap}\t${r.hOverflow}\t<< ${issues.join(' / ')}`) } } console.log(`\n共 ${rows.length} 组检查,${bad} 组异常`) await browser.close()