refactor(client): 日期选择统一为扁平组件——输入框+滚轮同宽居中入面板,桌面/移动全覆盖

- wheel_date_picker.dart 重构为统一组件:单日期 showDsDateDropdown(面板=
  可输入框+三列滚轮+今天/确定)、范围 showDateRangeDropdown(起始|结束双窗格
  并排+共用底栏);桌面锚定下拉(透明遮罩不冻结窗口)、窄屏底部 sheet 同内容
  (范围纵排)——同一套窗格两端复用
- 扁平化:输入框与滚轮同宽 248、文字水平垂直居中(mono)、去掉全部阴影/立体
  效果(滚轮外框仅 1px 边框,中心高亮带去描边)
- DsDateCell(录单日期格)与 DatePickerField(登记收支)改为触发器:点选/
  回车打开统一面板,键入在面板内完成;出库工具栏时间 chip 补切到新组件
- 旧 showWheelDatePicker/showWheelDateRange 两步弹窗删除;组件测试改写为
  新契约(触发器+面板键入+词法归一),表单 golden 随形态更新

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-04 17:49:17 +08:00
parent 06dc68245a
commit fb236018f3
17 changed files with 687 additions and 807 deletions
+35 -21
View File
@@ -33,19 +33,7 @@ void main() {
});
group('DatePickerField', () {
testWidgets('渲染可键入框 + 日历图标', (tester) async {
await tester.pumpWidget(MaterialApp(
theme: buildTheme('a'),
home: Scaffold(
body: DatePickerField(value: '2026-06-19', onChanged: (_) {}),
),
));
await tester.pumpAndSettle();
expect(find.byType(TextField), findsOneWidget);
expect(find.byIcon(LucideIcons.calendar), findsOneWidget);
});
testWidgets('初始值回显到输入框', (tester) async {
testWidgets('渲染触发字段:值回显 + 日历图标', (tester) async {
await tester.pumpWidget(MaterialApp(
theme: buildTheme('a'),
home: Scaffold(
@@ -54,30 +42,56 @@ void main() {
));
await tester.pumpAndSettle();
expect(find.text('2026-06-19'), findsOneWidget);
expect(find.byIcon(LucideIcons.calendar), findsOneWidget);
// 字段本体不再内嵌输入框(输入框在下拉面板里)
expect(find.byType(TextField), findsNothing);
});
testWidgets('只输年份归一为 yyyy-01-01', (tester) async {
testWidgets('点选打开统一下拉:面板含输入框 + 滚轮,确定回传', (tester) async {
String? out;
await tester.pumpWidget(MaterialApp(
theme: buildTheme('a'),
home: Scaffold(
body: DatePickerField(onChanged: (v) => out = v),
body: DatePickerField(value: '2026-06-19', onChanged: (v) => out = v),
),
));
await tester.enterText(find.byType(TextField), '2024');
expect(out, '2024-01-01');
await tester.pumpAndSettle();
await tester.tap(find.byIcon(LucideIcons.calendar));
await tester.pumpAndSettle();
// 面板:可输入框 + 三列滚轮 + 今天/确定
expect(find.byType(TextField), findsOneWidget);
expect(find.byType(ListWheelScrollView), findsNWidgets(3));
expect(find.text('今天'), findsOneWidget);
await tester.tap(find.text('确定'));
await tester.pumpAndSettle();
expect(out, '2026-06-19');
});
testWidgets('年月归一为 yyyy-MM-01', (tester) async {
testWidgets('面板内键入日期:归一并经确定回传', (tester) async {
String? out;
await tester.pumpWidget(MaterialApp(
theme: buildTheme('a'),
home: Scaffold(
body: DatePickerField(onChanged: (v) => out = v),
body: DatePickerField(value: '2026-06-19', onChanged: (v) => out = v),
),
));
await tester.enterText(find.byType(TextField), '2024-5');
expect(out, '2024-05-01');
await tester.pumpAndSettle();
await tester.tap(find.byIcon(LucideIcons.calendar));
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextField), '20240512');
await tester.pumpAndSettle();
await tester.tap(find.text('确定'));
await tester.pumpAndSettle();
expect(out, '2024-05-12');
});
testWidgets('normalizeYmd 词法:年/年月补 01', (tester) async {
expect(normalizeYmd('2024'), '2024-01-01');
expect(normalizeYmd('2024-5'), '2024-05-01');
expect(normalizeYmd('2024/05/12'), '2024-05-12');
expect(normalizeYmd('20240229'), '2024-02-29'); // 闰年
expect(normalizeYmd('20260231'), '2026-02-28'); // 超界 clamp
expect(normalizeYmd('abc'), isNull);
});
});
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB