diff --git a/ln_jq_app/lib/pages/c_page/car_info/controller.dart b/ln_jq_app/lib/pages/c_page/car_info/controller.dart index e0cabf2..e594076 100644 --- a/ln_jq_app/lib/pages/c_page/car_info/controller.dart +++ b/ln_jq_app/lib/pages/c_page/car_info/controller.dart @@ -24,6 +24,10 @@ class CarInfoController extends GetxController with BaseControllerMixin { final RxList operationAttachments = [].obs; final RxList hydrogenationAttachments = [].obs; final RxList registerAttachments = [].obs; + String color = ""; + String hydrogenCapacity = ""; + String rentFromCompany = ""; + String address = ""; bool isNotice = false; @override @@ -127,6 +131,12 @@ class CarInfoController extends GetxController with BaseControllerMixin { ...hydrogenationAttachments, ...registerAttachments, ]; + + color = data['color'].toString(); + hydrogenCapacity = data['hydrogenCapacity'].toString(); + rentFromCompany = data['rentFromCompany'].toString(); + address = data['address'].toString(); + loadAllPdfs(); } } @@ -165,7 +175,7 @@ class CarInfoController extends GetxController with BaseControllerMixin { return url.toLowerCase().endsWith('.pdf'); } - List attachments = []; + List attachments = []; // --- 新增: 状态管理 --- /// 用于存储网络PDF的本地路径,key是网络url,value是本地路径 diff --git a/ln_jq_app/lib/pages/c_page/car_info/view.dart b/ln_jq_app/lib/pages/c_page/car_info/view.dart index d47e95c..6d50d14 100644 --- a/ln_jq_app/lib/pages/c_page/car_info/view.dart +++ b/ln_jq_app/lib/pages/c_page/car_info/view.dart @@ -339,7 +339,7 @@ class CarInfoPage extends GetView { tabs: const [ Tab(text: '行驶证'), Tab(text: '营运证'), - Tab(text: '加氢资格证'), + Tab(text: '加氢证'), Tab(text: '登记证'), ], ), @@ -379,8 +379,8 @@ class CarInfoPage extends GetView { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - _buildCertDetailItem('所属公司', '上海羚牛氢运物联网科技有限公司', isFull: true), - _buildCertDetailItem('运营城市', controller.vin), + _buildCertDetailItem('所属公司', controller.rentFromCompany, isFull: true), + _buildCertDetailItem('运营城市', controller.address), ], ), const SizedBox(height: 16), @@ -389,10 +389,10 @@ class CarInfoPage extends GetView { children: [ _buildCertDetailItem( '车辆颜色', - '2028-08-14', + controller.color, valueColor: const Color(0xFF52C41A), ), - _buildCertDetailItem('氢瓶容量', '货运'), + _buildCertDetailItem('氢瓶容量', controller.hydrogenCapacity), ], ), const SizedBox(height: 16), diff --git a/ln_jq_app/lib/pages/c_page/reservation/controller.dart b/ln_jq_app/lib/pages/c_page/reservation/controller.dart index 9def2c6..fb5b930 100644 --- a/ln_jq_app/lib/pages/c_page/reservation/controller.dart +++ b/ln_jq_app/lib/pages/c_page/reservation/controller.dart @@ -689,6 +689,18 @@ class C_ReservationController extends GetxController with BaseControllerMixin { } finally { HttpService.to.setBaseUrl(AppTheme.test_service_url); } + renderSliderTheme(); + } + + double current = 0.0; + double maxVal = 0.0; + + void renderSliderTheme() { + current = double.tryParse(amountController.text) ?? 0.0; + maxVal = double.tryParse(difference) ?? 100.0; + if (maxVal <= 0) maxVal = 100.0; + + updateUi(); } void getSiteList() async { diff --git a/ln_jq_app/lib/pages/c_page/reservation/view.dart b/ln_jq_app/lib/pages/c_page/reservation/view.dart index f650602..47a22c4 100644 --- a/ln_jq_app/lib/pages/c_page/reservation/view.dart +++ b/ln_jq_app/lib/pages/c_page/reservation/view.dart @@ -1,6 +1,7 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:get/get.dart'; import 'package:getx_scaffold/getx_scaffold.dart'; import 'package:ln_jq_app/common/login_util.dart'; import 'package:ln_jq_app/common/model/station_model.dart'; @@ -25,29 +26,24 @@ class ReservationPage extends GetView { return Scaffold( backgroundColor: Color.fromRGBO(247, 249, 251, 1), body: GestureDetector( - onTap: () { - hideKeyboard(); - }, + onTap: () => unfocus(), child: Stack( children: [ - Positioned( - left: 0, - right: 0, - bottom: 10.h, - top: 0, + Positioned.fill( child: SingleChildScrollView( child: Column( children: [ _buildUserInfoCard(), Padding( - padding: EdgeInsets.only(left: 20.w, right: 20.w), + padding: EdgeInsets.symmetric(horizontal: 18.w), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SizedBox(height: 16.h), _buildCarInfoCard(), - SizedBox(height: 32.h), + SizedBox(height: 24.h), _buildReservationFormCard(context), + SizedBox(height: 180.h), ], ), ), @@ -58,7 +54,7 @@ class ReservationPage extends GetView { Positioned( left: 20.w, right: 20.w, - bottom: 90.h, + bottom: 110.h, child: _buildReservationItem(context), ), ], @@ -156,13 +152,7 @@ class ReservationPage extends GetView { ), ), IconButton( - onPressed: () { - Get.to(() => const MessagePage()); - }, - style: IconButton.styleFrom( - backgroundColor: Colors.grey[100], - padding: const EdgeInsets.all(8), - ), + onPressed: () => Get.to(() => const MessagePage()), icon: Badge( smallSize: 8, backgroundColor: controller.isNotice @@ -196,9 +186,6 @@ class ReservationPage extends GetView { ); } - - - Widget _buildModernStatItem(String title, String subtitle, String value, String unit) { return Expanded( child: Container( @@ -241,7 +228,6 @@ class ReservationPage extends GetView { ); } - /// 构建车辆信息卡片 Widget _buildCarInfoCard() { return Card( elevation: 2, @@ -251,13 +237,8 @@ class ReservationPage extends GetView { padding: const EdgeInsets.all(16.0), child: Row( children: [ - // 左侧:车辆图片 - Expanded( - flex: 4, - child: LoginUtil.getAssImg('ic_car_bg@2x'), - ), + Expanded(flex: 4, child: LoginUtil.getAssImg('ic_car_bg@2x')), const SizedBox(width: 16), - // 右侧:信息与进度条 Expanded( flex: 6, child: Column( @@ -268,24 +249,39 @@ class ReservationPage extends GetView { const SizedBox(height: 8), _buildCarDataItem('百公里氢耗', '${controller.workEfficiency}Kg'), const SizedBox(height: 12), - // 进度条部分 Column( children: [ ClipRRect( borderRadius: BorderRadius.circular(4), - child: LinearProgressIndicator( + child: LinearProgressIndicator( value: controller.progressValue, minHeight: 6, - backgroundColor: Color(0xFFF0F2F5), - valueColor: AlwaysStoppedAnimation(Color(0xFF006633)), + backgroundColor: const Color(0xFFF0F2F5), + valueColor: const AlwaysStoppedAnimation( + Color(0xFF006633), + ), ), ), const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text("剩余氢量", style: TextStyle(fontSize: 10, color: Colors.grey)), - Text("${controller.leftHydrogen}Kg", style: const TextStyle(fontSize: 10, color: Color(0xFF006633), fontWeight: FontWeight.bold)), + const Text( + "剩余氢量", + style: TextStyle( + fontSize: 10, + color: Colors.grey, + fontWeight: FontWeight.w400, + ), + ), + Text( + "${controller.leftHydrogen}Kg", + style: const TextStyle( + fontSize: 10, + color: Color(0xFF006633), + fontWeight: FontWeight.w600, + ), + ), ], ), ], @@ -304,196 +300,440 @@ class ReservationPage extends GetView { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: const TextStyle(fontSize: 12, color: Colors.grey)), - Text(value, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black87)), + Text( + value, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), ], ); } - /// 构建预约表单卡片 + /// --- 构建预约表单卡片--- Widget _buildReservationFormCard(BuildContext context) { - return Card( - elevation: 2, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Obx( - () => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildPickerRow( - label: '日期', - value: controller.formattedDate, - icon: Icons.calendar_today_outlined, - onTap: () => controller.pickDate(context), - ), - _buildPickerRow( - label: '预约时间', - value: controller.formattedTimeSlot, - icon: Icons.access_time_outlined, - onTap: () => controller.pickTime(context), - ), - _buildTextField( - label: '预约氢量(KG)', - controller: controller.amountController, - hint: '当前最大可预约氢量${controller.difference}(KG)', - keyboardType: TextInputType.number, - ), - /*_buildTextField( - label: '车牌号', - controller: controller.plateNumberController, - hint: '请输入车牌号', // 修改提示文案 - enabled: false, // 设置为不可编辑 - ),*/ - _buildStationSelector(), - const SizedBox(height: 20), - ], - ), - ), - ), - ); - } - - Widget _buildReservationItem(BuildContext context) { - return Row( + return Column( children: [ - Expanded( - flex: 1, - child: OutlinedButton( - onPressed: () { - controller.getReservationList(showPopup: true, addStatus: ''); - }, - style: OutlinedButton.styleFrom( - minimumSize: Size(double.infinity, 40.h), // 高度与另一个按钮保持一致 - side: const BorderSide(color: Color.fromRGBO(226, 232, 240, 1)), - backgroundColor: Colors.white, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), - ), - child: const Text( - '查看预约', - style: TextStyle( - color: Color.fromRGBO(119, 119, 119, 1), - fontSize: 13, - fontWeight: FontWeight.bold, - ), + // 1. 顶部:日期与时间选择 + Card( + elevation: 0, + color: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "预约日期与时间", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + const SizedBox(height: 20), + _buildHorizontalDateSelector(), + const SizedBox(height: 32), + _buildTimeSlider(context), + ], ), ), ), - const SizedBox(width: 16), - Expanded( - flex: 2, - child: ElevatedButton( - onPressed: controller.submitReservation, - style: ElevatedButton.styleFrom( - minimumSize: Size(double.infinity, 40.h), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), - backgroundColor: AppTheme.themeColor, - foregroundColor: Colors.white, - ), - child: const Text( - '提交预约', - style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold), - ), - ), + const SizedBox(height: 12), + // 2. 底部:氢量与站点 + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: _buildAmountSliderSection()), + const SizedBox(width: 12), + Expanded(child: _buildStationCardSection(context)), + ], ), ], ); } - // 表单中的可点击行 (用于日期和时间选择) - Widget _buildPickerRow({ - required String label, - required String value, - required IconData icon, - required VoidCallback onTap, - }) { - return Padding( - padding: const EdgeInsets.only(bottom: 12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 13)), - const SizedBox(height: 8), - InkWell( - onTap: onTap, + /// 水平日期选择器 + Widget _buildHorizontalDateSelector() { + final DateTime today = DateTime( + DateTime.now().year, + DateTime.now().month, + DateTime.now().day, + ); + final DateTime tomorrow = today.add(const Duration(days: 1)); + final List dates = List.generate( + 5, + (index) => today.add(Duration(days: index)), + ); + const List weekMap = ['日', '一', '二', '三', '四', '五', '六']; + + return SizedBox( + height: 75, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: dates.length, + itemBuilder: (context, index) { + DateTime date = dates[index]; + bool isSelectable = + date.isAtSameMomentAs(today) || date.isAtSameMomentAs(tomorrow); + bool isSelected = + controller.selectedDate.value.year == date.year && + controller.selectedDate.value.month == date.month && + controller.selectedDate.value.day == date.day; + + return GestureDetector( + onTap: isSelectable + ? () { + controller.selectedDate.value = date; + controller.resetTimeForSelectedDate(); + controller.updateUi(); + } + : null, child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + width: 58, + margin: const EdgeInsets.only(right: 12), decoration: BoxDecoration( - border: Border.all(color: Colors.grey[400]!), - borderRadius: BorderRadius.circular(8), + color: isSelected ? const Color(0xFF006633) : const Color(0xFFF8F9FA), + borderRadius: BorderRadius.circular(14), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(value, style: const TextStyle(fontSize: 14)), - Icon(icon, color: Colors.grey, size: 20), + Text( + weekMap[date.weekday % 7], + style: TextStyle( + fontSize: 12, + color: isSelected + ? Colors.white70 + : (isSelectable ? Colors.grey : Colors.grey[300]), + ), + ), + const SizedBox(height: 6), + Text( + "${date.day}", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: isSelected + ? Colors.white + : (isSelectable ? Colors.black87 : Colors.grey[300]), + ), + ), ], ), ), - ), - ], + ); + }, ), ); } - // 表单中的文本输入框 - Widget _buildTextField({ - required String label, - required TextEditingController controller, - required String hint, - TextInputType? keyboardType, - bool enabled = true, - }) { - bool showCounter = keyboardType == TextInputType.number; - return Padding( - padding: const EdgeInsets.only(bottom: 12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 13)), - const SizedBox(height: 8), - TextFormField( - controller: controller, - keyboardType: keyboardType, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, // 只允许数字输入 - ], - enabled: enabled, - style: const TextStyle(fontSize: 14), - decoration: InputDecoration( - isDense: true, - hintText: hint, - hintStyle: TextStyle(fontSize: 14), - border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)), - contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), - filled: !enabled, - 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, + /// 时间 Slider 选择器 + Widget _buildTimeSlider(BuildContext context) { + return Obx(() { + // 这里的逻辑对应 Controller 中的 24 小时可用 Slot + int currentIdx = controller.startTime.value.hour; - // 右侧加号按钮 - 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, + return Column( + children: [ + Stack( + alignment: Alignment.topCenter, + clipBehavior: Clip.none, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 12), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), + decoration: BoxDecoration( + color: const Color(0xFFE6F4EA), + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Colors.white, width: 2), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: Text( + controller.formattedTimeSlot, + style: const TextStyle( + color: Color(0xFF006633), + fontSize: 13, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + SliderTheme( + data: SliderTheme.of(context).copyWith( + trackHeight: 10, + activeTrackColor: const Color(0xFF006633), + inactiveTrackColor: const Color(0xFFF0F2F5), + thumbColor: Colors.white, + thumbShape: const RoundSliderThumbShape( + enabledThumbRadius: 12, + elevation: 4, + ), + overlayColor: const Color(0xFF006633).withOpacity(0.1), + ), + child: Slider( + value: currentIdx.toDouble(), + min: 0, + max: 23, + divisions: 23, + 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; + } + + controller.startTime.value = TimeOfDay(hour: hour, minute: 0); + controller.endTime.value = TimeOfDay(hour: (hour + 1) % 24, minute: 0); + }, ), ), ], + ); + }); + } + + /// 氢量滑块区域 + Widget _buildAmountSliderSection() { + return Card( + elevation: 0, + color: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + child: Padding( + padding: const EdgeInsets.all(13.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "预约氢量", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + const Text( + "Refuel Amount", + style: TextStyle(fontSize: 10, color: Colors.grey), + ), + const SizedBox(height: 24), + Column( + children: [ + SliderTheme( + data: SliderTheme.of(Get.context!).copyWith( + trackHeight: 6, + activeTrackColor: const Color(0xFF006633), + inactiveTrackColor: const Color(0xFFF0F2F5), + thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 9), + ), + child: Slider( + value: controller.current > controller.maxVal + ? controller.maxVal + : controller.current, + min: 0, + max: controller.maxVal, + onChanged: (val) { + final safeVal = val < 1 ? 1 : val; // 最小 1 + controller.amountController.text = safeVal.toStringAsFixed(0); + controller.renderSliderTheme(); + }, + ), + ), + const SizedBox(height: 12), + Container( + height: 40.h, + decoration: BoxDecoration( + color: const Color(0xFFF8F9FA), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + onPressed: () => _updateAmount(-1), + icon: const Icon(Icons.remove, size: 18, color: Colors.grey), + ), + Text( + "${controller.amountController.text} Kg", + style: const TextStyle(fontSize: 14, color: Colors.black87), + ), + IconButton( + onPressed: () => _updateAmount(1), + icon: const Icon(Icons.add, size: 18, color: Colors.grey), + ), + ], + ), + ), + ], + ), + ], + ), + ), + ); + } + + /// 站点卡片区域 + + Widget _buildStationCardSection(BuildContext context) { + return Obx(() { + return DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + + /// 当前选中值 + value: controller.selectedStationId.value, + + hint: const Text('请选择加氢站', style: TextStyle(fontSize: 14, color: Colors.grey)), + + /// 下拉数据 + items: controller.stationOptions + .map( + (station) => DropdownMenuItem( + value: station.hydrogenId, + enabled: station.isSelect == 1, + child: _buildDropdownItem(station), + ), + ) + .toList(), + + /// 选中回调 + onChanged: (value) { + if (value != null) { + controller.selectedStationId.value = value; + } + }, + + ///作为 Dropdown 的触发按钮 + customButton: _buildStationCard(), + + /// 隐藏按钮自身样式 + buttonStyleData: const ButtonStyleData(padding: EdgeInsets.zero), + + /// 隐藏默认箭头 + iconStyleData: const IconStyleData(icon: SizedBox.shrink()), + + /// 下拉样式 + dropdownStyleData: DropdownStyleData( + maxHeight: 300, + width: MediaQuery.of(context).size.width / 1.3, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)), + ), + + /// 下拉项高度 + menuItemStyleData: const MenuItemStyleData(height: 60), + ), + ); + }); + } + + Widget _buildStationCard() { + final stationId = controller.selectedStationId.value; + final station = controller.stationOptions.firstWhereOrNull( + (s) => s.hydrogenId == stationId, + ); + + return Card( + elevation: 0, + color: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + child: Padding( + padding: const EdgeInsets.all(13.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "加氢站", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + Text("Station", style: TextStyle(fontSize: 10, color: Colors.grey)), + ], + ), + Icon(Icons.more_vert, color: Colors.grey[300], size: 20), + ], + ), + const SizedBox(height: 24), + Container( + width: double.infinity, + height: 100, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + image: const DecorationImage( + image: AssetImage("assets/images/bg_map@2x.png"), + fit: BoxFit.cover, + ), + ), + child: Stack( + children: [ + Center( + child: Icon( + Icons.location_on_outlined, + color: Colors.grey[300], + size: 40, + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(16), + bottomRight: Radius.circular(16), + ), + ), + child: Text( + "${station?.name ?? '请选择站点'} | ${station?.price ?? '0.00'}/Kg", + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ), + ), + ], + ), ), ); } - // :更新氢量逻辑 void _updateAmount(int change) { // 获取当前输入框的值,默认为 0 double currentAmount = double.tryParse(controller.amountController.text) ?? 0; @@ -522,98 +762,48 @@ class ReservationPage extends GetView { controller.amountController.selection = TextSelection.fromPosition( TextPosition(offset: controller.amountController.text.length), ); + controller.updateUi(); } - Widget _buildStationSelector() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, + Widget _buildReservationItem(BuildContext context) { + return Row( children: [ - Container( - padding: EdgeInsets.all(0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('加氢站', style: TextStyle(color: Colors.grey[600], fontSize: 14)), - TextButton( - onPressed: () { - controller.getSiteList(); - }, - child: const Text('刷新'), + Expanded( + flex: 1, + child: OutlinedButton( + onPressed: () => + controller.getReservationList(showPopup: true, addStatus: ''), + style: OutlinedButton.styleFrom( + minimumSize: Size(double.infinity, 50.h), + side: const BorderSide(color: Color.fromRGBO(226, 232, 240, 1)), + backgroundColor: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)), + ), + child: const Text( + '查看预约', + style: TextStyle( + color: Color.fromRGBO(119, 119, 119, 1), + fontSize: 14, + fontWeight: FontWeight.bold, ), - ], + ), ), ), - Obx( - () => DropdownButtonHideUnderline( - child: DropdownButton2( - isExpanded: true, - hint: const Text( - '请选择加氢站', - style: TextStyle(fontSize: 14, color: Colors.grey), - ), - // items 列表现在从 stationOptions (StationModel列表) 构建 - items: controller.stationOptions - .map( - (station) => DropdownMenuItem( - value: station.hydrogenId, // value 是站点的唯一ID - enabled: station.isSelect == 1, - child: _buildDropdownItem(station), // child 是自定义的 Widget - ), - ) - .toList(), - value: - // 当前的站点 处理默认 - controller.selectedStationId.value ?? - (controller.stationOptions.isNotEmpty - ? controller.stationOptions.first.hydrogenId - : null), - // 当前选中的是站点ID - onChanged: (value) { - if (value != null) { - controller.selectedStationId.value = value; - } - }, - customButton: Obx(() { - // 优先从已选中的 ID 查找 - var selectedStation = controller.stationOptions.firstWhereOrNull( - (s) => s.hydrogenId == controller.selectedStationId.value, - ); - - // 如果找不到已选中的(比如 ID 为空或列表里没有),并且列表不为空,则取第一个作为默认 - final stationToShow = - selectedStation ?? - (controller.stationOptions.isNotEmpty - ? controller.stationOptions.first - : null); - - // 如果有要显示的站点,就构建按钮 - if (stationToShow != null) { - return _buildSelectedStationButton(stationToShow); - } - - // 否则,返回一个空占位符,让 hint 生效 - // DropdownButton2 内部会判断,如果 customButton 返回的不是一个有效Widget(或根据其内部逻辑),就会显示 hint - return const SizedBox.shrink(); - }), - buttonStyleData: ButtonStyleData( - height: 40, // 增加高度以容纳两行文字 - padding: const EdgeInsets.symmetric(horizontal: 12.0), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - border: Border.all(color: Colors.grey[400]!), - ), - ), - iconStyleData: const IconStyleData( - icon: Icon(Icons.arrow_drop_down, color: Colors.grey), - iconSize: 24, - ), - dropdownStyleData: DropdownStyleData( - maxHeight: 300, - decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)), - ), - menuItemStyleData: const MenuItemStyleData( - height: 60, // 增加下拉项的高度 - ), + const SizedBox(width: 16), + Expanded( + flex: 2, + child: ElevatedButton( + onPressed: controller.submitReservation, + style: ElevatedButton.styleFrom( + minimumSize: Size(double.infinity, 50.h), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)), + backgroundColor: const Color(0xFF006633), + foregroundColor: Colors.white, + elevation: 4, + ), + child: const Text( + '提交预约', + style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), ), ), ),