司机预约时间调整

This commit is contained in:
2026-02-06 14:16:26 +08:00
parent f68c2d0938
commit 756bf53cf5
5 changed files with 64 additions and 242 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 545 KiB

View File

@@ -4,7 +4,9 @@ class StationModel {
final String address;
final String price;
final String siteStatusName; // 例如 "维修中"
final int isSelect; // 新增字段 1是可用 0是不可用
final int isSelect; // 1是可用 0是不可用
final String startBusiness; // 新增:可预约最早开始时间,如 "06:00:00"
final String endBusiness; // 新增:可预约最晚结束时间,如 "22:00:00"
StationModel({
required this.hydrogenId,
@@ -13,9 +15,10 @@ class StationModel {
required this.price,
required this.siteStatusName,
required this.isSelect,
required this.startBusiness,
required this.endBusiness,
});
// 从 JSON map 创建对象的工厂构造函数
factory StationModel.fromJson(Map<String, dynamic> json) {
return StationModel(
hydrogenId: json['hydrogenId'] ?? '',
@@ -23,7 +26,9 @@ class StationModel {
address: json['address'] ?? '地址未知',
price: json['price']?.toString() ?? '0.00',
siteStatusName: json['siteStatusName'] ?? '',
isSelect: json['isSelect'] as int? ?? 0, // 新增字段的解析,默认为 0
isSelect: json['isSelect'] as int? ?? 0,
startBusiness: json['startBusiness'] ?? '00:00:00', // 默认全天
endBusiness: json['endBusiness'] ?? '23:59:59', // 默认全天
);
}
}

View File

@@ -69,239 +69,34 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
String get formattedTimeSlot =>
'${_formatTimeOfDay(startTime.value)} - ${_formatTimeOfDay(endTime.value)}';
void pickDate(BuildContext context) {
DateTime tempDate = selectedDate.value;
// 获取今天的日期 (不含时间)
final DateTime today = DateTime(
DateTime.now().year,
DateTime.now().month,
DateTime.now().day,
);
//计算明天的日期
final DateTime tomorrow = today.add(const Duration(days: 1));
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 bool isChangingToToday =
tempDate.isAtSameMomentAs(today) &&
!selectedDate.value.isAtSameMomentAs(today);
final bool isDateChanged = !tempDate.isAtSameMomentAs(
selectedDate.value,
);
// 更新选中的日期
selectedDate.value = tempDate;
Get.back(); // 先关闭弹窗
// 如果日期发生了变化,则重置时间
if (isDateChanged) {
resetTimeForSelectedDate();
}
},
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,
minimumDate: today,
// 最小可选日期为今天
maximumDate: tomorrow,
// 最大可选日期为明天
// ---------------------
onDateTimeChanged: (DateTime newDate) {
tempDate = newDate;
},
),
),
],
),
),
backgroundColor: Colors.transparent,
);
}
void resetTimeForSelectedDate() {
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
// 判断新选择的日期是不是今天
// 1. 获取当前站点
final station = stationOptions.firstWhereOrNull(
(s) => s.hydrogenId == selectedStationId.value,
);
if (station == null) return;
// 2. 解析营业开始和结束的小时
final bizStartHour = int.tryParse(station.startBusiness.split(':')[0]) ?? 0;
final bizEndHour = int.tryParse(station.endBusiness.split(':')[0]) ?? 23;
if (selectedDate.value.isAtSameMomentAs(today)) {
// 如果是今天,就将时间重置为当前时间所在的半小时区间
startTime.value = _calculateInitialStartTime(now);
endTime.value = TimeOfDay.fromDateTime(
_getDateTimeFromTimeOfDay(startTime.value).add(const Duration(minutes: 30)),
);
// 如果是今天:起始时间 = max(当前小时, 营业开始小时),且上限为营业结束小时
int targetHour = now.hour;
if (targetHour < bizStartHour) targetHour = bizStartHour;
if (targetHour > bizEndHour) targetHour = bizEndHour;
startTime.value = TimeOfDay(hour: targetHour, minute: 0);
} else {
// 如果是明天(或其他未来日期),则可以将时间重置为一天的最早可用时间,例如 00:00
startTime.value = const TimeOfDay(hour: 0, minute: 0);
endTime.value = const TimeOfDay(hour: 0, minute: 30);
}
}
///60 分钟为间隔 时间选择器
void pickTime(BuildContext context) {
final now = DateTime.now();
final isToday =
selectedDate.value.year == now.year &&
selectedDate.value.month == now.month &&
selectedDate.value.day == now.day;
final List<TimeSlot> availableSlots = [];
for (int i = 0; i < 24; i++) {
// 每次增加 60 分钟
final startMinutes = i * 60;
final endMinutes = startMinutes + 60;
final startTime = TimeOfDay(hour: startMinutes ~/ 60, minute: startMinutes % 60);
// 注意endMinutes % 60 始终为 0因为间隔是整小时
final endTime = TimeOfDay(hour: (endMinutes ~/ 60) % 24, minute: endMinutes % 60);
// 如果不是今天,所有时间段都有效
if (!isToday) {
availableSlots.add(TimeSlot(startTime, endTime));
} else {
// 如果是今天,需要判断该时间段是否可选
// 创建时间段的结束时间对象
final slotEndDateTime = DateTime(
selectedDate.value.year,
selectedDate.value.month,
selectedDate.value.day,
endTime.hour,
endTime.minute,
);
// 注意:如果是跨天的 00:00 (例如 23:00 - 00:00),需要将日期加一天,否则 isAfter 判断会出错
// 但由于我们用的是 endTime.hour % 24当变成 0 时,日期还是 selectedDate
// 这里做一个特殊处理:如果 endTime 是 00:00意味着它实际上是明天的开始
DateTime realEndDateTime = slotEndDateTime;
if (endTime.hour == 0 && endTime.minute == 0) {
realEndDateTime = slotEndDateTime.add(const Duration(days: 1));
}
// 只要时间段的结束时间晚于当前时间,这个时间段就是可预约的
if (realEndDateTime.isAfter(now)) {
availableSlots.add(TimeSlot(startTime, endTime));
}
}
// 如果是明天:起始时间直接重置为营业开始小时
startTime.value = TimeOfDay(hour: bizStartHour, minute: 0);
}
if (availableSlots.isEmpty) {
showToast('今天已没有可预约的时间段');
return;
}
// 查找当前选中的时间对应的新列表中的索引
int initialItem = availableSlots.indexWhere(
(slot) => slot.start.hour == startTime.value.hour,
);
if (initialItem == -1) {
initialItem = 0;
}
TimeSlot tempSlot = availableSlots[initialItem];
final FixedExtentScrollController scrollController = FixedExtentScrollController(
initialItem: initialItem,
);
Get.bottomSheet(
Container(
height: 300,
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: () {
startTime.value = tempSlot.start;
endTime.value = tempSlot.end;
Get.back();
},
child: const Text(
'确认',
style: TextStyle(
color: AppTheme.themeColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
const Divider(height: 1, color: Color(0xFFE5E5E5)),
Expanded(
child: CupertinoPicker(
scrollController: scrollController,
itemExtent: 40.0,
onSelectedItemChanged: (index) {
tempSlot = availableSlots[index];
},
children: availableSlots
.map((slot) => Center(child: Text(slot.display)))
.toList(),
),
),
],
),
),
backgroundColor: Colors.transparent,
);
// 结束时间默认顺延1小时
endTime.value = TimeOfDay(hour: (startTime.value.hour + 1) % 24, minute: 0);
}
// 用于存储上一次成功预约的信息
@@ -727,7 +522,7 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
}
void getSiteList() async {
if (StorageService.to.phone == "13344444444") {
if (StorageService.to.phone == "13888888888") {
//该账号给stationOptions手动添加一个数据
final testStation = StationModel(
hydrogenId: '1142167389150920704',
@@ -737,7 +532,9 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
// 价格
siteStatusName: '营运中',
// 状态
isSelect: 1, // 默认可选
isSelect: 1,
startBusiness: '08:00:00',
endBusiness: '20:00:00', // 默认可选
);
// 使用 assignAll 可以确保列表只包含这个测试数据
stationOptions.assignAll([testStation]);

View File

@@ -457,8 +457,27 @@ class ReservationPage extends GetView<C_ReservationController> {
/// 时间 Slider 选择器
Widget _buildTimeSlider(BuildContext context) {
return Obx(() {
// 这里的逻辑对应 Controller 中的 24 小时可用 Slot
int currentIdx = controller.startTime.value.hour;
// 获取当前小时作为滑块值 (0-23)
int currentHour = controller.startTime.value.hour;
// 动态获取站点的营业范围限制
final station = controller.stationOptions.firstWhereOrNull(
(s) => s.hydrogenId == controller.selectedStationId.value,
);
// 解析营业时间
// 处理格式如 "09:00" 或 "09:00:00"
final startParts = (station?.startBusiness ?? "00:00").split(':');
final endParts = (station?.endBusiness ?? "23:59").split(':');
int bizStartHour = int.tryParse(startParts[0]) ?? 0;
int bizEndHour = int.tryParse(endParts[0]) ?? 23;
int bizEndMinute = (endParts.length > 1) ? (int.tryParse(endParts[1]) ?? 0) : 0;
// 优化结束小时逻辑
// 如果分钟为 0 (例如 18:00),说明该小时整点已关门,最大可选小时应减 1
if (bizEndMinute == 0 && bizEndHour > 0) {
bizEndHour--;
}
return Column(
children: [
@@ -507,23 +526,23 @@ class ReservationPage extends GetView<C_ReservationController> {
overlayColor: const Color(0xFF006633).withOpacity(0.1),
),
child: Slider(
value: currentIdx.toDouble(),
min: 0,
max: 23,
divisions: 23,
value: currentHour.toDouble().clamp(
bizStartHour.toDouble(),
bizEndHour.toDouble(),
),
min: bizStartHour.toDouble(),
max: bizEndHour.toDouble(),
// divisions: bizEndHour - bizStartHour > 0 ? bizEndHour - bizStartHour : 1,
onChanged: (val) {
int hour = val.toInt();
// 模拟 Controller 中的 pickTime 逻辑校验
final now = DateTime.now();
final isToday =
controller.selectedDate.value.year == now.year &&
controller.selectedDate.value.month == now.month &&
controller.selectedDate.value.day == now.day;
if (isToday && hour < now.hour) {
// 如果是今天且小时数小于当前,则忽略
return;
}
// 如果是今天,判断不可选过去的时间点
if (isToday && hour < now.hour) return;
controller.startTime.value = TimeOfDay(hour: hour, minute: 0);
controller.endTime.value = TimeOfDay(hour: (hour + 1) % 24, minute: 0);
@@ -650,6 +669,7 @@ class ReservationPage extends GetView<C_ReservationController> {
onChanged: (value) {
if (value != null) {
controller.selectedStationId.value = value;
controller.resetTimeForSelectedDate();
}
},

View File

@@ -23,7 +23,7 @@ class WelcomePage extends GetView<WelcomeController> {
right: 0,
child: Image.asset(
'assets/images/welcome.png',
fit: BoxFit.fill
fit: BoxFit.cover
),
),
],