diff --git a/lib/pages/terminal/menu_query/widgets/query_meal_slot_selector.dart b/lib/pages/terminal/menu_query/widgets/query_meal_slot_selector.dart deleted file mode 100644 index 872096b..0000000 --- a/lib/pages/terminal/menu_query/widgets/query_meal_slot_selector.dart +++ /dev/null @@ -1,176 +0,0 @@ -import 'dart:async'; - -import 'package:ducafe_ui_core/ducafe_ui_core.dart'; -import 'package:flutter/material.dart'; -import 'package:forui/forui.dart'; - -import 'package:pj_supermarket_cloud/common/index.dart'; - -/// 查询菜单的低强调餐段选择器,仅在操作员主动点开时展示完整餐段选项。 -class QueryMealSlotSelector extends StatefulWidget { - /// 创建餐段选择器;餐段状态与持久化由页面 Controller 负责,组件只处理展示和浮层交互。 - const QueryMealSlotSelector({ - super.key, - required this.mealSlots, - required this.selected, - required this.enabled, - required this.onSelected, - }); - - /// 查询前允许选择的餐段列表,顺序遵循后端稳定业务配置。 - final List mealSlots; - - /// 当前已选择并在触发器中持续可见的餐段。 - final ReservationMealSlot selected; - - /// 查询请求进行中为 `false`,此时关闭浮层并禁止改变当前轮次的目标餐段。 - final bool enabled; - - /// 操作员选择餐段后的回调,状态更新及本机缓存写入由 Controller 统一处理。 - final ValueChanged onSelected; - - /// 创建管理 Forui 浮层生命周期的组件状态。 - @override - State createState() => _QueryMealSlotSelectorState(); -} - -/// 餐段选择浮层状态,确保查询锁定或组件销毁时不会遗留终端 Overlay。 -class _QueryMealSlotSelectorState extends State - with SingleTickerProviderStateMixin { - /// Forui 浮层控制器,动画时长遵循终端 150-300ms 的轻量交互节奏。 - late final FPopoverController _popoverController; - - /// 初始化并持有当前组件唯一的浮层控制器。 - @override - void initState() { - super.initState(); - _popoverController = FPopoverController( - vsync: this, - animationDuration: const Duration(milliseconds: 160), - ); - } - - /// 查询状态切换为禁用时主动收起浮层,避免仍可点击旧餐段选项。 - @override - void didUpdateWidget(covariant QueryMealSlotSelector oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.enabled && !widget.enabled && _popoverController.shown) { - unawaited(_popoverController.hide()); - } - } - - /// 构建单行弱提示触发器,并通过 Forui 浮层渐进展示完整餐段列表。 - @override - Widget build(BuildContext context) { - final trigger = [ - Text( - '餐段', - style: TextStyle( - color: XfjDesignTokens.muted, - fontSize: 12.sp, - fontWeight: FontWeight.w600, - ), - ), - const Spacer(), - Text( - widget.selected.title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: widget.enabled ? XfjDesignTokens.ink : XfjDesignTokens.muted, - fontSize: 14.sp, - fontWeight: FontWeight.w700, - ), - ), - FIcon( - FAssets.icons.chevronsUpDown, - color: XfjDesignTokens.muted, - size: 16.w, - ), - ] - .toRowSpace(space: 6.w) - .padding(horizontal: 12.w) - // 外观保持紧凑,但完整行仍满足 Android 终端 48dp 的触控下限。 - .constrained(minHeight: 48) - .decorated( - color: XfjDesignTokens.canvas, - border: Border.all(color: XfjDesignTokens.line), - borderRadius: BorderRadius.circular(XfjDesignTokens.controlRadius), - ); - - return FPopoverMenu( - popoverController: _popoverController, - menuAnchor: Alignment.topRight, - childAnchor: Alignment.bottomRight, - directionPadding: true, - semanticLabel: '预查询餐段选项', - divider: FTileDivider.none, - menu: >[ - FTileGroup( - divider: FTileDivider.none, - children: [ - ...widget.mealSlots.map(_mealSlotTile), - ], - ), - ], - child: Semantics( - key: const ValueKey('reservation-meal-slot-trigger'), - button: true, - enabled: widget.enabled, - label: '预查询餐段', - value: widget.selected.title, - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: widget.enabled && widget.mealSlots.isNotEmpty - ? _popoverController.toggle - : null, - borderRadius: BorderRadius.circular( - XfjDesignTokens.controlRadius, - ), - child: trigger, - ), - ), - ), - ); - } - - /// 构建浮层中的单个餐段选项,勾选图标与读屏文案共同表达当前选择。 - FTile _mealSlotTile(ReservationMealSlot slot) { - final selected = slot == widget.selected; - return FTile( - key: ValueKey('reservation-meal-slot-option-${slot.code}'), - semanticLabel: selected ? '${slot.title},已选择' : '选择${slot.title}', - title: Text( - slot.title, - style: TextStyle( - color: selected ? XfjDesignTokens.primary : XfjDesignTokens.ink, - fontSize: 14.sp, - fontWeight: selected ? FontWeight.w700 : FontWeight.w600, - ), - ).alignment(Alignment.centerLeft).constrained(minHeight: 48), - suffixIcon: selected - ? FIcon( - FAssets.icons.check, - color: XfjDesignTokens.primary, - size: 18.w, - ) - : SizedBox(width: 18.w), - onPress: () => _select(slot), - ); - } - - /// 提交本次选择并立即收起浮层;相同餐段也收起但不触发重复缓存写入。 - void _select(ReservationMealSlot slot) { - if (!widget.enabled) return; - if (slot != widget.selected) widget.onSelected(slot); - unawaited(_popoverController.hide()); - } - - /// 释放动画资源,避免多次进出查询菜单后累积 Ticker。 - @override - void dispose() { - _popoverController.dispose(); - super.dispose(); - } -}