- add scripts/annotate_roster_invoices.py: match passenger names to invoice PDFs, write status + relative encoded hyperlinks into roster xlsx - add scripts/add_invoice_passenger_name.py, scripts/calibrate.py - add .opencode/skills/invoice-wrapup skill - improve invoice_tool.py / web_ui.py / vision_fallback.py automation - add device/screen/windows adaptation docs - ignore out/ (delivered invoice PDFs contain sensitive data)
5.2 KiB
5.2 KiB
Web UI 换手机适配改造 · 操作记录
2026-07-09 会话产出的操作经验沉淀。换手机后开票流程报错的排查与改造全过程。
配套文档:
device-adaptation.md(截屏节点与适配清单)、screen-adaptation.md(理论全链路识图点)。
背景
用户换了手机后开票流程总报错。异常关机重启 UI 后,对话上下文丢失。通过跟踪 work/web_runner.log 实时日志 + 对照代码,定位换手机适配的三个根因,并完成 Web UI 的改造(校准入口 + 查缺补漏开关 + 进度表排序)。
关键决策
- 不新建排查文档体系,复用已有 calibrate.py:项目里已有完整的设备校准工具(
scripts/calibrate.py+ Web UI/calibrate页),只是work/device_profile.json从未生成。换手机第一步是跑校准生成 profile,而非另造轮子。 - 校准入口放首页顶部状态栏:用
.pill样式做链接按钮,与状态指示同区,不占用主操作区。 - 查缺补漏做成 toggle 按钮而非 checkbox:放"开始运行"按钮同一排,绿色=开/灰=关,状态可见。功能原本默认开启(
final_retry_pass=True),之前用户不可见不可控。 - 总进度表按 progress 行序排:用户要求"完全按开票顺序"。
android_invoice_progress.csv的行顺序 = 处理顺序,以此为排序键,未处理项放最后。
调查与修改
1. 定位换手机报错根因(见 device-adaptation.md)
跟踪 work/web_runner.log 实测一次完整开票流程,确认 8 个截屏节点的触发频率:
| 节点 | 触发次数 | 风险 |
|---|---|---|
| vision_list_rows(列表识行) | 13 | 低(归一化坐标) |
| _vision_find_button("查看") | 3,全 found:false | 中(误判纠偏) |
| vision_detect_page(探测页面) | 3 | 低 |
| _vision_find_button("开具") | 3,全 found | 中 |
| _vision_find_button("确认") | 3,全 found | 高(H5 必走视觉) |
| _vision_find_button("继续开票") | 3,全 found:false | 高(当前机型已失效) |
| _vision_find_button("扫码开票") | 2 | 中(recover 路径) |
| _vision_find_qr_thumbnail | 2 | 低 |
根因三类:A 硬编码像素坐标(防熄屏 tap / 滑动)、B 滑动幅度按绝对像素、C 视觉 fallback 找不到按钮。A/B 已有 device_profile.json 机制但从未生成。
2. 首页加「设备校准」按钮
scripts/web_ui.py:562 header statusbar 加链接:
<a class="pill calibrate-link" href="/calibrate" title="换手机屏幕适配">📱 设备校准</a>
配套 CSS(web_ui.py:480).pill.calibrate-link 蓝色高亮可点击样式。
3. 首页加「查缺补漏」toggle 按钮
- HTML(
web_ui.py:607):<button id="finalRetryPass" class="toggle-on">放 run-actions 排最后 - CSS(
web_ui.py:504):button.toggle-on(绿)/button.toggle-off(灰) - JS(
web_ui.py:938):点击切换 class + 文案 - 后端
start_runner(web_ui.py:295)加final_retry_pass参数,False 时 append--no-final-retry-pass /api/starthandler(web_ui.py:1141)传参,前端 onclick(web_ui.py:934)读classList
4. 总进度表按开票顺序排列
status_payload(web_ui.py:208):给每条 roster_status 记 _progress_idx(progress 表行号),排序后清除临时键。未在 progress 的用 len(progress) 排最后。
progress_idx = next((i for i, r in enumerate(progress) if r is progress_row), len(progress))
# ...
roster_status.sort(key=lambda r: (r.get("_progress_idx", 0), r.get("_roster_idx", 0)))
产出物
docs/device-adaptation.md(新建):换手机适配截屏节点清单 + 操作步骤docs/web-ui-adaptation-log.md(本文件):操作经验记录scripts/web_ui.py(修改):校准入口 + 查缺补漏按钮 + 进度表排序
踩坑与启示
- 浏览器缓存是前端改动的头号陷阱:改完 web_ui.py 后用户点按钮"没执行",根因是浏览器跑旧 JS。强制刷新
Cmd+Shift+R或加 URL 参数?nocache=1绕过。后端用curl验证比盯浏览器快。 - 截图单文件覆盖无法回溯:
work/last_screenshot.png和work/_vision_find_tmp.png都是覆盖式,报错时已被后续节点覆盖。换手机排错时需手动备份或改代码按节点留档。 - "继续开票"按钮视觉恒 found:false:当前机型已失效,靠 XML 硬编码 bounds 兜底。新手机 bounds 不同会直接失败--这是换手机的高风险点。
- after_verify 必超时 12s:每个乘客都走
network_or_loading超时纠偏,全靠视觉兜底。新手机 XML 更不稳时会放大。 is比较字典引用可行:by_person_key存的是 progress 列表里的字典引用,next((i for i, r in enumerate(progress) if r is progress_row), ...)能正确找到行号。
未尽事项
work/device_profile.json仍未生成(需在/calibrate页点「保存设备配置」)- 截图留档机制未改(仍是单文件覆盖)
- 校准工具缺「扫码开票」节点的 step(recover 路径无校准项)
- 真换手机后需实测验证滑动比例适配