/** * 诊断:详情卡状态标签 与 一张图顶部状态栏 的 DOM 结构逐一对比(维护者反馈"多出一个废弃设计")。 * 只读探针:打开点位详情 → dump 两侧 outerHTML + 计算样式。 */ import { mkdir, writeFile } from 'node:fs/promises' import path from 'node:path' import { cdpLaunch } from '../../../../cockpit-shell-panel-layout/verify/cdp-driver.mjs' const CHROME = process.env.CHROME_PATH || 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' const sleep = (ms) => new Promise((r) => setTimeout(r, ms)) const POINT = 'CBWS202610150001' const browser = await cdpLaunch({ executablePath: CHROME, headless: true, width: 1680, height: 950 }) const page = await browser.newPage() await page.goto('http://127.0.0.1:8000/#/cockpit', { timeout: 60000 }) await sleep(2500) await page.click('[data-cockpit-intro]').catch(() => {}) await page.waitForFunction(() => ['ready', 'failed', 'disabled'].includes(document.querySelector('.cockpit-shell__map .map-canvas')?.dataset.mapEngine || ''), { timeout: 60000 }) // 报警筛选 + 飞近 + 真鼠标点标记(与走查同一进入路径) await page.click('[data-legend-key="ALARM"]').catch(() => {}) await sleep(4000) const coord = await page.evaluate(async (code) => { const r = await fetch('http://192.168.110.96:8301/api/cockpit/v1/map-snapshot?page=1&pageSize=200&pointStatus=ALARM', { cache: 'no-store' }) const j = await r.json() const found = ((j.data || j).monitoringPoints || []).find((p) => (p.pointCode || p.code) === code) return found ? { lon: found.longitude, lat: found.latitude } : null }, POINT) await page.evaluate((c) => globalThis.__cockpitMap?.setCameraFocus({ lon: c.lon, lat: c.lat, height: 900 }), coord) await sleep(4000) const box = await page.evaluate((code) => { const node = document.querySelector(`[data-marker-overlay-code="${code}"]`) const inner = node?.querySelector('svg') if (!inner) return null const r = inner.getBoundingClientRect() return { cx: r.x + r.width / 2, cy: r.y + r.height / 2 } }, POINT) if (box) { await page._dispatchMouse('mouseMoved', box.cx, box.cy) await page._dispatchMouse('mousePressed', box.cx, box.cy, { button: 'left', buttons: 1, clickCount: 1 }) await page._dispatchMouse('mouseReleased', box.cx, box.cy, { button: 'left', buttons: 0, clickCount: 1 }) } await sleep(3000) const dump = await page.evaluate(() => { const strip = (html) => (html || '').replace(/\s+/g, ' ').slice(0, 900) const topAlarm = document.querySelector('[data-map-legend] [data-legend-key="ALARM"]') const topEmpty = document.querySelector('[data-map-legend] [data-legend-key="EMPTY"]') const cardStatus = document.querySelector('[data-point-status]') const deviceCardTag = document.querySelector('[data-device-card] .status-tag') const shapeOf = (el) => { const svg = el?.querySelector('svg.status-tag__shape') if (!svg) return { shape: false } const r = svg.getBoundingClientRect() return { shape: true, w: Math.round(r.width), h: Math.round(r.height), display: getComputedStyle(svg).display } } const iconOf = (el) => { const icon = el?.querySelector('.map-holo__legend-icon, .status-tag__icon') if (!icon) return { icon: false } const r = icon.getBoundingClientRect() return { icon: true, w: Math.round(r.width), h: Math.round(r.height), cls: icon.className.baseVal !== undefined ? String(icon.getAttribute('class')) : icon.className } } return { topAlarmHTML: strip(topAlarm?.outerHTML), topEmptyHTML: strip(topEmpty?.outerHTML), cardStatusHTML: strip(cardStatus?.outerHTML), deviceCardHTML: strip(deviceCardTag?.outerHTML), topAlarmShape: shapeOf(topAlarm?.querySelector('.status-tag')), topAlarmIcon: iconOf(topAlarm), cardShape: shapeOf(cardStatus), cardIcon: iconOf(cardStatus), } }) console.log(JSON.stringify(dump, null, 2)) // 放大截图:状态卡区域 + 顶部图例区域 const clipDump = await page.evaluate(() => { const pick = (el) => { const r = el.getBoundingClientRect(); return { x: r.x, y: r.y, width: r.width, height: r.height } } return { card: pick(document.querySelector('[data-point-status-card]')), legend: pick(document.querySelector('[data-map-legend]')), } }) const OUT = path.resolve('.scratch/cockpit-real-data-wayfinding/verify/artifacts/t46') await mkdir(OUT, { recursive: true }) for (const [tag, clip] of Object.entries(clipDump)) { const b64 = await page.screenshot({ clip: { ...clip, scale: 3 } }) await writeFile(path.join(OUT, `zoom-${tag}.png`), Buffer.from(b64, 'base64')) } console.log('zoom shots written') await browser.close()