feat:查询菜谱适配小键盘硬件
This commit is contained in:
1 parent
a179729e79
commit
8e9bcba3c3
11 files changed
+861
-7
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import '../api/hard_api_models.dart';
|
||||
@@ -92,6 +93,103 @@ enum ReservationVerificationDateScope {
|
||||
final String title;
|
||||
}
|
||||
|
||||
/// 查询菜单外接快捷小键盘上可绑定的动作。
|
||||
///
|
||||
/// [code] 会保存到终端设置中,必须保持稳定;动作语义与查询菜单页面操作一一对应。
|
||||
enum MenuQueryShortcutAction {
|
||||
/// 取消当前识别流程并清空查询结果。
|
||||
cancel('cancel', '取消'),
|
||||
|
||||
/// 对当前识别出的用户提交核销。
|
||||
verification('verification', '核销'),
|
||||
|
||||
/// 切换到人脸识别并立即启动识别。
|
||||
faceRecognition('face', '人脸识别'),
|
||||
|
||||
/// 切换到饭卡识别并立即启动识别。
|
||||
cardRecognition('card', '饭卡识别'),
|
||||
|
||||
/// 重新打印最近一次小票。
|
||||
reprint('reprint', '重新打印'),
|
||||
;
|
||||
|
||||
/// 创建与稳定编码绑定的快捷动作。
|
||||
const MenuQueryShortcutAction(this.code, this.title);
|
||||
|
||||
/// 终端设置中持久化的稳定动作编码。
|
||||
final String code;
|
||||
|
||||
/// 设置页展示、查询菜单运行时复用的动作名称。
|
||||
final String title;
|
||||
}
|
||||
|
||||
/// 单个快捷动作与物理按键的绑定关系,保存在本机 sysJson 中,不涉及数据库结构。
|
||||
@immutable
|
||||
@JsonSerializable()
|
||||
class MenuQueryShortcutKeyBinding {
|
||||
/// 创建按键绑定;设备未提供物理键位时 [usbHidUsage] 允许为 0,仅以逻辑键兜底匹配。
|
||||
const MenuQueryShortcutKeyBinding({
|
||||
this.usbHidUsage = 0,
|
||||
this.logicalKeyId = 0,
|
||||
});
|
||||
|
||||
/// 从真实硬件键盘事件提取绑定;物理键位是首选匹配依据,逻辑键仅兜底。
|
||||
factory MenuQueryShortcutKeyBinding.fromKeyEvent(KeyEvent event) {
|
||||
return MenuQueryShortcutKeyBinding(
|
||||
usbHidUsage: event.physicalKey.usbHidUsage,
|
||||
logicalKeyId: event.logicalKey.keyId,
|
||||
);
|
||||
}
|
||||
|
||||
factory MenuQueryShortcutKeyBinding.fromJson(Map<String, dynamic> json) =>
|
||||
_$MenuQueryShortcutKeyBindingFromJson(json);
|
||||
|
||||
/// 按键的 USB HID 物理键位;同一物理按键不随键盘布局变化,0 表示设备未提供。
|
||||
final int usbHidUsage;
|
||||
|
||||
/// 按键的逻辑键 ID;仅作为物理键位缺失时的兜底匹配与展示依据,0 表示未记录。
|
||||
final int logicalKeyId;
|
||||
|
||||
Map<String, dynamic> toJson() => _$MenuQueryShortcutKeyBindingToJson(this);
|
||||
|
||||
/// 判断两个绑定是否指向同一个按键。
|
||||
///
|
||||
/// 物理或逻辑键任一相同即视为冲突:物理键相同说明是同一个物理按键;
|
||||
/// 物理键不同但逻辑键相同时,运行期匹配存在歧义风险,同样不允许重复绑定。
|
||||
bool conflictsWith(MenuQueryShortcutKeyBinding other) {
|
||||
if (usbHidUsage != 0 &&
|
||||
other.usbHidUsage != 0 &&
|
||||
usbHidUsage == other.usbHidUsage) {
|
||||
return true;
|
||||
}
|
||||
return logicalKeyId != 0 &&
|
||||
other.logicalKeyId != 0 &&
|
||||
logicalKeyId == other.logicalKeyId;
|
||||
}
|
||||
|
||||
/// 展示按键的可读名称;优先物理键名,其次逻辑键名,均缺失时回退到键值,便于现场排查。
|
||||
String get displayLabel {
|
||||
final physicalName =
|
||||
PhysicalKeyboardKey.findKeyByCode(usbHidUsage)?.debugName;
|
||||
if (physicalName != null && physicalName.isNotEmpty) return physicalName;
|
||||
final logicalName =
|
||||
LogicalKeyboardKey.findKeyByKeyId(logicalKeyId)?.debugName;
|
||||
if (logicalName != null && logicalName.isNotEmpty) return logicalName;
|
||||
return '键码 0x${usbHidUsage.toRadixString(16).padLeft(4, '0').toUpperCase()}';
|
||||
}
|
||||
|
||||
/// 按全部持久化字段比较,用于恢复配置与测试断言。
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
other is MenuQueryShortcutKeyBinding &&
|
||||
other.usbHidUsage == usbHidUsage &&
|
||||
other.logicalKeyId == logicalKeyId;
|
||||
|
||||
/// 与相等判断字段一致的哈希值。
|
||||
@override
|
||||
int get hashCode => Object.hash(usbHidUsage, logicalKeyId);
|
||||
}
|
||||
|
||||
/// 查询菜单在本机保存的页面交互偏好,不改变预约接口和硬件协议。
|
||||
@immutable
|
||||
class ReservationQueryPagePreferences {
|
||||
@@ -107,6 +205,7 @@ class ReservationQueryPagePreferences {
|
||||
this.allowManualMealPeriodChange = true,
|
||||
this.selectedMealSlot = ReservationMealSlot.morning,
|
||||
this.verificationDateScope = ReservationVerificationDateScope.today,
|
||||
this.shortcutKeyBindings = const {},
|
||||
});
|
||||
|
||||
/// 未保存页面设置时使用的默认查询偏好。
|
||||
@@ -121,6 +220,7 @@ class ReservationQueryPagePreferences {
|
||||
allowManualMealPeriodChange: true,
|
||||
selectedMealSlot: ReservationMealSlot.morning,
|
||||
verificationDateScope: ReservationVerificationDateScope.today,
|
||||
shortcutKeyBindings: {},
|
||||
);
|
||||
|
||||
/// 查询方式的页面展示顺序。
|
||||
@@ -154,6 +254,10 @@ class ReservationQueryPagePreferences {
|
||||
|
||||
/// 允许显示核销入口的预约日期范围。
|
||||
final ReservationVerificationDateScope verificationDateScope;
|
||||
|
||||
/// 外接快捷小键盘的动作按键绑定;键为动作,值为绑定的物理按键,未配置时为空集合。
|
||||
final Map<MenuQueryShortcutAction, MenuQueryShortcutKeyBinding>
|
||||
shortcutKeyBindings;
|
||||
}
|
||||
|
||||
/// 预约订单中的套餐组成展示模型,统一处理缺失名称和异常份数。
|
||||
|
||||
@@ -6,6 +6,20 @@ part of 'reservation_menu_page_models.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
MenuQueryShortcutKeyBinding _$MenuQueryShortcutKeyBindingFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
MenuQueryShortcutKeyBinding(
|
||||
usbHidUsage: (json['usbHidUsage'] as num?)?.toInt() ?? 0,
|
||||
logicalKeyId: (json['logicalKeyId'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$MenuQueryShortcutKeyBindingToJson(
|
||||
MenuQueryShortcutKeyBinding instance) =>
|
||||
<String, dynamic>{
|
||||
'usbHidUsage': instance.usbHidUsage,
|
||||
'logicalKeyId': instance.logicalKeyId,
|
||||
};
|
||||
|
||||
ReservationMenuMealData _$ReservationMenuMealDataFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
ReservationMenuMealData(
|
||||
|
||||
Reference in new issue
Block a user