feat(client): 日期范围改锚定下拉——起止并排双滚轮 + 可键入,不再冻结窗口

- 新增 showDateRangeDropdown:锚定触发控件下方(透明遮罩点外关闭,形态同
  DsMenu 下拉),起始/结束两列并排,各带可输入框(键入/回车/失焦归一
  yyyy-MM-dd)与滚轮,输入↔滚轮双向联动;共用底栏显示当前范围 + 确定,
  起止倒置自动交换;窄屏回落既有两步滚轮弹窗
- WheelDatePanel 加 showFooter/onChanged(滚动即回调),供范围面板复用
- 出入库列表 6 处调用切换:工具栏时间 chip「自定义…」、桌面详搜弹窗、
  窄屏详搜 sheet 的日期区间字段均锚定在各自控件下方

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-04 17:29:21 +08:00
parent 0425939d7e
commit 06dc68245a
3 changed files with 533 additions and 189 deletions
@@ -1713,8 +1713,8 @@ class _AdvancedSearchDialogState extends ConsumerState<_AdvancedSearchDialog> {
));
}
Future<void> _pickDate() async {
final range = await showWheelDateRange(context, initial: _dateRange);
Future<void> _pickDate(BuildContext anchor) async {
final range = await showDateRangeDropdown(anchor, initial: _dateRange);
if (range != null) setState(() => _dateRange = range);
}
@@ -1838,38 +1838,39 @@ class _AdvancedSearchDialogState extends ConsumerState<_AdvancedSearchDialog> {
}
final dateHas = _dateRange != null;
Widget dateField() => InkWell(
onTap: _pickDate,
borderRadius: BorderRadius.circular(AppDims.rMd),
child: Container(
height: 38,
padding: const EdgeInsets.symmetric(horizontal: 11),
decoration: BoxDecoration(
color: t.surface,
border: Border.all(color: t.border),
Widget dateField() => Builder(
builder: (anchorCtx) => InkWell(
onTap: () => _pickDate(anchorCtx),
borderRadius: BorderRadius.circular(AppDims.rMd),
),
child: Row(children: [
Expanded(
child: Text(
dateHas
? '${DateFormat('yyyy-MM-dd').format(_dateRange!.start)} ~ ${DateFormat('yyyy-MM-dd').format(_dateRange!.end)}'
: '全部时间',
style: TextStyle(
fontSize: AppDims.fsBody,
color: dateHas ? t.text : t.faint),
child: Container(
height: 38,
padding: const EdgeInsets.symmetric(horizontal: 11),
decoration: BoxDecoration(
color: t.surface,
border: Border.all(color: t.border),
borderRadius: BorderRadius.circular(AppDims.rMd),
),
child: Row(children: [
Expanded(
child: Text(
dateHas
? '${DateFormat('yyyy-MM-dd').format(_dateRange!.start)} ~ ${DateFormat('yyyy-MM-dd').format(_dateRange!.end)}'
: '全部时间',
style: TextStyle(
fontSize: AppDims.fsBody,
color: dateHas ? t.text : t.faint),
),
),
if (dateHas)
GestureDetector(
onTap: () => setState(() => _dateRange = null),
child: Icon(LucideIcons.x, size: 14, color: t.muted),
)
else
Icon(LucideIcons.calendar, size: 16, color: t.faint),
]),
),
if (dateHas)
GestureDetector(
onTap: () => setState(() => _dateRange = null),
child: Icon(LucideIcons.x, size: 14, color: t.muted),
)
else
Icon(LucideIcons.calendar, size: 16, color: t.faint),
]),
),
);
));
return AlertDialog(
backgroundColor: t.surface,
@@ -2135,8 +2136,8 @@ class _AdvSearchSheetState extends ConsumerState<_AdvSearchSheet> {
));
}
Future<void> _pickDate() async {
final range = await showWheelDateRange(context, initial: _dateRange);
Future<void> _pickDate(BuildContext anchor) async {
final range = await showDateRangeDropdown(anchor, initial: _dateRange);
if (range != null) setState(() => _dateRange = range);
}
@@ -2225,37 +2226,39 @@ class _AdvSearchSheetState extends ConsumerState<_AdvSearchSheet> {
final statusSelIdx = _statusCodes.indexOf(_status);
final dateHas = _dateRange != null;
final dateField = InkWell(
onTap: _pickDate,
borderRadius: BorderRadius.circular(AppDims.rMd),
child: Container(
height: 42,
padding: const EdgeInsets.symmetric(horizontal: 11),
decoration: BoxDecoration(
color: t.surface,
border: Border.all(color: t.border),
borderRadius: BorderRadius.circular(AppDims.rMd),
),
child: Row(children: [
Expanded(
child: Text(
dateHas
? '${DateFormat('yyyy-MM-dd').format(_dateRange!.start)} ~ ${DateFormat('yyyy-MM-dd').format(_dateRange!.end)}'
: '全部时间',
style: TextStyle(
fontSize: AppDims.fsBody, color: dateHas ? t.text : t.faint),
),
),
if (dateHas)
GestureDetector(
onTap: () => setState(() => _dateRange = null),
child: Icon(LucideIcons.x, size: 14, color: t.muted),
)
else
Icon(LucideIcons.calendar, size: 16, color: t.faint),
]),
),
);
final dateField = Builder(
builder: (anchorCtx) => InkWell(
onTap: () => _pickDate(anchorCtx),
borderRadius: BorderRadius.circular(AppDims.rMd),
child: Container(
height: 42,
padding: const EdgeInsets.symmetric(horizontal: 11),
decoration: BoxDecoration(
color: t.surface,
border: Border.all(color: t.border),
borderRadius: BorderRadius.circular(AppDims.rMd),
),
child: Row(children: [
Expanded(
child: Text(
dateHas
? '${DateFormat('yyyy-MM-dd').format(_dateRange!.start)} ~ ${DateFormat('yyyy-MM-dd').format(_dateRange!.end)}'
: '全部时间',
style: TextStyle(
fontSize: AppDims.fsBody,
color: dateHas ? t.text : t.faint),
),
),
if (dateHas)
GestureDetector(
onTap: () => setState(() => _dateRange = null),
child: Icon(LucideIcons.x, size: 14, color: t.muted),
)
else
Icon(LucideIcons.calendar, size: 16, color: t.faint),
]),
),
));
return Column(
mainAxisSize: MainAxisSize.min,