102 lines
6.5 KiB
JavaScript
102 lines
6.5 KiB
JavaScript
/**
|
||
* 详情页接收行/发送行 视觉回归校验(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);
|