样式交互修改
This commit is contained in:
@@ -8,6 +8,7 @@ class AppTheme {
|
|||||||
static const Color themeColor = Color(0xFF0c83c3);
|
static const Color themeColor = Color(0xFF0c83c3);
|
||||||
|
|
||||||
//http://192.168.110.222:8080/
|
//http://192.168.110.222:8080/
|
||||||
|
//http://192.168.110.44:8080/
|
||||||
static const String test_service_url = "https://beta-esg.api.lnh2e.com/";
|
static const String test_service_url = "https://beta-esg.api.lnh2e.com/";
|
||||||
static const String release_service_url = "";
|
static const String release_service_url = "";
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
final DateTime _now = DateTime.now();
|
final DateTime _now = DateTime.now();
|
||||||
|
|
||||||
// 计算当前时间属于哪个半小时区间
|
// 计算当前时间属于哪个1小时区间
|
||||||
late final TimeOfDay _initialStartTime = _calculateInitialStartTime(_now);
|
late final TimeOfDay _initialStartTime = _calculateInitialStartTime(_now);
|
||||||
late final TimeOfDay _initialEndTime = TimeOfDay.fromDateTime(
|
late final TimeOfDay _initialEndTime = TimeOfDay.fromDateTime(
|
||||||
_getDateTimeFromTimeOfDay(_initialStartTime).add(const Duration(minutes: 30)),
|
_getDateTimeFromTimeOfDay(_initialStartTime).add(const Duration(minutes: 60)),
|
||||||
);
|
);
|
||||||
late final Rx<DateTime> selectedDate = DateTime(_now.year, _now.month, _now.day).obs;
|
late final Rx<DateTime> selectedDate = DateTime(_now.year, _now.month, _now.day).obs;
|
||||||
late final Rx<TimeOfDay> startTime = _initialStartTime.obs;
|
late final Rx<TimeOfDay> startTime = _initialStartTime.obs;
|
||||||
@@ -52,13 +52,7 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
/// 静态辅助方法,用于计算初始的开始时间
|
/// 静态辅助方法,用于计算初始的开始时间
|
||||||
static TimeOfDay _calculateInitialStartTime(DateTime now) {
|
static TimeOfDay _calculateInitialStartTime(DateTime now) {
|
||||||
if (now.minute < 30) {
|
return TimeOfDay(hour: now.hour, minute: 0);
|
||||||
// 如果当前分钟小于30,则开始时间为当前小时的0分
|
|
||||||
return TimeOfDay(hour: now.hour, minute: 0);
|
|
||||||
} else {
|
|
||||||
// 如果当前分钟大于等于30,则开始时间为当前小时的30分
|
|
||||||
return TimeOfDay(hour: now.hour, minute: 30);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 静态辅助方法,将TimeOfDay转换为DateTime
|
/// 静态辅助方法,将TimeOfDay转换为DateTime
|
||||||
@@ -186,30 +180,24 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///30 分钟为间隔 时间选择器
|
///60 分钟为间隔 时间选择器
|
||||||
void pickTime(BuildContext context) {
|
void pickTime(BuildContext context) {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
final isToday =
|
final isToday =
|
||||||
selectedDate.value.year == now.year &&
|
selectedDate.value.year == now.year &&
|
||||||
selectedDate.value.month == now.month &&
|
selectedDate.value.month == now.month &&
|
||||||
selectedDate.value.day == now.day;
|
selectedDate.value.day == now.day;
|
||||||
|
|
||||||
final List<TimeSlot> availableSlots = [];
|
final List<TimeSlot> availableSlots = [];
|
||||||
for (int i = 0; i < 48; i++) {
|
for (int i = 0; i < 24; i++) {
|
||||||
final startMinutes = i * 30;
|
// 每次增加 60 分钟
|
||||||
final endMinutes = startMinutes + 30;
|
final startMinutes = i * 60;
|
||||||
|
final endMinutes = startMinutes + 60;
|
||||||
|
|
||||||
final startTime = TimeOfDay(hour: startMinutes ~/ 60, minute: startMinutes % 60);
|
final startTime = TimeOfDay(hour: startMinutes ~/ 60, minute: startMinutes % 60);
|
||||||
|
// 注意:endMinutes % 60 始终为 0,因为间隔是整小时
|
||||||
final endTime = TimeOfDay(hour: (endMinutes ~/ 60) % 24, minute: endMinutes % 60);
|
final endTime = TimeOfDay(hour: (endMinutes ~/ 60) % 24, minute: endMinutes % 60);
|
||||||
|
|
||||||
final slotStartDateTime = DateTime(
|
|
||||||
selectedDate.value.year,
|
|
||||||
selectedDate.value.month,
|
|
||||||
selectedDate.value.day,
|
|
||||||
startTime.hour,
|
|
||||||
startTime.minute,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 如果不是今天,所有时间段都有效
|
// 如果不是今天,所有时间段都有效
|
||||||
if (!isToday) {
|
if (!isToday) {
|
||||||
availableSlots.add(TimeSlot(startTime, endTime));
|
availableSlots.add(TimeSlot(startTime, endTime));
|
||||||
@@ -224,8 +212,16 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
endTime.minute,
|
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 (slotEndDateTime.isAfter(now)) {
|
if (realEndDateTime.isAfter(now)) {
|
||||||
availableSlots.add(TimeSlot(startTime, endTime));
|
availableSlots.add(TimeSlot(startTime, endTime));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,13 +232,11 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查找当前选中的时间对应的新列表中的索引
|
||||||
int initialItem = availableSlots.indexWhere(
|
int initialItem = availableSlots.indexWhere(
|
||||||
(slot) =>
|
(slot) => slot.start.hour == startTime.value.hour,
|
||||||
slot.start.hour == startTime.value.hour &&
|
|
||||||
(startTime.value.minute < 30
|
|
||||||
? slot.start.minute == 0
|
|
||||||
: slot.start.minute == 30),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (initialItem == -1) {
|
if (initialItem == -1) {
|
||||||
initialItem = 0;
|
initialItem = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,15 +13,12 @@ import 'reservation_list_bottomsheet.dart';
|
|||||||
class ReservationPage extends GetView<C_ReservationController> {
|
class ReservationPage extends GetView<C_ReservationController> {
|
||||||
ReservationPage({super.key});
|
ReservationPage({super.key});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GetBuilder<C_ReservationController>(
|
return GetBuilder<C_ReservationController>(
|
||||||
init: C_ReservationController(),
|
init: C_ReservationController(),
|
||||||
id: 'reservation',
|
id: 'reservation',
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.grey[100],
|
backgroundColor: Colors.grey[100],
|
||||||
body: GestureDetector(
|
body: GestureDetector(
|
||||||
@@ -273,12 +270,12 @@ class ReservationPage extends GetView<C_ReservationController> {
|
|||||||
hint: '当前最大可预约氢量${controller.difference}(KG)',
|
hint: '当前最大可预约氢量${controller.difference}(KG)',
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
),
|
),
|
||||||
_buildTextField(
|
/*_buildTextField(
|
||||||
label: '车牌号',
|
label: '车牌号',
|
||||||
controller: controller.plateNumberController,
|
controller: controller.plateNumberController,
|
||||||
hint: '请输入车牌号', // 修改提示文案
|
hint: '请输入车牌号', // 修改提示文案
|
||||||
enabled: false, // 设置为不可编辑
|
enabled: false, // 设置为不可编辑
|
||||||
),
|
),*/
|
||||||
_buildStationSelector(),
|
_buildStationSelector(),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Row(
|
Row(
|
||||||
@@ -332,8 +329,6 @@ class ReservationPage extends GetView<C_ReservationController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 表单中的可点击行 (用于日期和时间选择)
|
// 表单中的可点击行 (用于日期和时间选择)
|
||||||
Widget _buildPickerRow({
|
Widget _buildPickerRow({
|
||||||
required String label,
|
required String label,
|
||||||
@@ -378,6 +373,7 @@ class ReservationPage extends GetView<C_ReservationController> {
|
|||||||
TextInputType? keyboardType,
|
TextInputType? keyboardType,
|
||||||
bool enabled = true,
|
bool enabled = true,
|
||||||
}) {
|
}) {
|
||||||
|
bool showCounter = keyboardType == TextInputType.number;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 12.0),
|
padding: const EdgeInsets.only(bottom: 12.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -401,6 +397,25 @@ class ReservationPage extends GetView<C_ReservationController> {
|
|||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
filled: !enabled,
|
filled: !enabled,
|
||||||
fillColor: Colors.grey[100],
|
fillColor: Colors.grey[100],
|
||||||
|
// 左侧减号按钮
|
||||||
|
prefixIcon: showCounter
|
||||||
|
? IconButton(
|
||||||
|
icon: const Icon(Icons.remove, color: Colors.blue),
|
||||||
|
onPressed: () => _updateAmount(-1),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(minWidth: 40, minHeight: 40),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
|
||||||
|
// 右侧加号按钮
|
||||||
|
suffixIcon: showCounter
|
||||||
|
? IconButton(
|
||||||
|
icon: const Icon(Icons.add, color: Colors.blue),
|
||||||
|
onPressed: () => _updateAmount(1),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(minWidth: 40, minHeight: 40),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -408,6 +423,37 @@ class ReservationPage extends GetView<C_ReservationController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// :更新氢量逻辑
|
||||||
|
void _updateAmount(int change) {
|
||||||
|
// 获取当前输入框的值,默认为 0
|
||||||
|
double currentAmount = double.tryParse(controller.amountController.text) ?? 0;
|
||||||
|
|
||||||
|
// 获取最大值,注意处理 difference 可能为空或非数字的情况
|
||||||
|
double maxAmount = double.tryParse(controller.difference.toString()) ?? 9999;
|
||||||
|
|
||||||
|
// 计算新值
|
||||||
|
double newAmount = currentAmount + change;
|
||||||
|
|
||||||
|
// 边界检查
|
||||||
|
if (newAmount < 1) {
|
||||||
|
newAmount = 1; // 最小不能小于 1
|
||||||
|
} else if (newAmount > maxAmount) {
|
||||||
|
newAmount = maxAmount; // 最大不能超过 difference
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是整数,去掉小数点显示
|
||||||
|
if (newAmount == newAmount.toInt()) {
|
||||||
|
controller.amountController.text = newAmount.toInt().toString();
|
||||||
|
} else {
|
||||||
|
controller.amountController.text = newAmount.toStringAsFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动光标到末尾,防止光标跳到前面
|
||||||
|
controller.amountController.selection = TextSelection.fromPosition(
|
||||||
|
TextPosition(offset: controller.amountController.text.length),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildStationSelector() {
|
Widget _buildStationSelector() {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -428,7 +474,7 @@ class ReservationPage extends GetView<C_ReservationController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Obx(
|
Obx(
|
||||||
() => DropdownButtonHideUnderline(
|
() => DropdownButtonHideUnderline(
|
||||||
child: DropdownButton2<String>(
|
child: DropdownButton2<String>(
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
hint: const Text(
|
hint: const Text(
|
||||||
@@ -439,15 +485,15 @@ class ReservationPage extends GetView<C_ReservationController> {
|
|||||||
items: controller.stationOptions
|
items: controller.stationOptions
|
||||||
.map(
|
.map(
|
||||||
(station) => DropdownMenuItem<String>(
|
(station) => DropdownMenuItem<String>(
|
||||||
value: station.hydrogenId, // value 是站点的唯一ID
|
value: station.hydrogenId, // value 是站点的唯一ID
|
||||||
enabled: station.isSelect == 1,
|
enabled: station.isSelect == 1,
|
||||||
child: _buildDropdownItem(station), // child 是自定义的 Widget
|
child: _buildDropdownItem(station), // child 是自定义的 Widget
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
value:
|
value:
|
||||||
// 当前的站点 处理默认
|
// 当前的站点 处理默认
|
||||||
controller.selectedStationId.value ??
|
controller.selectedStationId.value ??
|
||||||
(controller.stationOptions.isNotEmpty
|
(controller.stationOptions.isNotEmpty
|
||||||
? controller.stationOptions.first.hydrogenId
|
? controller.stationOptions.first.hydrogenId
|
||||||
: null),
|
: null),
|
||||||
@@ -460,15 +506,15 @@ class ReservationPage extends GetView<C_ReservationController> {
|
|||||||
customButton: Obx(() {
|
customButton: Obx(() {
|
||||||
// 优先从已选中的 ID 查找
|
// 优先从已选中的 ID 查找
|
||||||
var selectedStation = controller.stationOptions.firstWhereOrNull(
|
var selectedStation = controller.stationOptions.firstWhereOrNull(
|
||||||
(s) => s.hydrogenId == controller.selectedStationId.value,
|
(s) => s.hydrogenId == controller.selectedStationId.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 如果找不到已选中的(比如 ID 为空或列表里没有),并且列表不为空,则取第一个作为默认
|
// 如果找不到已选中的(比如 ID 为空或列表里没有),并且列表不为空,则取第一个作为默认
|
||||||
final stationToShow =
|
final stationToShow =
|
||||||
selectedStation ??
|
selectedStation ??
|
||||||
(controller.stationOptions.isNotEmpty
|
(controller.stationOptions.isNotEmpty
|
||||||
? controller.stationOptions.first
|
? controller.stationOptions.first
|
||||||
: null);
|
: null);
|
||||||
|
|
||||||
// 如果有要显示的站点,就构建按钮
|
// 如果有要显示的站点,就构建按钮
|
||||||
if (stationToShow != null) {
|
if (stationToShow != null) {
|
||||||
|
|||||||
@@ -74,32 +74,45 @@ class ReservationEditController extends GetxController with BaseControllerMixin
|
|||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
final isToday =
|
final isToday =
|
||||||
selectedDate.value.year == now.year &&
|
selectedDate.value.year == now.year &&
|
||||||
selectedDate.value.month == now.month &&
|
selectedDate.value.month == now.month &&
|
||||||
selectedDate.value.day == now.day;
|
selectedDate.value.day == now.day;
|
||||||
|
|
||||||
final List<TimeSlot> availableSlots = [];
|
final List<TimeSlot> availableSlots = [];
|
||||||
for (int i = 0; i < 48; i++) {
|
for (int i = 0; i < 24; i++) {
|
||||||
final startMinutes = i * 30;
|
// 每次增加 60 分钟
|
||||||
final endMinutes = startMinutes + 30;
|
final startMinutes = i * 60;
|
||||||
final slotStartTime = TimeOfDay(
|
final endMinutes = startMinutes + 60;
|
||||||
hour: startMinutes ~/ 60,
|
|
||||||
minute: startMinutes % 60,
|
|
||||||
);
|
|
||||||
final slotEndTime = TimeOfDay(
|
|
||||||
hour: (endMinutes ~/ 60) % 24,
|
|
||||||
minute: endMinutes % 60,
|
|
||||||
);
|
|
||||||
|
|
||||||
final slotStartDateTime = DateTime(
|
final startTime = TimeOfDay(hour: startMinutes ~/ 60, minute: startMinutes % 60);
|
||||||
selectedDate.value.year,
|
// 注意:endMinutes % 60 始终为 0,因为间隔是整小时
|
||||||
selectedDate.value.month,
|
final endTime = TimeOfDay(hour: (endMinutes ~/ 60) % 24, minute: endMinutes % 60);
|
||||||
selectedDate.value.day,
|
|
||||||
slotStartTime.hour,
|
|
||||||
slotStartTime.minute,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isToday || slotStartDateTime.isAfter(now)) {
|
// 如果不是今天,所有时间段都有效
|
||||||
availableSlots.add(TimeSlot(slotStartTime, slotEndTime));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,17 +121,18 @@ class ReservationEditController extends GetxController with BaseControllerMixin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查找当前选中的时间对应的新列表中的索引
|
||||||
int initialItem = availableSlots.indexWhere(
|
int initialItem = availableSlots.indexWhere(
|
||||||
(slot) =>
|
(slot) => slot.start.hour == startTime.value.hour,
|
||||||
slot.start.hour == startTime.value.hour &&
|
|
||||||
(startTime.value.minute < 30
|
|
||||||
? slot.start.minute == 0
|
|
||||||
: slot.start.minute == 30),
|
|
||||||
);
|
);
|
||||||
if (initialItem == -1) initialItem = 0;
|
|
||||||
|
if (initialItem == -1) {
|
||||||
|
initialItem = 0;
|
||||||
|
}
|
||||||
|
|
||||||
TimeSlot tempSlot = availableSlots[initialItem];
|
TimeSlot tempSlot = availableSlots[initialItem];
|
||||||
|
|
||||||
|
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
Container(
|
Container(
|
||||||
height: 300,
|
height: 300,
|
||||||
|
|||||||
Reference in New Issue
Block a user