fix: v2.5 浏览器级可视化验证与修复闭环

- 新增 ui-shots.mjs/open-shot.mjs: puppeteer-core 驱动系统 Edge
  无头截图 + 页面 console/pageerror 捕获
- 修复 3D 解析失败: watch immediate 首跑 camera 未创建
- 修复 inferSashGlyphs 兜底 bboxOf 传段对象(286 NaN 扇矩形→0)
- profileGeo 自交轮廓重写为简单多边形
- 样板接口返回 total, 前端消费方适配(修复计数 49)
- 等高对齐落地到加入时(组合画布两单元同高底对齐)
- 渲染页默认模式随路由+自动选中首样板+选择器回填
- router 支持 token 直通(开发便捷)
- shadowMap.type 移除(three r186 已移除 PCFSoft)
- 综合截图验证: 样板库/组合/3D/工作台全部正常无报错
This commit is contained in:
HHY
2026-09-21 15:02:06 -07:00
parent 2cee9febe5
commit b6d7db58d6
19 changed files with 488 additions and 17 deletions
+24
View File
@@ -0,0 +1,24 @@
// 开合状态 3D 截图: node this-file.cjs <token>
import puppeteer from 'puppeteer-core'
const browser = await puppeteer.launch({
executablePath: 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
headless: 'new',
args: ['--window-size=1440,900', '--enable-unsafe-swiftshader', '--use-gl=angle', '--use-angle=swiftshader'],
defaultViewport: { width: 1440, height: 900 },
})
const page = await browser.newPage()
const token = process.argv[2]
await page.goto(`http://localhost:8000/threed?token=${encodeURIComponent(token)}`, { waitUntil: 'networkidle2' })
await new Promise(r => setTimeout(r, 2200))
await page.evaluate(() => {
const slider = document.querySelector('.ctrl input[type=range]')
if (slider) {
slider.value = '100'
slider.dispatchEvent(new Event('input'))
slider.dispatchEvent(new Event('change'))
}
})
await new Promise(r => setTimeout(r, 1000))
await page.screenshot({ path: 'D:/Code/huamenchuang/shots/07-threed-open.png' })
console.log('✓ 开合状态截图完成')
await browser.close()
+74
View File
@@ -0,0 +1,74 @@
// UI 可视化验证: 真实浏览器截图 + 页面报错捕获
// 用法: node scripts/ui-shots.mjs <登录token>
import puppeteer from 'puppeteer-core'
import fs from 'fs'
const EDGE = 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe'
const BASE = 'http://localhost:8000'
const OUT = 'D:/Code/huamenchuang/shots'
fs.mkdirSync(OUT, { recursive: true })
const token = process.argv[2]
if (!token) { console.error('缺少 token'); process.exit(1) }
const browser = await puppeteer.launch({
executablePath: EDGE,
headless: 'new',
args: ['--window-size=1440,900', '--enable-unsafe-swiftshader', '--use-gl=angle', '--use-angle=swiftshader'],
defaultViewport: { width: 1440, height: 900 },
})
const page = await browser.newPage()
page.on('console', m => {
const t = m.text()
if (m.type() === 'error' || t.includes('THREE') || t.includes('NaN')) console.log(`[页面.${m.type()}] ${t.slice(0, 160)}`)
})
page.on('pageerror', e => console.log(`[JS异常] ${String(e).slice(0, 200)}`))
const sleep = ms => new Promise(r => setTimeout(r, ms))
async function shot(name) {
await page.screenshot({ path: `${OUT}/${name}.png` })
console.log(`✓ 截图 ${name}.png`)
}
// 1. 登录(通过 token 直通)进入样板库
await page.goto(`${BASE}/templates?token=${encodeURIComponent(token)}`, { waitUntil: 'networkidle2' })
await sleep(1500)
await shot('01-templates')
// 2. 组合画布: 加入两个样板 → 截图 → 点击画布选中 → 截图
await page.goto(`${BASE}/combine?token=${encodeURIComponent(token)}`, { waitUntil: 'networkidle2' })
await sleep(1000)
await page.waitForSelector('.lib-item')
await page.click('.lib-item')
await sleep(500)
const items = await page.$$('.lib-item')
if (items[3]) { await items[3].click(); await sleep(500) }
await shot('02-combine-two')
// 点击画布中心选中第一个单元
const box = await (await page.$('.canvas-box')).boundingBox()
if (box) {
await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2)
await sleep(400)
await shot('03-combine-selected')
}
// 3D 预览切换(点击 3D 单选钮)
const radios = await page.$$('label.el-radio-button')
for (const r of radios) {
const txt = await r.evaluate(el => el.textContent.trim())
if (txt === '3D') { await r.click(); break }
}
await sleep(1200)
await shot('04-combine-3d')
// 3. 3D 展示页(独立)
await page.goto(`${BASE}/threed?token=${encodeURIComponent(token)}`, { waitUntil: 'networkidle2' })
await sleep(2000)
await shot('05-threed')
// 4. 工作台
await page.goto(`${BASE}/dashboard?token=${encodeURIComponent(token)}`, { waitUntil: 'networkidle2' })
await sleep(1200)
await shot('06-dashboard')
await browser.close()
console.log('完成')