feat:餐段设置

This commit is contained in:
dzc committed 2026-09-07 12:53:05 +08:00
1 parent 67950343a7
commit 07a3141934
14 files changed
+285 -18

No files matched your search

@@ -104,6 +104,7 @@ class ReservationQueryPagePreferences {
this.nameMaskEnabled = true,
this.verificationEnabled = false,
this.quickVerificationEnabled = false,
this.allowManualMealPeriodChange = true,
this.selectedMealSlot = ReservationMealSlot.morning,
this.verificationDateScope = ReservationVerificationDateScope.today,
});
@@ -117,6 +118,7 @@ class ReservationQueryPagePreferences {
nameMaskEnabled: true,
verificationEnabled: false,
quickVerificationEnabled: false,
allowManualMealPeriodChange: true,
selectedMealSlot: ReservationMealSlot.morning,
verificationDateScope: ReservationVerificationDateScope.today,
);
@@ -144,6 +146,9 @@ class ReservationQueryPagePreferences {
/// 识别查询成功后是否直接打开今天预选餐段的核销弹框,默认关闭以保持旧终端流程。
final bool quickVerificationEnabled;
/// 是否允许操作员在查询菜单主动修改餐段;关闭后严格按餐段配置时间自动切换。
final bool allowManualMealPeriodChange;
/// 查询菜单最近一次选中的餐段,旧终端未保存时默认早餐。
final ReservationMealSlot selectedMealSlot;
+27 -4
View File
@@ -35,6 +35,10 @@ abstract class AppSettingTool {
static const String _cashierShowHoldActionKey = "cashierShowHoldAction";
static const String _cashierShowMoreActionKey = "cashierShowMoreAction";
/// 收银页是否允许主动修改餐段的稳定存储键;缺失时按允许处理,兼容旧终端。
static const String _cashierAllowManualMealPeriodChangeKey =
"cashierAllowManualMealPeriodChange";
/// 顶部隐藏设备编码列表在现有 sysJson 中的稳定存储键;缺失或空列表表示五项全部显示。
static const String _hiddenTerminalDeviceStatusesKey =
"hiddenTerminalDeviceStatuses";
@@ -63,6 +67,10 @@ abstract class AppSettingTool {
static const String _reservationQueryQuickVerificationEnabledKey =
"reservationQueryQuickVerificationEnabled";
/// 查询菜单是否允许主动修改餐段的稳定存储键;缺失时按允许处理,兼容旧终端。
static const String _reservationQueryAllowManualMealPeriodChangeKey =
"reservationQueryAllowManualMealPeriodChange";
/// 查询菜单最近餐段在现有 sysJson 中的稳定存储键,值使用后端餐段编码且不新增数据库字段。
static const String _reservationQuerySelectedMealSlotKey =
"reservationQuerySelectedMealSlot";
@@ -472,25 +480,34 @@ abstract class AppSettingTool {
});
}
/// 读取收银页底部快捷操作显隐;旧终端没有配置时保持两个入口都显示。
static Future<({bool showHoldAction, bool showMoreAction})>
getCashierPagePreferences() async {
/// 读取收银页底部快捷操作显隐及餐段修改权限;旧终端没有配置时保持默认允许。
static Future<
({
bool showHoldAction,
bool showMoreAction,
bool allowManualMealPeriodChange,
})> getCashierPagePreferences() async {
final setting = await _getSetting();
final sysJson = Map<String, dynamic>.from(json.decode(setting.sysJson));
return (
showHoldAction: sysJson[_cashierShowHoldActionKey] != false,
showMoreAction: sysJson[_cashierShowMoreActionKey] != false,
allowManualMealPeriodChange:
sysJson[_cashierAllowManualMealPeriodChangeKey] != false,
);
}
/// 收银快捷操作属于本机页面布局配置,继续复用 sysJson,避免增加数据库字段。
/// 收银快捷操作与餐段修改权限属于本机页面布局配置,继续复用 sysJson。
static Future<void> setCashierPagePreferences({
required bool showHoldAction,
required bool showMoreAction,
required bool allowManualMealPeriodChange,
}) =>
_updateSysJson((sysJson) {
sysJson[_cashierShowHoldActionKey] = showHoldAction;
sysJson[_cashierShowMoreActionKey] = showMoreAction;
sysJson[_cashierAllowManualMealPeriodChangeKey] =
allowManualMealPeriodChange;
});
/// 读取商品餐段时间;旧终端无配置或持久化值损坏时返回产品默认时间。
@@ -627,6 +644,9 @@ abstract class AppSettingTool {
// 快速核销从属于核销入口,旧设置缺键或核销关闭时均不得隐式生效。
quickVerificationEnabled: verificationEnabled &&
sysJson[_reservationQueryQuickVerificationEnabledKey] == true,
// 旧设置没有该键时保持允许手动修改,避免升级后收银流程被意外锁死。
allowManualMealPeriodChange:
sysJson[_reservationQueryAllowManualMealPeriodChangeKey] != false,
selectedMealSlot: selectedMealSlot,
verificationDateScope: verificationDateScope,
);
@@ -641,6 +661,7 @@ abstract class AppSettingTool {
required bool nameMaskEnabled,
required bool verificationEnabled,
required bool quickVerificationEnabled,
required bool allowManualMealPeriodChange,
required ReservationVerificationDateScope verificationDateScope,
}) {
final normalized = <ReservationRecognitionMethod>[];
@@ -664,6 +685,8 @@ abstract class AppSettingTool {
sysJson[_reservationQueryVerificationEnabledKey] = verificationEnabled;
sysJson[_reservationQueryQuickVerificationEnabledKey] =
verificationEnabled && quickVerificationEnabled;
sysJson[_reservationQueryAllowManualMealPeriodChangeKey] =
allowManualMealPeriodChange;
sysJson[_reservationQueryVerificationDateScopeKey] =
verificationDateScope.code;
});
@@ -13,6 +13,7 @@ import 'package:pj_supermarket_cloud/common/index.dart';
/// 收银、预订和设置页面继续使用原有的图标与强调色样式。
/// [checkConfiguredTime] 开启后会在页面初始化完成时校验当前餐段;[ready] 用于等待调用方恢复自身状态。
/// [currentHourTypeLoader] 允许调用方提供当前配置餐段读取器,未提供时读取本机餐段配置。
/// [allowManualChange] 关闭后禁止人工切换餐段:选择器只读,且与餐段配置不符时静默切换为配置餐段。
Widget appRadioHourTypeDialog(
BuildContext context,
FRadioSelectGroupController<String> controller, {
@@ -21,6 +22,7 @@ Widget appRadioHourTypeDialog(
bool enabled = true,
bool compact = false,
bool checkConfiguredTime = false,
bool allowManualChange = true,
Future<void>? ready,
Future<ProductHourType?> Function()? currentHourTypeLoader,
}) {
@@ -28,13 +30,14 @@ Widget appRadioHourTypeDialog(
return _MealPeriodSelectionGuard(
controller: controller,
ready: ready,
allowManualChange: allowManualChange,
currentHourTypeLoader: currentHourTypeLoader,
selectorBuilder: (context, candidateController) => _buildHourTypeSelector(
context,
candidateController,
key: key,
title: title,
enabled: enabled,
enabled: enabled && allowManualChange,
compact: compact,
),
);
@@ -44,7 +47,7 @@ Widget appRadioHourTypeDialog(
controller,
key: key,
title: title,
enabled: enabled,
enabled: enabled && allowManualChange,
compact: compact,
);
}
@@ -191,6 +194,7 @@ class _MealPeriodSelectionGuard extends StatefulWidget {
required this.controller,
required this.selectorBuilder,
this.ready,
this.allowManualChange = true,
this.currentHourTypeLoader,
});
@@ -200,6 +204,9 @@ class _MealPeriodSelectionGuard extends StatefulWidget {
/// 页面业务初始化信号;为空表示无需等待额外初始化。
final Future<void>? ready;
/// 是否允许人工切换餐段;关闭后忽略人工候选并静默跟随餐段配置。
final bool allowManualChange;
/// 当前时间命中餐段的读取器;测试可注入固定结果。
final Future<ProductHourType?> Function()? currentHourTypeLoader;
@@ -258,7 +265,8 @@ class _MealPeriodSelectionGuardState extends State<_MealPeriodSelectionGuard> {
_selectionRevision++;
_scheduleCheck();
} else if (oldWidget.ready != widget.ready ||
oldWidget.currentHourTypeLoader != widget.currentHourTypeLoader) {
oldWidget.currentHourTypeLoader != widget.currentHourTypeLoader ||
oldWidget.allowManualChange != widget.allowManualChange) {
_scheduleCheck();
}
}
@@ -289,7 +297,9 @@ class _MealPeriodSelectionGuardState extends State<_MealPeriodSelectionGuard> {
if (_syncingCandidate) return;
final candidate = ProductHourType.values.fromCode(values.firstOrNull);
final committedCode = widget.controller.value.firstOrNull;
if (candidate == null ||
// 禁止人工修改时任何候选都不受理,候选显示立即回退为已提交餐段。
if (!widget.allowManualChange ||
candidate == null ||
candidate == ProductHourType.TOTAL ||
candidate.code == committedCode) {
_syncCandidateFromCommitted();
@@ -333,6 +343,9 @@ class _MealPeriodSelectionGuardState extends State<_MealPeriodSelectionGuard> {
}
/// 校验人工候选或当前缓存餐段;人工确认后提交候选,初始化确认后提交配置餐段。
///
/// 禁止人工修改时不弹确认框:与餐段配置不符直接静默切换为配置餐段,
/// 当前时间未命中任何餐段时保留原选择,避免把餐段清空成不可用状态。
Future<void> _checkSelection() async {
if (_checking || !mounted) return;
_checking = true;
@@ -350,6 +363,12 @@ class _MealPeriodSelectionGuardState extends State<_MealPeriodSelectionGuard> {
final configured = await (widget.currentHourTypeLoader?.call() ??
AppSettingTool.getCurrentProductHourType());
if (!mounted || revision != _selectionRevision) return;
if (!widget.allowManualChange) {
if (configured != null && configured != selected) {
_commitSelection(configured);
}
return;
}
if (candidate != null &&
(configured == null || configured == candidate)) {
_commitSelection(candidate);
@@ -1557,7 +1557,7 @@ class _CheckoutCartItem extends StatelessWidget {
}
}
/// 套餐购物车行,默认展示全部组成,并将展开热区与数量步进器分离。
/// 套餐购物车行,默认收起组成明细,并将展开热区与数量步进器分离。
class _CheckoutComboCartItem extends StatefulWidget {
/// 创建一条可由主商品整行切换展开状态的套餐记录。
const _CheckoutComboCartItem({
@@ -1591,8 +1591,8 @@ class _CheckoutComboCartItem extends StatefulWidget {
/// 管理单条套餐的展开状态;状态只属于当前购物车快照条目。
class _CheckoutComboCartItemState extends State<_CheckoutComboCartItem> {
/// 首次打开结算弹框时默认展开,方便收银员直接核对套餐组成。
bool _expanded = true;
/// 首次打开结算弹框时默认收起组成明细,与查询菜单核销弹框的套餐行为一致。
bool _expanded = false;
/// 切换套餐明细;没有组成时不制造无反馈的点击行为。
void _toggleExpanded() {