fastbee/yack-mini/tools/detail-layout-check.js
2026-09-27 14:46:05 +08:00

102 lines
6.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 详情页接收行/发送行 视觉回归校验(headless Chrome)
*
* 做法:直接读取真实 detail.wxss(rpx→px 换算) + 按 detail.wxml 结构生成 DOM,
* 并把小程序基础库的 button 默认样式(含 wx-button:not([size=mini]){width:184px})
* 一起注入 → 得到的布局与真机同源,可直接量出「上传离线是否被挤出屏幕」。
*
* 用法:node tools/detail-layout-check.js (生成 /tmp/detail-layout.html)
*/
const fs = require('fs');
const path = require('path');
const BASE = path.join(__dirname, '../miniprogram');
const DEVICE_W = 390; // iPhone 12/13/14 逻辑宽
const RATIO = DEVICE_W / 750; // 1rpx -> px (0.52)
// 1) 真实 wxss:rpx → px(保持其余原样,含选择器与特性权重)
const rawCss = fs.readFileSync(path.join(BASE, 'pages/device/detail/detail.wxss'), 'utf8');
const css = rawCss.replace(/(-?\d*\.?\d+)rpx/g, (_, n) => `${(parseFloat(n) * RATIO).toFixed(3)}px`);
// 2) 基础库 button 默认样式(从 WAWebview.js 原样抄,选择器 wx-button → button 等价替换)
const BASE_CSS = `
button{-webkit-tap-highlight-color:transparent;background-color:#f8f8f8;border-radius:5px;box-sizing:border-box;color:#000;cursor:pointer;display:block;font-size:18px;line-height:2.55555556;overflow:hidden;padding-left:14px;padding-right:14px;position:relative;text-align:center;text-decoration:none;border:none}
button:not([size=mini]){margin-left:auto;margin-right:auto;width:184px}
button::after{content:" ";position:absolute;top:0;left:0;width:200%;height:200%;border:1px solid rgba(0,0,0,.2);border-radius:10px;box-sizing:border-box;transform:scale(.5);transform-origin:0 0}
button[disabled]:not([type]){background-color:#fafafa;color:rgba(0,0,0,.18)}
`;
// 3) 按 detail.wxml 结构写 DOM(含 {{}} 取值)
const DATA = {
device: { serialNumber: 'BT_ECB0D1002F84', deviceId: 'EC:B0:D1:00:2F:84', enabled: true },
bleConnected: true, mqttConnected: true,
canRetryBle: false, canRetryMqtt: false,
displayList: [], stats: { received: 0, uploaded: 1, failed: 0 },
offlineCount: 128, autoScroll: true, recvHex: true,
logFollowing: true, lastError: null,
measurePointId: '371883', measurePointIdValid: true,
collectMode: 1, modeManual: true, canFlush: true, canSend: true, sendHex: true, sendInput: '',
};
const ev = (expr) => { try { return new Function('d', `with(d){return (${expr})}`)(DATA); } catch { return `?${expr}`; } };
const bind = (s) => s.replace(/\{\{([^}]+)\}\}/g, (_, e) => ev(e.trim()));
const html = `<!doctype html><html><head><meta charset="utf-8"><style>
${BASE_CSS}
/* ===== 真实 detail.wxss(rpx→px) ===== */
${css}
html,body{margin:0;padding:0}
</style></head><body>
<div class="page" style="width:${DEVICE_W}px;height:1000px">
<div class="body-scroll">
<div class="device-info card">
<div class="info-row"><span class="info-label">设备编号</span><span class="info-value">${bind('{{device.serialNumber}}')}</span></div>
<div class="info-row"><span class="info-label">BLE 状态</span>
<span class="status-tag ${bind("{{bleConnected ? 'online' : 'offline'}}")} ${bind("{{canRetryBle ? 'retryable' : ''}}")}">${bind("{{bleConnected ? '已连接' : (canRetryBle ? '未连接 · 点击重连' : '未连接')}}")}</span></div>
<div class="info-row"><span class="info-label">MQTT 状态</span>
<span class="status-tag ${bind("{{mqttConnected ? 'online' : 'offline'}}")} ${bind("{{canRetryMqtt ? 'retryable' : ''}}")}">${bind("{{mqttConnected ? '已连接' : (canRetryMqtt ? '未连接 · 点击重连' : '未连接')}}")}</span></div>
</div>
</div>
<div class="console">
<div class="console-header">
<span class="console-title">数据接收:</span>
<div class="console-tools" id="tools">
<button class="btn-clear-mini" id="b-clear">清空</button>
<label class="chk-item"><span class="chk-box">✓</span><span>滚动</span></label>
<label class="chk-item"><span class="chk-box">✓</span><span>Hex</span></label>
<button class="btn-flush ${bind("{{canFlush ? '' : 'is-disabled'}}")}" id="b-flush" ${DATA.canFlush ? '' : 'disabled'}>上传离线(${DATA.offlineCount})</button>
</div>
</div>
<div class="log-wrap"><div class="log-scroll"><div class="log-empty">等待设备上报数据…</div></div></div>
<div class="console-stats"><span class="stat-chip">接收 0</span><span class="stat-chip">已上报 1</span><span class="stat-chip">失败 0</span><span class="stat-chip">离线 ${DATA.offlineCount}</span></div>
<div class="console-header send-header"><span class="console-title">数据发送:</span>
<label class="chk-item"><span class="chk-box">✓</span><span>Hex</span></label></div>
<div class="send-row">
<input class="send-input" placeholder="如:00 60 00 FF 00 FF F0">
<button class="btn-send ${bind("{{canSend ? '' : 'is-disabled'}}")}" id="b-send">发送</button>
</div>
</div>
</div>
<script>
const W = ${DEVICE_W}, PAD = ${(20 * RATIO).toFixed(3)}; // console padding 20rpx
const avail = W - PAD * 2;
const r = (id) => document.getElementById(id).getBoundingClientRect();
const f = (n) => Math.round(n * 10) / 10;
const flush = r('b-flush'), tools = r('tools'), clear = r('b-clear'), send = r('b-send'), input = document.querySelector('.send-input').getBoundingClientRect();
const overflow = tools.width > avail + 0.5;
const rows = [
'可用宽度 ' + f(avail) + 'px(390 - 20rpx*2)',
'接收行工具区 ' + f(tools.width) + 'px ' + (overflow ? '❌ 溢出 ' + f(tools.width - avail) + 'px' : '✅ 未溢出,余量 ' + f(avail - tools.width) + 'px'),
' 清空 ' + f(clear.width) + 'px | 上传离线(' + ${DATA.offlineCount} + ') ' + f(flush.width) + 'px 右边界 ' + f(flush.right) + ' / 容器右界 ' + f(W - PAD),
' 上传离线是否完整可见:' + (flush.right <= W - PAD + 0.5 ? '✅ 是' : '❌ 否(被挤出 ' + f(flush.right - (W - PAD)) + 'px)'),
'发送行 输入框 ' + f(input.width) + 'px | 发送按钮 ' + f(send.width) + 'px',
'接收行高度 ' + f(r('tools').height) + 'px',
].join('\\n');
const out = document.createElement('pre');
out.id = 'result'; out.style.cssText = 'font:11px/1.6 monospace;background:#111;color:#0f0;margin:0;padding:6px 8px;white-space:pre-wrap';
out.textContent = rows; document.body.insertBefore(out, document.body.firstChild);
</script></body></html>`;
const outPath = '/tmp/detail-layout.html';
fs.writeFileSync(outPath, html);
console.log('written:', outPath);