import { chromium } from '@playwright/test' /** * 复现「表格高度 <= 行高之和 时底部重叠」。 * 做法:保持 3 行数据不变,把容器高度从大到小扫一遍, * 记录 is-body-fill 标志、最后一行是否画线、以及外框内底边与最后一行底边的关系。 */ const BASE = process.env.PROBE_URL || 'http://localhost:5200' const browser = await chromium.launch() const page = await browser.newPage({ viewport: { width: 1400, height: 1200 } }) await page.goto(BASE, { waitUntil: 'networkidle' }) const heights = [400, 300, 200, 160, 140, 128, 126, 124, 120, 100, 80, 60, 40] const rows = [] for (const h of heights) { await page.evaluate((hh) => { const wrap = document.querySelector('[data-testid="table-wrap"]') wrap.style.height = `${hh}px` }, h) await page.waitForTimeout(450) // 等 resize debounce(150ms) + rAF + nextTick const r = await page.evaluate(() => { const wrap = document.querySelector('[data-testid="table-wrap"]') const root = wrap.querySelector('.g3-table') const inner = wrap.querySelector('.stk-table') const cs = getComputedStyle(inner) const rrc = root.getBoundingClientRect() const tbody = wrap.querySelector('.stk-tbody-main') const rowsEls = [...wrap.querySelectorAll('.stk-tbody-main tr')] const thead = wrap.querySelector('thead') const hasBottom = (s) => /linear-gradient\(0deg, rgb/.test(s) const last = rowsEls[rowsEls.length - 1] const available = inner.clientHeight - (parseFloat(cs.borderTopWidth) || 0) - (parseFloat(cs.borderBottomWidth) || 0) const headerH = thead ? thead.getBoundingClientRect().height : 0 const bodyH = rowsEls.reduce((a, tr) => a + tr.getBoundingClientRect().height, 0) return { wrapH: Math.round(wrap.getBoundingClientRect().height), frameBottom: Math.round(rrc.bottom), frameInnerBottom: Math.round(rrc.bottom) - 1, available: Math.round(available), headerH: Math.round(headerH), bodyH: Math.round(bodyH), used: Math.round(headerH + bodyH), isBodyFill: root.classList.contains('is-body-fill'), // 最后一行底边 vs 外框内底边 lastRowBottom: last ? Math.round(last.getBoundingClientRect().bottom) : null, // 最后一行是否自己画底线 lastRowDrawsBottom: last ? [...last.querySelectorAll('td')].some((td) => hasBottom(getComputedStyle(td).backgroundImage)) : null, // 最后一行底边是否与外框内底边重合(重合且自己还在画线 = 双线) lastRowTouchesFrame: last ? Math.abs(Math.round(last.getBoundingClientRect().bottom) - (Math.round(rrc.bottom) - 1)) <= 2 : null, tbodyBottom: tbody ? Math.round(tbody.getBoundingClientRect().bottom) : null, rowCount: rowsEls.length, } }) rows.push({ ...r, targetH: h }) } console.log( 'targetH | avail | hdr | body | used | fill | lastRowBottom | frameInner | touches | drawsLine | 判定', ) for (const r of rows) { const double = r.lastRowTouchesFrame && r.lastRowDrawsBottom const verdict = double ? '★双线!' : !r.lastRowDrawsBottom && !r.lastRowTouchesFrame ? '缺线?' : 'ok' console.log( `${String(r.targetH).padStart(6)} | ${String(r.available).padStart(5)} | ${String(r.headerH).padStart(3)} | ${String(r.bodyH).padStart(4)} | ${String(r.used).padStart(4)} | ${String(r.isBodyFill).padStart(5)} | ${String(r.lastRowBottom).padStart(13)} | ${String(r.frameInnerBottom).padStart(10)} | ${String(r.lastRowTouchesFrame).padStart(7)} | ${String(r.lastRowDrawsBottom).padStart(9)} | ${verdict}`, ) } await browser.close()