import { chromium } from '@playwright/test' /** * 方向验证:在滚动容器底部覆盖一条 1px 的同色线(画在格线之上、外框之下), * 把"被裁切行留下的底边线"盖掉,使可视底边恒为一条。 * * 用 ::after 伪元素实现(absolute,画在内容上层)。 */ 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 clipSample = async (x, y, w, h) => { const shot = await page.screenshot({ clip: { x, y, width: w, height: h } }) return page.evaluate(async (dataUrl) => { const img = new Image() await new Promise((res) => { img.onload = res img.src = dataUrl }) const cv = document.createElement('canvas') cv.width = img.width cv.height = img.height const ctx = cv.getContext('2d') ctx.drawImage(img, 0, 0) const d = ctx.getImageData(0, 0, img.width, img.height).data const out = [] for (let i = 0; i < d.length; i += 4) out.push([d[i], d[i + 1], d[i + 2]]) return out }, `data:image/png;base64,${shot.toString('base64')}`) } const applyPatch = () => page.evaluate(() => { const s = document.createElement('style') s.id = 'probe-cover' s.textContent = ` /* 覆盖式收边:在 .stk-table 可视底部画一条与外框同色的线,压住被裁行的底边 */ .g3-table.is-bordered .g3-table__inner.stk-table { position: relative; } .g3-table.is-bordered .g3-table__inner.stk-table::after { content: ''; position: absolute; inset-inline: 0; bottom: 0; height: 1px; background: var(--g3-border-color, #d9d9d9); pointer-events: none; z-index: 5; } ` document.head.appendChild(s) }) const heights = [500, 400, 300, 250, 200, 180, 160, 150, 140, 130, 120, 110, 100] const sweep = async (label) => { const bad = [] for (const h of heights) { await page.evaluate((hh) => { document.querySelector('[data-testid="table-big-wrap"]').style.height = `${hh}px` }, h) await page.waitForTimeout(420) await page.locator('[data-testid="table-big-wrap"]').scrollIntoViewIfNeeded() await page.waitForTimeout(180) const g = await page.evaluate(() => { const wrap = document.querySelector('[data-testid="table-big-wrap"]') const root = wrap.querySelector('.g3-table') const r = root.getBoundingClientRect() return { left: Math.round(r.left), bottom: Math.round(r.bottom) } }) const y = Math.max(0, g.bottom - 4) if (1200 - y < 10) continue const px = await clipSample(g.left + 300, y, 1, 10) const vec = px.map(([r, gg, b]) => (r < 246 || gg < 246 || b < 246 ? 1 : 0)) const n = vec.reduce((a, b) => a + b, 0) if (n !== 1) bad.push(`h=${h}(${n})`) console.log(` ${label} h=${String(h).padStart(3)} ${vec.join('')} (${n}条) ${n === 1 ? '✅' : '❌'}`) } console.log(` → ${label} 失败档位: ${bad.length ? bad.join(', ') : '无 ✅'}\n`) } console.log('===== 修复前(table-big,真实滚动)=====') await sweep('before') console.log('===== 加覆盖式收边 =====') await applyPatch() await sweep('patch ') await browser.close()