feat:取消系统自带软键盘设计,改为系统自用键盘组件-初步
feat:将打印分为图片打印和命令式打印-初步
This commit is contained in:
1 parent
94ee7415b1
commit
e68f41375b
38 files changed
+1471
-408
No files matched your search
@@ -1,43 +1,62 @@
|
||||
# 查询菜单键盘模式:设置开关 + 运行时接线 + 冲突矩阵 + 解耦
|
||||
# 自研可拖动软键盘组件 + 全面清理系统软键盘适配代码
|
||||
|
||||
## 一、设置页:「该页面是否开启键盘模式」开关(默认否)
|
||||
## 一、新组件:`AppSoftKeyboard`(lib/common/widgets/app/app_soft_keyboard.dart)
|
||||
|
||||
- `ReservationQueryPagePreferences` 新增 `keyboardModeEnabled`(默认 false,旧终端兼容)。
|
||||
- `AppSettingTool`:sysJson 新键 `reservationQueryKeyboardModeEnabled`(缺失=false),随查询菜单偏好一起读写。
|
||||
- `SettingController`:Rx 字段 + `setReservationQueryKeyboardModeEnabled` + 载入/还原/串行持久化(复用现有模式)。
|
||||
- 设置 UI:「快捷按钮」分组顶部加开关行(SettingInfoRow+FSwitch):"开启键盘模式",副文案"开启后进入查询菜单,按已绑定的物理按键将语音播报并执行对应操作"。
|
||||
**一个可拖动的键盘弹出框,三个面板(数字/字母/符号),显式绑定 TextEditingController。**
|
||||
|
||||
## 二、解耦结构(三个新文件 + controller 最小改动)
|
||||
### 弹层形态
|
||||
- `showGeneralDialog`(`useRootNavigator: true`、`barrierColor: 透明`、`barrierDismissible: true`):点击键盘外任意处收起;键盘浮在页面内容之上,页面可见;生命周期由路由托管,零手工 OverlayEntry 管理。
|
||||
- 默认停靠屏幕底部居中,整体可用 `GestureDetector(onPan...)` + `Transform.translate` 自由拖动(实现借鉴 app_dialog.dart:224-274 的基准矩形+安全区钳制方案),拖动手柄在面板顶部。
|
||||
- 面板宽度约 760.w,按键高 62.h(与现 _KeyButton 一致),样式沿用 XfjDesignTokens(surface 底/line 描边/controlRadius)。
|
||||
|
||||
键盘输入与动作编排全部移出主 controller,controller 只做接线(项目已有 `OrderMixin` 的 mixin-on-controller 先例):
|
||||
### 三面板
|
||||
- **数字**:1-9、0、退格、清空、确认(3x4)。
|
||||
- **字母**:QWERTY 三行 + 第四行(⇧大小写、123 切数字、空格、退格、确认)。
|
||||
- **符号**:常用符号网格(`- . _ / * , : % # + @ & ? ! ( ) 空格` 等)+ 退格、确认。
|
||||
- 面板切换:顶部手柄行内三个 tab(123 / ABC / #+=)。
|
||||
|
||||
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` 与字母反查函数公共化,设置页与查询菜单页共用(模拟器/输入法合成文本两条路径全覆盖)。
|
||||
### API
|
||||
```dart
|
||||
static Future<void> show(BuildContext context, {
|
||||
required TextEditingController controller, // 直接操作光标位置插入/退格
|
||||
AppSoftKeyboardMode mode = ...digits, // digits | letters | symbols 初始面板
|
||||
RegExp? allowPattern, // 场景字符白名单(替代 inputFormatters 语义)
|
||||
int? maxLength,
|
||||
})
|
||||
```
|
||||
- 插入:`controller.value.replaced(selection, ch)`(光标处插入,插完校验 allowPattern/maxLength,超限忽略);
|
||||
- 退格/清空/确认同理操作 controller;确认=关闭弹层。
|
||||
|
||||
controller 改动仅:`with` 动作 mixin、`_loadPagePreferences` 补读 `keyboardModeEnabled`+`shortcutKeyBindings`、偏好加载后启动键盘协调器(`onClose` 停止)、新增公开 getter `resultDialogPresented`、`_publishQuickVerificationRequest` 改公开 `requestQuickVerification()`(快速核销与快捷键共用同一入口)、核销成功时记录最近核销订单 ID(供补打)。view.dart 挂条件隐形收集器。
|
||||
## 二、接入点改造(第一期:终端高频场景)
|
||||
|
||||
## 三、动作映射与冲突矩阵(核心细节)
|
||||
1. **`TerminalToolbarTextField`**(terminal_toolbar.dart,公共工具栏输入框)加参数 `softKeyboard`(AppSoftKeyboardMode?)与 `softKeyboardAllowPattern`:启用时 `keyboardType: TextInputType.none` + `onTap` 弹出自研键盘。物理键盘/扫码枪输入不受影响(none 只禁系统软键盘)。
|
||||
2. **收银搜索框**(cashier_workspace.dart):删除 `softKeyboardEnabled`/`searchSoftKeyboardEnabled` 参数链,食堂与超市统一传 `softKeyboard: letters`(超市实体键盘照常,触摸用自研键盘)。
|
||||
3. **开卡页卡号搜索**(card_active/view.dart):传 `softKeyboard: digits`。
|
||||
4. **终端操作验证密码弹框**(setting_password_dialog.dart):删除内嵌 AppPinKeyboard,改为密码框 `keyboardType: none` + 打开弹框时主动 `AppSoftKeyboard.show(mode: digits, allowPattern: 数字)`(覆盖 autofocus 场景),点输入框也可再次唤起。
|
||||
5. **绑定唯一编号弹框**(setting_baseInfo.dart):同样接 `digits`。
|
||||
|
||||
| 按键 | 动作 | 执行 | 拒绝条件(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`;补打进行中(防重标志) |
|
||||
第二期(本次不做):设置页其余搜索/读卡器/Cron/硬件输入弹框/登录页/设备绑定页,以及中文拼音首字母搜索。
|
||||
|
||||
通用规则:所有命中先 TTS「你已点击X」;拒绝时 TTS+Toast 说明原因并保持原状态;`_resultDialogPresented`(弹框占用期)一律拒绝;补打原因弹框打开期间同样进入占用态防重入。
|
||||
## 三、清理系统软键盘适配代码(全部删除)
|
||||
|
||||
## 四、行为说明
|
||||
1. `lib/common/utils/app/app_ime_tool.dart` 整文件 + main.dart 安装点 + utils/index.dart 导出;
|
||||
2. MainActivity.kt 的 `forceShowSoftInput` 分支(89-113 行)与 `InputMethodManager` import(通道本身保留,AppRuntimeTool 在用);
|
||||
3. `lib/common/widgets/app/app_shortcut_ime_collector.dart` 整文件 + 设置页绑定区的挂载与 `_handleImeText` 兜底回调(快捷键绑定的 KeyEvent 通道完整保留,真机小键盘/adb 注入仍可绑定);
|
||||
4. `lib/common/widgets/app/app_pin_keyboard.dart` 整文件(被 AppSoftKeyboard 取代)+ setting_password_dialog 内嵌段 + widgets/index.dart 两处导出更新;
|
||||
5. 4 处 `SystemChannels.textInput.invokeMethod('TextInput.hide')`(快捷键协调器、查询菜单 controller 的 releaseResidualTextInput、verification_records、order_history)——**各处 `unfocus()` 全部保留**(焦点卫生,防物理输入落入旧输入框),只删 hide 与"等 100ms 键盘退场"类代码,相关注释同步改写。
|
||||
|
||||
- 默认关闭;开关与绑定均为进页面时一次性快照(与现有偏好读取时机一致,改设置需重新进入查询菜单生效——与现有自动识别等配置口径相同)。
|
||||
- 与扫码枪监听(BarcodeScanManage)共存:快捷键仅在键盘模式开启且命中已绑定字母时消费事件;查询菜单页无扫码业务,扫码缓冲不受影响。
|
||||
- 模拟器验证:页面同样挂隐形收集器(`keyboardType.none` 不弹软键盘),软键盘/注入文本可触发;真机小键盘走按键事件通道。
|
||||
## 四、保留不动(说明理由)
|
||||
|
||||
- AndroidManifest `adjustResize`、登录页/AppDialog 的 viewInsets 消费(第二期接键盘后统一清);
|
||||
- `_hasActiveTextInput` 快捷键让路、扫码枪白名单、各页面 unfocus 焦点管理;
|
||||
- setting_face/setting_product 搜索框现状(第二期场景)。
|
||||
|
||||
## 五、验证
|
||||
|
||||
`dart format` + `flutter analyze`(0 新增错误)+ 跑 `test/pages/terminal/menu_query_controller_test.dart`(现有 38 例全过);键盘协调器为纯类,动作矩阵逻辑随 mixin 附带单测可后续补充。变更预计 9 个文件、新增约 700 行。
|
||||
`dart format` + `flutter analyze`(0 新增错误)+ 38 个 menu_query 测试全过 + 构建 debug APK 装真机:
|
||||
- 终端操作验证弹框打开 → 自研数字键盘自动弹出 → 触摸输入密码可提交;
|
||||
- 食堂收银搜索框点击 → 自研键盘(字母默认)弹出 → 可拖动 → 输入+确认触发搜索 → 点击外部收起;
|
||||
- 查询菜单快捷键 A/B/E 回归不受影响;
|
||||
- 超市机型语义:实体键盘/扫码枪输入正常(不装真机则靠代码审查+测试保证)。
|
||||
|
||||
变更预计:新增 1 个组件文件(~400 行),修改约 12 个文件,删除约 350 行老代码。
|
||||
@@ -0,0 +1,58 @@
|
||||
# 打印机「指令打印」模式改造计划(后端拼命令 + 终端写字节)
|
||||
|
||||
## 方案总览
|
||||
|
||||
现状:后端 `ThermalReceiptImage.java` 把发票/兑换券渲染成 PNG 图片 → 任务只带 `pic` URL 下发(MQTT PRINT 消息 / `/hard/hard-api/ad/printer/tasks`)→ 终端 `PrintManage.printImg` 下载图片、转 ESC/POS 栅格指令、写 USB。图片栅格打印慢。
|
||||
|
||||
目标:后端在生成图片的同时,用平行的 ESC/POS 命令拼装器产出**命令字符串(Base64 字节流)**,随任务经新字段 `printCmd` 下发;终端在设置「打印机」页新增**打印方式**切换(图片打印=默认 / 指令打印),指令模式下把命令解码后直接写 USB(前后仍按本地 `printerAutoCut` 补走纸/切刀,与图片模式边界一致)。无命令或命令损坏的任务自动回退图片打印,保证兼容与可用性。
|
||||
|
||||
---
|
||||
|
||||
## 一、协议契约(前后端共同约定)
|
||||
|
||||
- 新字段 `printCmd`:String、可空、**Base64(ESC/POS 字节流)**,随 MQTT PRINT 消息体和 `/printer/tasks` 接口下发。
|
||||
- 命令内容边界:凭证正文(标题、分隔线、字段行、商品明细、合计、打印时间),**不含**末尾走纸与切刀——由终端统一包装 `feed(1) + 命令 + feed(1) + [autoCut ? cut()]`,保持与现有图片模式相同的边界行为,且尊重终端本地的 `printerAutoCut` 配置。
|
||||
- 中文编码 GBK,行宽按 80mm/48 列(与终端 `maxPerLine` 一致)。
|
||||
- 向后兼容:字段可空可选;旧终端(含基线 `biz_supermarket_cloud`,json_serializable 忽略未知字段)不受影响;旧后端/旧任务无命令时终端回退图片打印。
|
||||
|
||||
## 二、后端改动(biz-api-cloud-zax / pjcloud-hardware)
|
||||
|
||||
1. **新增 `EscPosReceiptCommand`**(`pjcloud-hardware-api/.../hard/api/utils/`,与 `ThermalReceiptImage` 平行的流式 builder,同款 API:`title/banner/keyValue/sectionTitle/voucherOrderSummary/invoiceItem/voucherProduct/voucherTotal/divider`):
|
||||
- 内部拼 ESC/POS:`ESC @` 初始化、`ESC a` 对齐、`ESC E` 加粗、`GS !` 放大、GBK 中文编码、按 48 列排版(名称/数量/单价/小计左右布局,长名称折行或截断,避免依赖宽度易错 API)。
|
||||
- 输出 `toBase64()`;**不含**走纸/切刀。
|
||||
2. **`HardInvoiceServiceImpl`**(1404 行):发票(printType=1)与兑换券(printType=2)生成处,用同一份数据流双输出(图片 `pic` + 命令 `printCmd`),版式对齐两张参考图(智慧食堂发票 / 预约核销兑换券)。
|
||||
3. **`HardInvoiceEntity` 加 `print_cmd` 列**(可空,默认 NULL)支撑 tasks 补拉重下发;配套后端数据库迁移(可空列,兼容旧数据)。
|
||||
4. **MQTT PRINT 消息体与 `/printer/tasks` 响应**加 `printCmd` 字段。
|
||||
5. **单测**:新增 `EscPosReceiptCommandTest`(对照 `ThermalReceiptImageTest` 的用例结构),验证发票/兑换券两种凭证的命令拼装。
|
||||
|
||||
## 三、终端改动(biz_xfj_cloud,当前工作区)
|
||||
|
||||
1. **`SysSetting` 新字段 `printerOutputMode`**(String,默认 `'IMAGE'`,取值 `'IMAGE'`/`'COMMAND'`,`lib/common/models/database/database_models.dart`)——写入 sysJson JSON 内新键,**不改表结构**;`updatePrinterBinding()` 现有逻辑只逐字段赋值打印机配置,不会覆盖此本地开关。
|
||||
2. **`AppSettingTool` 加本地 setter**(如 `setPrinterOutputMode`,走既有 `_updateSysJson` 串行落库模式,参考 `setCashierPagePreferences` 写法)。
|
||||
3. **设置页「打印机」**(`lib/pages/terminal/setting/widgets/setting_printer_info.dart`):在「后台打印配置」组附近新增「打印方式」分段选择器(复用 `SettingSegmentedSelector`,选项:图片打印/指令打印),切换即时生效并持久化;附一行说明文案(指令模式需后台支持,无命令的任务自动回退图片打印)。UI 对齐现有分组、间距、风格。
|
||||
4. **`MqttInvoiceMsg` 加 `printCmd` 字段**(`lib/common/models/mq/mq_models.dart`,可空 String)→ 跑 build_runner 更新 `mq_models.g.dart`;`SyncPrintTool.pullPendingTasks` 与 `app_mq_tool.dart` 入队处透传。
|
||||
5. **`print_history` 表加列 `printCmd`**(TEXT 可空):
|
||||
- `print_history_entity.dart` 加字段 + 注释;
|
||||
- `database_migration.dart` 新增连续 `Migration(13, 14, ...)`(`ALTER TABLE print_history ADD COLUMN printCmd TEXT`)并按版本顺序加入 `getDatabaseMigrations()`;
|
||||
- `database.dart` `@Database(version: 14)`;
|
||||
- **同步** `android/.../facediscern/database/DatabaseHelper.java` `DATABASE_VERSION = 14`(Floor 先迁移、虹软后访问的既有顺序不变)。
|
||||
6. **`PrintManage` 新增 `printCommand(String base64Cmd)`**:Base64 解码(空/损坏 → `lastError` + 返回 false)→ `feed(1) + 命令字节 + feed(1) + [printerAutoCut ? cut()]` → `writeBytes`;设备状态上报(`updatePrinterWorking` 等)与 `printImg` 完全一致。
|
||||
7. **`SyncPrintTool._print` 打印方式分支**:
|
||||
- 图片模式:照旧 `printImg(task.invoiceUrl)`(忽略命令)。
|
||||
- 指令模式:有命令 → `printCommand`;**USB 写入失败只按既有 0/5/30s 重试命令,不回退图片**(避免一次任务两种内容重复出纸);仅在**无命令或 Base64 解码失败**时回退 `printImg`。
|
||||
8. **测试打印适配**:指令模式下 `printTestPage` 改为终端本地拼简单中文文本指令(`EscGenerator.text(containsChinese: true)` + `hr` + `feed` + `cut`,注意行宽 48 列内),用于验证指令链路;图片模式照旧画图。
|
||||
9. 既有重打(`reprintVoucher` 重试版本)、结果上报(`reportPrinterResult`)、幂等逻辑**均不改动**——重打任务经服务端重新下发自然携带命令。
|
||||
|
||||
## 四、验证
|
||||
|
||||
- 终端:`fvm dart run build_runner build --delete-conflicting-outputs`(受影响模型);`dart format` 本次改动文件;`fvm flutter analyze` 区分新增/既有告警。
|
||||
- 后端:pjcloud-hardware 模块 `mvn` 编译 + 单测(含新 EscPosReceiptCommandTest)。
|
||||
- 数据库:迁移需在真机验证「13 升 14」与「全新安装」两条路径,`user_version` 与虹软版本一致。
|
||||
- 真机联调(需实际收银机):指令模式打印发票与兑换券(对照参考图核对版式/中文/合计)、无命令回退图片、设置切换即时生效、指令模式测试打印、切刀行为。
|
||||
- 执行期间遵守通用规则:会话步骤条 + 变更统计(新增/删除/总变动),首次写入后保持 Diff 预览。
|
||||
|
||||
## 五、边界与不做的事
|
||||
|
||||
- 不改 `printImg` 现有行为、不改上报/幂等协议、不改 `printerImageMode`(GS_V0/ESC_STAR 仍只作用于图片模式)。
|
||||
- 基线项目 `biz_supermarket_cloud` 本次不引入(协议向后兼容,旧终端忽略新字段);如需同步另开任务。
|
||||
- 指令模式版式为「按图片版式近似还原」而非像素级一致,字重/放大效果以后端拼装为准,真机验收时微调。
|
||||
Reference in new issue
Block a user