补充选择器逻辑
This commit is contained in:
@@ -6,6 +6,9 @@ PODS:
|
|||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
- flutter_native_splash (2.4.3):
|
- flutter_native_splash (2.4.3):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- image_picker_ios (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- MTBBarcodeScanner (5.0.11)
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
- path_provider_foundation (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
@@ -13,6 +16,9 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- permission_handler_apple (9.3.0):
|
- permission_handler_apple (9.3.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- qr_code_scanner_plus (0.2.6):
|
||||||
|
- Flutter
|
||||||
|
- MTBBarcodeScanner
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@@ -24,12 +30,18 @@ DEPENDENCIES:
|
|||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||||
|
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
|
- qr_code_scanner_plus (from `.symlinks/plugins/qr_code_scanner_plus/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
|
||||||
|
SPEC REPOS:
|
||||||
|
trunk:
|
||||||
|
- MTBBarcodeScanner
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||||
@@ -39,12 +51,16 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter
|
:path: Flutter
|
||||||
flutter_native_splash:
|
flutter_native_splash:
|
||||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||||
|
image_picker_ios:
|
||||||
|
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
|
qr_code_scanner_plus:
|
||||||
|
:path: ".symlinks/plugins/qr_code_scanner_plus/ios"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
@@ -55,9 +71,12 @@ SPEC CHECKSUMS:
|
|||||||
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
|
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||||
|
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
||||||
|
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
|
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
|
||||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||||
|
qr_code_scanner_plus: 7e087021bc69873140e0754750eb87d867bed755
|
||||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||||
import 'package:get_storage/get_storage.dart';
|
import 'package:get_storage/get_storage.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
@@ -12,7 +13,11 @@ import 'pages/login/view.dart';
|
|||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
WidgetsBinding widgetsBinding = await init(isDebug: true, logTag: '小羚羚');
|
WidgetsBinding widgetsBinding = await init(
|
||||||
|
isDebug: true,
|
||||||
|
logTag: '小羚羚',
|
||||||
|
supportedLocales: [Locale('zh', 'CN')],
|
||||||
|
);
|
||||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||||
|
|
||||||
await GetStorage.init();
|
await GetStorage.init();
|
||||||
@@ -38,9 +43,14 @@ void main() async {
|
|||||||
title: '小羚羚',
|
title: '小羚羚',
|
||||||
// 首页入口
|
// 首页入口
|
||||||
home: HomePage(),
|
home: HomePage(),
|
||||||
// 推荐使用命名路由,如果配置好了可以取消下面两行的注释
|
//组件国际化
|
||||||
// initialRoute: AppPages.INITIAL,
|
fallbackLocale: Locale('zh', 'CN'),
|
||||||
// getPages: AppPages.routes,
|
supportedLocales: [Locale('zh', 'CN')],
|
||||||
|
localizationsDelegates: const [
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
|
||||||
// Builder
|
// Builder
|
||||||
builder: (context, widget) {
|
builder: (context, widget) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:getx_scaffold/common/common.dart';
|
import 'package:getx_scaffold/common/common.dart';
|
||||||
@@ -9,117 +10,283 @@ import 'package:ln_jq_app/common/model/vehicle_info.dart';
|
|||||||
import 'package:ln_jq_app/pages/b_page/site/controller.dart';
|
import 'package:ln_jq_app/pages/b_page/site/controller.dart';
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
|
|
||||||
import '../../../common/styles/theme.dart'; // 用于日期格式化
|
import '../../../common/styles/theme.dart';
|
||||||
|
|
||||||
class ReservationController extends GetxController with BaseControllerMixin {
|
class ReservationController extends GetxController with BaseControllerMixin {
|
||||||
@override
|
@override
|
||||||
String get builderId => 'reservation';
|
String get builderId => 'reservation';
|
||||||
|
|
||||||
// 【修改】使用 Rx 变量,让 GetX 的 Obx/GetX 能够自动监听变化,UI更新更简单
|
|
||||||
// 日期,默认为今天
|
|
||||||
final Rx<DateTime> selectedDate = DateTime.now().obs;
|
final Rx<DateTime> selectedDate = DateTime.now().obs;
|
||||||
|
|
||||||
// 开始时间,默认为当前时分
|
|
||||||
final Rx<TimeOfDay> startTime = TimeOfDay.now().obs;
|
final Rx<TimeOfDay> startTime = TimeOfDay.now().obs;
|
||||||
|
|
||||||
// 结束时间,默认为开始时间后30分钟
|
|
||||||
final Rx<TimeOfDay> endTime = TimeOfDay.fromDateTime(
|
final Rx<TimeOfDay> endTime = TimeOfDay.fromDateTime(
|
||||||
DateTime.now().add(const Duration(minutes: 30)),
|
DateTime.now().add(const Duration(minutes: 30)),
|
||||||
).obs;
|
).obs;
|
||||||
|
|
||||||
// 预约氢量
|
|
||||||
final TextEditingController amountController = TextEditingController();
|
final TextEditingController amountController = TextEditingController();
|
||||||
|
|
||||||
// 车牌号
|
|
||||||
TextEditingController plateNumberController = TextEditingController();
|
TextEditingController plateNumberController = TextEditingController();
|
||||||
|
|
||||||
// 加氢站
|
final List<String> stationOptions = ['诚志AP银河路加氢站', '站点B', '站点C'];
|
||||||
final List<String> stationOptions = [
|
|
||||||
'诚志AP银河路加氢站',
|
|
||||||
'站点B',
|
|
||||||
'站点C',
|
|
||||||
'站点C',
|
|
||||||
'站点C',
|
|
||||||
'站点C',
|
|
||||||
'站点C',
|
|
||||||
];
|
|
||||||
final Rx<String> selectedStation = '诚志AP银河路加氢站'.obs;
|
final Rx<String> selectedStation = '诚志AP银河路加氢站'.obs;
|
||||||
|
|
||||||
// --- 用于UI显示的格式化字符串 (Getters) ---
|
|
||||||
String get formattedDate => DateFormat('yyyy-MM-dd').format(selectedDate.value);
|
String get formattedDate => DateFormat('yyyy-MM-dd').format(selectedDate.value);
|
||||||
|
|
||||||
// 使用 context-aware 的 format 方法
|
String get formattedStartTime => _formatTimeOfDay(startTime.value);
|
||||||
String get formattedStartTime => startTime.value.format(Get.context!);
|
|
||||||
|
|
||||||
String get formattedEndTime => endTime.value.format(Get.context!);
|
String get formattedEndTime => _formatTimeOfDay(endTime.value);
|
||||||
|
|
||||||
/// --- 交互方法 ---
|
void pickDate(BuildContext context) {
|
||||||
|
DateTime tempDate = selectedDate.value;
|
||||||
|
|
||||||
/// 【修改】显示 Flutter 内置的日期选择器
|
Get.bottomSheet(
|
||||||
void pickDate(BuildContext context) async {
|
Container(
|
||||||
// 改为 async
|
height: 300,
|
||||||
final DateTime? pickedDate = await showDatePicker(
|
padding: const EdgeInsets.only(top: 6.0),
|
||||||
context: context,
|
decoration: const BoxDecoration(
|
||||||
initialDate: selectedDate.value,
|
color: Colors.white,
|
||||||
firstDate: DateTime.now(),
|
borderRadius: BorderRadius.only(
|
||||||
// 只能从今天开始选
|
topLeft: Radius.circular(16),
|
||||||
lastDate: DateTime.now().add(const Duration(days: 365)),
|
topRight: Radius.circular(16),
|
||||||
// 假设最多预约一年内
|
),
|
||||||
helpText: '选择预约日期',
|
),
|
||||||
confirmText: '确定',
|
child: Column(
|
||||||
cancelText: '取消',
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
CupertinoButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: const Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(color: CupertinoColors.systemGrey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CupertinoButton(
|
||||||
|
onPressed: () {
|
||||||
|
selectedDate.value = tempDate;
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
child: const Text(
|
||||||
|
'确认',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppTheme.themeColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 1, color: Color(0xFFE5E5E5)),
|
||||||
|
Expanded(
|
||||||
|
child: CupertinoDatePicker(
|
||||||
|
mode: CupertinoDatePickerMode.date,
|
||||||
|
initialDateTime:
|
||||||
|
selectedDate.value.isBefore(
|
||||||
|
DateTime(
|
||||||
|
DateTime.now().year,
|
||||||
|
DateTime.now().month,
|
||||||
|
DateTime.now().day,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
? DateTime(
|
||||||
|
DateTime.now().year,
|
||||||
|
DateTime.now().month,
|
||||||
|
DateTime.now().day,
|
||||||
|
)
|
||||||
|
: selectedDate.value,
|
||||||
|
|
||||||
|
// 【核心】设置最小可选日期为“今天凌晨0点”
|
||||||
|
minimumDate: DateTime(
|
||||||
|
DateTime.now().year,
|
||||||
|
DateTime.now().month,
|
||||||
|
DateTime.now().day,
|
||||||
|
),
|
||||||
|
maximumDate: DateTime.now().add(const Duration(days: 365)),
|
||||||
|
onDateTimeChanged: (DateTime newDate) {
|
||||||
|
tempDate = newDate;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (pickedDate != null && pickedDate != selectedDate.value) {
|
|
||||||
selectedDate.value = pickedDate;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 【修改】显示 Flutter 内置的时间选择器
|
void pickTime(BuildContext context, bool isStartTime) {
|
||||||
void pickTime(BuildContext context, bool isStartTime) async {
|
// 1. 确定当前操作的时间和初始值
|
||||||
// 改为 async
|
|
||||||
TimeOfDay initialTime = isStartTime ? startTime.value : endTime.value;
|
TimeOfDay initialTime = isStartTime ? startTime.value : endTime.value;
|
||||||
|
DateTime now = DateTime.now();
|
||||||
|
|
||||||
final TimeOfDay? pickedTime = await showTimePicker(
|
// 2.【核心】计算最小可选时间 (Minimum Date)
|
||||||
context: context,
|
DateTime? minimumDateTime; // 默认为 null,即可选任意时间
|
||||||
initialTime: initialTime,
|
|
||||||
helpText: isStartTime ? '选择开始时间' : '选择结束时间',
|
// 获取当前选择的日期(年月日)
|
||||||
confirmText: '确定',
|
final selectedDay = DateTime(
|
||||||
cancelText: '取消',
|
selectedDate.value.year,
|
||||||
|
selectedDate.value.month,
|
||||||
|
selectedDate.value.day,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (pickedTime != null) {
|
// 获取今天的日期(年月日)
|
||||||
if (isStartTime) {
|
final today = DateTime(now.year, now.month, now.day);
|
||||||
startTime.value = pickedTime;
|
|
||||||
// 如果新的开始时间晚于或等于结束时间,自动将结束时间设置为开始时间后30分钟
|
if (isStartTime) {
|
||||||
if ((pickedTime.hour * 60 + pickedTime.minute) >=
|
// 如果是选择【开始时间】并且日期是【今天】
|
||||||
(endTime.value.hour * 60 + endTime.value.minute)) {
|
if (selectedDay.isAtSameMomentAs(today)) {
|
||||||
final newDateTime = DateTime(
|
minimumDateTime = now; // 最小可选时间就是现在
|
||||||
selectedDate.value.year,
|
}
|
||||||
selectedDate.value.month,
|
} else {
|
||||||
selectedDate.value.day,
|
// 如果是选择【结束时间】
|
||||||
pickedTime.hour,
|
// 将开始时间转换为 DateTime 对象
|
||||||
pickedTime.minute,
|
final startDateTime = DateTime(
|
||||||
).add(const Duration(minutes: 30));
|
selectedDate.value.year,
|
||||||
endTime.value = TimeOfDay.fromDateTime(newDateTime);
|
selectedDate.value.month,
|
||||||
}
|
selectedDate.value.day,
|
||||||
} else {
|
startTime.value.hour,
|
||||||
// 确保结束时间不早于开始时间
|
startTime.value.minute,
|
||||||
if ((pickedTime.hour * 60 + pickedTime.minute) >
|
);
|
||||||
(startTime.value.hour * 60 + startTime.value.minute)) {
|
|
||||||
endTime.value = pickedTime;
|
// 结束时间的最小值必须晚于开始时间
|
||||||
} else {
|
minimumDateTime = startDateTime;
|
||||||
// 如果选择了更早的时间,给出提示
|
|
||||||
Get.snackbar(
|
// 如果日期是今天,并且开始时间早于现在,那么结束时间的最小值也应该是现在
|
||||||
'时间选择错误',
|
if (selectedDay.isAtSameMomentAs(today) && startDateTime.isBefore(now)) {
|
||||||
'结束时间必须晚于开始时间',
|
minimumDateTime = now;
|
||||||
snackPosition: SnackPosition.BOTTOM,
|
|
||||||
backgroundColor: Colors.red,
|
|
||||||
colorText: Colors.white,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. 准备 DatePicker 的初始显示时间
|
||||||
|
DateTime initialDateTime = DateTime(
|
||||||
|
selectedDate.value.year,
|
||||||
|
selectedDate.value.month,
|
||||||
|
selectedDate.value.day,
|
||||||
|
initialTime.hour,
|
||||||
|
initialTime.minute,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 确保初始时间不早于最小时间
|
||||||
|
if (minimumDateTime != null && initialDateTime.isBefore(minimumDateTime)) {
|
||||||
|
initialDateTime = minimumDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime tempTime = initialDateTime;
|
||||||
|
|
||||||
|
Get.bottomSheet(
|
||||||
|
Container(
|
||||||
|
height: 300,
|
||||||
|
padding: const EdgeInsets.only(top: 6.0),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(16),
|
||||||
|
topRight: Radius.circular(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
CupertinoButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: const Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(color: CupertinoColors.systemGrey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CupertinoButton(
|
||||||
|
onPressed: () {
|
||||||
|
final pickedTempTime = TimeOfDay.fromDateTime(tempTime);
|
||||||
|
|
||||||
|
// 验证条件1:不能选择过去的时间
|
||||||
|
// 这个验证只在选择【今天】的【开始时间】或【结束时间】时有意义
|
||||||
|
final selectedDay = DateTime(
|
||||||
|
selectedDate.value.year,
|
||||||
|
selectedDate.value.month,
|
||||||
|
selectedDate.value.day,
|
||||||
|
);
|
||||||
|
final today = DateTime(
|
||||||
|
DateTime.now().year,
|
||||||
|
DateTime.now().month,
|
||||||
|
DateTime.now().day,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selectedDay.isAtSameMomentAs(today) &&
|
||||||
|
tempTime.isBefore(DateTime.now())) {
|
||||||
|
showToast('不能选择过去的时间');
|
||||||
|
return; // 中断执行,不关闭弹窗
|
||||||
|
}
|
||||||
|
|
||||||
|
//结束时间必须晚于开始时间
|
||||||
|
if (isStartTime) {
|
||||||
|
// 和【已有的结束时间】比较
|
||||||
|
final pickedStartInMinutes =
|
||||||
|
pickedTempTime.hour * 60 + pickedTempTime.minute;
|
||||||
|
final endInMinutes =
|
||||||
|
endTime.value.hour * 60 + endTime.value.minute;
|
||||||
|
|
||||||
|
if (pickedStartInMinutes >= endInMinutes) {
|
||||||
|
// 在这里,我们不提示错误,而是智能地调整结束时间
|
||||||
|
startTime.value = pickedTempTime;
|
||||||
|
final newEndDateTime = tempTime.add(
|
||||||
|
const Duration(minutes: 30),
|
||||||
|
);
|
||||||
|
endTime.value = TimeOfDay.fromDateTime(newEndDateTime);
|
||||||
|
} else {
|
||||||
|
// 合法,直接设置开始时间
|
||||||
|
startTime.value = pickedTempTime;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果当前正在设置【结束时间】,我们来和【已有的开始时间】比较
|
||||||
|
final pickedEndInMinutes =
|
||||||
|
pickedTempTime.hour * 60 + pickedTempTime.minute;
|
||||||
|
final startInMinutes =
|
||||||
|
startTime.value.hour * 60 + startTime.value.minute;
|
||||||
|
|
||||||
|
if (pickedEndInMinutes <= startInMinutes) {
|
||||||
|
showToast('结束时间必须晚于开始时间');
|
||||||
|
return; // 中断执行,不关闭弹窗
|
||||||
|
} else {
|
||||||
|
// 合法,直接设置结束时间
|
||||||
|
endTime.value = pickedTempTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
child: const Text(
|
||||||
|
'确认',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppTheme.themeColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 1, color: Color(0xFFE5E5E5)),
|
||||||
|
Expanded(
|
||||||
|
child: CupertinoDatePicker(
|
||||||
|
mode: CupertinoDatePickerMode.time,
|
||||||
|
use24hFormat: true,
|
||||||
|
initialDateTime: initialDateTime,
|
||||||
|
minimumDate: minimumDateTime,
|
||||||
|
onDateTimeChanged: (DateTime newTime) {
|
||||||
|
tempTime = newTime;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 提交预约
|
/// 提交预约
|
||||||
@@ -150,7 +317,7 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
var response = await HttpService.to.post(
|
var response = await HttpService.to.post(
|
||||||
"appointment/orderAddHyd/driverOrderPage",
|
"appointment/orderAddHyd/driverOrderPage",
|
||||||
data: {
|
data: {
|
||||||
'phone': StorageService.to.userId, // 使用从 renderData 中获取到的 name
|
'phone': StorageService.to.phone, // 使用从 renderData 中获取到的 name
|
||||||
'pageNum': 1,
|
'pageNum': 1,
|
||||||
'pageSize': 30, // 暂时不考虑分页,一次获取30条
|
'pageSize': 30, // 暂时不考虑分页,一次获取30条
|
||||||
},
|
},
|
||||||
@@ -189,7 +356,7 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
Container(
|
Container(
|
||||||
height: Get.height * 0.45,
|
height: Get.height * 0.55,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
@@ -199,6 +366,7 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
//标题
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 15, 20, 15),
|
padding: const EdgeInsets.fromLTRB(20, 15, 20, 15),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -207,7 +375,7 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
const Text(
|
const Text(
|
||||||
'我的预约',
|
'我的预约',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 15,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.black87,
|
color: Colors.black87,
|
||||||
),
|
),
|
||||||
@@ -228,83 +396,91 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
),
|
),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.builder(
|
child: !hasReservationData
|
||||||
padding: const EdgeInsets.all(16),
|
? Container(
|
||||||
itemCount: reservationList.length,
|
margin: EdgeInsets.only(top: 40),
|
||||||
itemBuilder: (context, index) {
|
child: TextX.bodyLarge('暂无预约', weight: FontWeight.w500),
|
||||||
final ReservationModel reservation = reservationList[index];
|
)
|
||||||
return Card(
|
: ListView.builder(
|
||||||
margin: const EdgeInsets.only(bottom: 12.0),
|
padding: const EdgeInsets.all(16),
|
||||||
elevation: 1,
|
itemCount: reservationList.length,
|
||||||
shape: RoundedRectangleBorder(
|
itemBuilder: (context, index) {
|
||||||
borderRadius: BorderRadius.circular(12),
|
final ReservationModel reservation = reservationList[index];
|
||||||
),
|
return Card(
|
||||||
child: Padding(
|
color: Color.fromARGB(255, 246, 248, 250),
|
||||||
padding: EdgeInsets.all(10),
|
margin: const EdgeInsets.only(bottom: 12.0),
|
||||||
child: Column(
|
elevation: 1,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
shape: RoundedRectangleBorder(
|
||||||
children: [
|
borderRadius: BorderRadius.circular(12),
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 10,
|
|
||||||
vertical: 5,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFE6F7FF), // Light blue background
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
border: Border.all(
|
|
||||||
color: const Color(0xFF91D5FF),
|
|
||||||
), // Blue border
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
reservation.stateName,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Color(0xFF1890FF),
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
|
||||||
vertical: 4,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(
|
|
||||||
0xFFFFF7E6,
|
|
||||||
), // Light orange background
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
reservation.addStatusName,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Color(0xFFFA8C16),
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
child: Padding(
|
||||||
_buildDetailRow('车牌号:', reservation.plateNumber),
|
padding: EdgeInsets.all(10),
|
||||||
_buildDetailRow('预约日期:', reservation.date),
|
child: Column(
|
||||||
_buildDetailRow('预约氢量:', reservation.hydAmount),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
_buildDetailRow('加氢站:', reservation.stationName),
|
children: [
|
||||||
_buildDetailRow('开始时间:', reservation.startTime),
|
Row(
|
||||||
_buildDetailRow('结束时间:', reservation.endTime),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
_buildDetailRow('联系人:', reservation.contacts),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
_buildDetailRow('联系电话:', reservation.phone),
|
children: [
|
||||||
],
|
Container(
|
||||||
),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 10,
|
||||||
|
vertical: 5,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(
|
||||||
|
0xFFE6F7FF,
|
||||||
|
), // Light blue background
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(0xFF91D5FF),
|
||||||
|
), // Blue border
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
reservation.stateName,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Color(0xFF1890FF),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(
|
||||||
|
0xFFFFF7E6,
|
||||||
|
), // Light orange background
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
reservation.addStatusName,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Color(0xFFFA8C16),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
_buildDetailRow('车牌号:', reservation.plateNumber),
|
||||||
|
_buildDetailRow('预约日期:', reservation.date),
|
||||||
|
_buildDetailRow('预约氢量:', reservation.hydAmount),
|
||||||
|
_buildDetailRow('加氢站:', reservation.stationName),
|
||||||
|
_buildDetailRow('开始时间:', reservation.startTime),
|
||||||
|
_buildDetailRow('结束时间:', reservation.endTime),
|
||||||
|
_buildDetailRow('联系人:', reservation.contacts),
|
||||||
|
_buildDetailRow('联系电话:', reservation.phone),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -344,8 +520,6 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
String name = "";
|
String name = "";
|
||||||
String leftHydrogen = "0";
|
String leftHydrogen = "0";
|
||||||
String workEfficiency = "0";
|
String workEfficiency = "0";
|
||||||
|
|
||||||
//累计数据
|
|
||||||
String fillingWeight = "0";
|
String fillingWeight = "0";
|
||||||
String fillingTimes = "0";
|
String fillingTimes = "0";
|
||||||
String plateNumber = "";
|
String plateNumber = "";
|
||||||
@@ -355,7 +529,6 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
void onInit() {
|
void onInit() {
|
||||||
phone = StorageService.to.phone ?? "";
|
phone = StorageService.to.phone ?? "";
|
||||||
name = StorageService.to.name ?? "";
|
name = StorageService.to.name ?? "";
|
||||||
|
|
||||||
getUserBindCarInfo();
|
getUserBindCarInfo();
|
||||||
getSiteList();
|
getSiteList();
|
||||||
super.onInit();
|
super.onInit();
|
||||||
@@ -457,6 +630,12 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _formatTimeOfDay(TimeOfDay time) {
|
||||||
|
final hour = time.hour.toString().padLeft(2, '0');
|
||||||
|
final minute = time.minute.toString().padLeft(2, '0');
|
||||||
|
return '$hour:$minute';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
amountController.dispose();
|
amountController.dispose();
|
||||||
|
|||||||
@@ -310,6 +310,11 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "5.0.0"
|
||||||
|
flutter_localizations:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_native_splash:
|
flutter_native_splash:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -489,7 +494,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.2"
|
version: "0.2.2"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ dependencies:
|
|||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
getx_scaffold: ^0.2.2
|
getx_scaffold: ^0.2.2
|
||||||
|
flutter_localizations:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
encrypt: ^5.0.3
|
encrypt: ^5.0.3
|
||||||
get_storage: ^2.1.1
|
get_storage: ^2.1.1
|
||||||
|
|
||||||
@@ -45,6 +48,8 @@ dependencies:
|
|||||||
image: ^4.5.4
|
image: ^4.5.4
|
||||||
zxing_lib: ^1.1.4
|
zxing_lib: ^1.1.4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
@@ -52,6 +57,9 @@ dev_dependencies:
|
|||||||
flutter_lints: ^5.0.0
|
flutter_lints: ^5.0.0
|
||||||
|
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
intl: 0.19.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|||||||
Reference in New Issue
Block a user