Files
ln-ios/ln_jq_app/lib/pages/c_page/reservation/controller.dart
2025-11-11 16:51:27 +08:00

354 lines
13 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:getx_scaffold/common/common.dart';
import 'package:getx_scaffold/common/services/http.dart';
import 'package:intl/intl.dart';
import 'package:ln_jq_app/common/model/base_model.dart';
import 'package:ln_jq_app/common/styles/theme.dart';
import 'package:ln_jq_app/pages/b_page/site/controller.dart';
import 'package:ln_jq_app/storage_service.dart'; // 用于日期格式化
class ReservationController extends GetxController {
/// --- 状态变量 ---
// 【修改】使用 Rx 变量,让 GetX 的 Obx/GetX 能够自动监听变化UI更新更简单
// 日期,默认为今天
final Rx<DateTime> selectedDate = DateTime.now().obs;
// 开始时间,默认为当前时分
final Rx<TimeOfDay> startTime = TimeOfDay.now().obs;
// 结束时间默认为开始时间后30分钟
final Rx<TimeOfDay> endTime = TimeOfDay.fromDateTime(
DateTime.now().add(const Duration(minutes: 30)),
).obs;
// 预约氢量
final TextEditingController amountController = TextEditingController();
// 车牌号
final TextEditingController plateNumberController = TextEditingController();
// 加氢站
final List<String> stationOptions = [
'诚志AP银河路加氢站',
'站点B',
'站点C',
'站点C',
'站点C',
'站点C',
'站点C',
];
final Rx<String> selectedStation = '诚志AP银河路加氢站'.obs;
// --- 用于UI显示的格式化字符串 (Getters) ---
String get formattedDate => DateFormat('yyyy-MM-dd').format(selectedDate.value);
// 使用 context-aware 的 format 方法
String get formattedStartTime => startTime.value.format(Get.context!);
String get formattedEndTime => endTime.value.format(Get.context!);
/// --- 交互方法 ---
/// 【修改】显示 Flutter 内置的日期选择器
void pickDate(BuildContext context) async {
// 改为 async
final DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: selectedDate.value,
firstDate: DateTime.now(),
// 只能从今天开始选
lastDate: DateTime.now().add(const Duration(days: 365)),
// 假设最多预约一年内
helpText: '选择预约日期',
confirmText: '确定',
cancelText: '取消',
);
if (pickedDate != null && pickedDate != selectedDate.value) {
selectedDate.value = pickedDate;
}
}
/// 【修改】显示 Flutter 内置的时间选择器
void pickTime(BuildContext context, bool isStartTime) async {
// 改为 async
TimeOfDay initialTime = isStartTime ? startTime.value : endTime.value;
final TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: initialTime,
helpText: isStartTime ? '选择开始时间' : '选择结束时间',
confirmText: '确定',
cancelText: '取消',
);
if (pickedTime != null) {
if (isStartTime) {
startTime.value = pickedTime;
// 如果新的开始时间晚于或等于结束时间自动将结束时间设置为开始时间后30分钟
if ((pickedTime.hour * 60 + pickedTime.minute) >=
(endTime.value.hour * 60 + endTime.value.minute)) {
final newDateTime = DateTime(
selectedDate.value.year,
selectedDate.value.month,
selectedDate.value.day,
pickedTime.hour,
pickedTime.minute,
).add(const Duration(minutes: 30));
endTime.value = TimeOfDay.fromDateTime(newDateTime);
}
} else {
// 确保结束时间不早于开始时间
if ((pickedTime.hour * 60 + pickedTime.minute) >
(startTime.value.hour * 60 + startTime.value.minute)) {
endTime.value = pickedTime;
} else {
// 如果选择了更早的时间,给出提示
Get.snackbar(
'时间选择错误',
'结束时间必须晚于开始时间',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.red,
colorText: Colors.white,
);
}
}
}
}
/// 提交预约
void submitReservation() {
// TODO: 在这里执行提交预约的逻辑
print('提交预约:');
print('日期: $formattedDate');
print('开始时间: $formattedStartTime');
print('结束时间: $formattedEndTime');
print('预约氢量: ${amountController.text}');
print('车牌号: ${plateNumberController.text}');
print('加氢站: ${selectedStation.value}');
Get.snackbar('成功', '预约已提交!', snackPosition: SnackPosition.BOTTOM);
}
/// 状态变量:是否有预约数据
bool hasReservationData = false;
// 新增预约数据列表
List<ReservationModel> reservationList = [];
//查看预约列表
void getReservationList() async {
showLoading("加载中");
try {
var response = await HttpService.to.post(
"appointment/orderAddHyd/driverOrderPage",
data: {
'phone': StorageService.to.userId, // 使用从 renderData 中获取到的 name
'pageNum': 1,
'pageSize': 30, // 暂时不考虑分页一次获取30条
},
);
// 安全校验
if (response == null || response.data == null) {
showToast('暂时无法获取预约数据');
hasReservationData = false;
reservationList = [];
return;
}
final baseModel = BaseModel<dynamic>.fromJson(response.data);
if (baseModel.code == 0 && baseModel.data != null) {
// 【核心修改】处理接口返回的列表数据
final dataMap = baseModel.data as Map<String, dynamic>;
final List<dynamic> listFromServer = dataMap['list'] ?? [];
// 使用 .map() 遍历列表,将每个 item 转换为一个 ReservationModel 对象
reservationList = listFromServer.map((item) {
return ReservationModel.fromJson(item as Map<String, dynamic>);
}).toList();
// 根据列表是否为空来更新 hasReservationData 状态
hasReservationData = reservationList.isNotEmpty;
} else {
// 接口返回业务错误
showToast(baseModel.message);
hasReservationData = false;
reservationList = []; // 清空列表
}
} catch (e) {
// 捕获网络或解析异常
showToast('获取预约数据失败');
hasReservationData = false;
reservationList = []; // 清空列表
} finally {
// 无论成功失败最后都要关闭加载动画并更新UI
dismissLoading();
}
Get.bottomSheet(
Container(
height: Get.height * 0.45,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
child: Column(
children: [
Container(
padding: const EdgeInsets.fromLTRB(20, 15, 20, 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'我的预约',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
ElevatedButton(
onPressed: () => Get.back(),
style: ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: Colors.grey[200],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
),
child: const Text('关闭', style: TextStyle(color: Colors.black54)),
),
],
),
),
const Divider(height: 1),
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: reservationList.length,
itemBuilder: (context, index) {
final ReservationModel reservation = reservationList[index];
return Card(
margin: const EdgeInsets.only(bottom: 12.0),
elevation: 1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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),
_buildDetailRow('车牌号:', reservation.plateNumber),
_buildDetailRow('预约日期:', reservation.date),
_buildDetailRow('预约氢量:', reservation.hydAmount),
_buildDetailRow('加氢站:', reservation.stationName),
_buildDetailRow('开始时间:', reservation.startTime),
_buildDetailRow('结束时间:', reservation.endTime),
_buildDetailRow('联系人:', reservation.contacts),
_buildDetailRow('联系电话:', reservation.phone),
],
),
),
);
},
),
),
],
),
),
isScrollControlled: true,
backgroundColor:
Colors.transparent, // Make background transparent to see the rounded corners
);
}
Widget _buildDetailRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 85,
child: Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 14)),
),
Expanded(
child: Text(
value,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
color: Colors.black87,
),
),
),
],
),
);
}
@override
void onClose() {
amountController.dispose();
plateNumberController.dispose();
super.onClose();
}
}