|
@@ -0,0 +1,90 @@
|
|
|
|
|
+/**
|
|
|
|
|
+ * 诊断:详情卡状态标签 与 一张图顶部状态栏 的 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()
|