feat:查询菜单页面适配小键盘

This commit is contained in:
dzc committed 2026-09-20 13:20:50 +08:00
1 parent 8e9bcba3c3
commit f9be6dea13
10 files changed
+922 -182

No files matched your search

@@ -1,34 +1,43 @@
# 修复"绑定按键按下 e 后无响应/软键盘弹出"问题
# 查询菜单键盘模式:设置开关 + 运行时接线 + 冲突矩阵 + 解耦
## 根因
## 一、设置页:「该页面是否开启键盘模式」开关(默认否)
截图证明"取消"行已进入"等待按键…"监听态,但按"e"后**没有任何 Toast、没有任何绑定变化** —— 说明按键事件根本没到达 `HardwareKeyboard` 的监听 handler(若到达,成功/冲突都必有 Toast)。
- `ReservationQueryPagePreferences` 新增 `keyboardModeEnabled`(默认 false,旧终端兼容)。
- `AppSettingTool`:sysJson 新键 `reservationQueryKeyboardModeEnabled`(缺失=false),随查询菜单偏好一起读写。
- `SettingController`:Rx 字段 + `setReservationQueryKeyboardModeEnabled` + 载入/还原/串行持久化(复用现有模式)。
- 设置 UI:「快捷按钮」分组顶部加开关行(SettingInfoRow+FSwitch):"开启键盘模式",副文案"开启后进入查询菜单,按已绑定的物理按键将语音播报并执行对应操作"。
结合"按 e 时软键盘弹出",根因是 **Android IME(输入法)拦截**:
## 二、解耦结构(三个新文件 + controller 最小改动)
1. 当输入法连接处于激活状态(软键盘弹出、或此前某个输入框残留焦点未释放)时,Android 会把物理键盘的字符键(如 e)先交给输入法,**合成为文本提交**,不再作为 KeyEvent 分发给 Flutter —— 我们的 handler 因此收不到任何 KeyDownEvent。
2. 若用户按的是**屏幕软键盘上的"e"**(小键盘尚未接入、用软键盘模拟测试),同样只产生 IME 文本提交,Flutter 侧本来就收不到"按了哪个键"的按键事件 —— 软键盘按键**机制上无法**用于绑定。
键盘输入与动作编排全部移出主 controller,controller 只做接线(项目已有 `OrderMixin` 的 mixin-on-controller 先例):
排查确认:设置模块存在多个可激活 IME 的入口(日志预览关键词筛选框、读写卡密钥三个输入框、食堂机型人脸姓名搜索框、`autofocus: true` 的人工硬件输入弹窗);而我们的 `_startListening` **只注册了键盘监听器,没有断开输入焦点** —— 项目里 `BarcodeScanManage`(扫码枪)正是因为同样的问题,在监听前和每次事件时都先 `unfocus()`。
1. **`menu_query/reservation_shortcut_keyboard.dart`(新)— 输入协调器** `ReservationShortcutKeyboard`:
纯类(无 GetX 依赖、可单测)。职责:持有模式开关与绑定快照、挂/卸 `HardwareKeyboard` 处理器、KeyDownEvent+修饰键过滤、`conflictsWith` 匹配动作、命中后回调 `onAction(action)` 并 TTS「你已点击X」;提供 `handleImeText()` 供输入法文本兜底统一进入同一匹配。TTS/提示经构造注入(复用 controller 的 `_speaker`/`_errorNotifier`)。
2. **`menu_query/reservation_shortcut_actions.dart`(新)— 动作 mixin** `ReservationMenuQueryShortcutActions on ReservationMenuQueryController`:
实现五个动作的编排(冲突校验矩阵、防重、补打流程),调用 controller 现有公开方法,主 controller 零业务膨胀。
3. **`common/widgets/app/app_shortcut_ime_collector.dart`(新)— 公共隐形输入收集器**:
把设置页的 `_ShortcutImeCollector` 与字母反查函数公共化,设置页与查询菜单页共用(模拟器/输入法合成文本两条路径全覆盖)。
## 修复内容(仅改 1 个文件)
controller 改动仅:`with` 动作 mixin、`_loadPagePreferences` 补读 `keyboardModeEnabled`+`shortcutKeyBindings`、偏好加载后启动键盘协调器(`onClose` 停止)、新增公开 getter `resultDialogPresented`、`_publishQuickVerificationRequest` 改公开 `requestQuickVerification()`(快速核销与快捷键共用同一入口)、核销成功时记录最近核销订单 ID(供补打)。view.dart 挂条件隐形收集器。
`lib/pages/terminal/setting/widgets/setting_menu_query_shortcut.dart`:
## 三、动作映射与冲突矩阵(核心细节)
1. **`_startListening` 进入监听时断开输入焦点并隐藏软键盘**(对齐项目既有防御写法 `barcode_scan_manage.dart:63` 与 `order_history/view.dart:433-434` 组合):
```dart
FocusManager.instance.primaryFocus?.unfocus();
SystemChannels.textInput.invokeMethod<void>('TextInput.hide');
```
确保真实小键盘的物理按键走 KeyEvent 路径直达监听器,不被输入法吞掉。
| 按键 | 动作 | 执行 | 拒绝条件(TTS+Toast 提示) |
|---|---|---|---|
| E | 取消 | `clearForPrivacy()`(现成,幂等) | 弹框展示期:「请先完成当前弹窗操作」 |
| D | 核销 | `requestQuickVerification()`(复用快速核销事件→同一核销弹框,自带"今天+选中餐段+可核销订单"校验) | 弹框期;`recognizing/querying`:「正在识别/查询,请稍后」;提交中 |
| A | 人脸识别 | 未识别时=启动/切换人脸识别(`selectMethod(face)`,自动模式自带启动;已是该方式且空闲则直接 `startRecognition()`,等同"开始识别"按钮) | `recognizing` 且当前方式为饭卡/人脸:「正在进行{当前方式}识别,不能切换为{目标}识别,请先按取消」;`querying`:「正在查询,请稍后」;弹框期 |
| B | 饭卡识别 | 对称同上 | 对称同上 |
| C | 重新打印 | 记录本页最近一次核销订单 → `HardApi.getPrinterRecords(orderId)` 找最新可补打记录 → `showVoucherReprintDialog` 选原因(复用核销记录页弹框)→ `HardApi.reprintVoucher` → `SyncPrintTool.pullPendingTasks()`;无记录时提示「本页暂无可补打的核销小票」 | 弹框期;`recognizing/querying`;补打进行中(防重标志) |
2. **提示文案说明软键盘不可用**:等待提示与空闲提示明确"请按外接小键盘的物理按键,屏幕软键盘按键无法完成绑定",避免现场用软键盘测试时误判功能损坏。
通用规则:所有命中先 TTS「你已点击X」;拒绝时 TTS+Toast 说明原因并保持原状态;`_resultDialogPresented`(弹框占用期)一律拒绝;补打原因弹框打开期间同样进入占用态防重入。
## 验证
## 四、行为说明
- `flutter analyze` + `dart format` 改动文件;
- 说明清楚:软键盘按键在 Flutter 中不产生按键事件,属于平台机制,绑定功能需接入真实外接小键盘后才能完整验证(本次修复保证接入后 IME 不再拦截)。
- 默认关闭;开关与绑定均为进页面时一次性快照(与现有偏好读取时机一致,改设置需重新进入查询菜单生效——与现有自动识别等配置口径相同)。
- 与扫码枪监听(BarcodeScanManage)共存:快捷键仅在键盘模式开启且命中已绑定字母时消费事件;查询菜单页无扫码业务,扫码缓冲不受影响。
- 模拟器验证:页面同样挂隐形收集器(`keyboardType.none` 不弹软键盘),软键盘/注入文本可触发;真机小键盘走按键事件通道。
## 变更统计
## 五、验证
预计新增约 12 行 / 删除 2 行(单文件小改)。
`dart format` + `flutter analyze`(0 新增错误)+ 跑 `test/pages/terminal/menu_query_controller_test.dart`(现有 38 例全过);键盘协调器为纯类,动作矩阵逻辑随 mixin 附带单测可后续补充。变更预计 9 个文件、新增约 700 行。