司机 预约 样式修改

This commit is contained in:
2026-01-27 17:35:37 +08:00
parent 18c04272e2
commit f8a8ecb0ed
4 changed files with 495 additions and 283 deletions

View File

@@ -24,6 +24,10 @@ class CarInfoController extends GetxController with BaseControllerMixin {
final RxList<String> operationAttachments = <String>[].obs; final RxList<String> operationAttachments = <String>[].obs;
final RxList<String> hydrogenationAttachments = <String>[].obs; final RxList<String> hydrogenationAttachments = <String>[].obs;
final RxList<String> registerAttachments = <String>[].obs; final RxList<String> registerAttachments = <String>[].obs;
String color = "";
String hydrogenCapacity = "";
String rentFromCompany = "";
String address = "";
bool isNotice = false; bool isNotice = false;
@override @override
@@ -127,6 +131,12 @@ class CarInfoController extends GetxController with BaseControllerMixin {
...hydrogenationAttachments, ...hydrogenationAttachments,
...registerAttachments, ...registerAttachments,
]; ];
color = data['color'].toString();
hydrogenCapacity = data['hydrogenCapacity'].toString();
rentFromCompany = data['rentFromCompany'].toString();
address = data['address'].toString();
loadAllPdfs(); loadAllPdfs();
} }
} }

View File

@@ -339,7 +339,7 @@ class CarInfoPage extends GetView<CarInfoController> {
tabs: const [ tabs: const [
Tab(text: '行驶证'), Tab(text: '行驶证'),
Tab(text: '营运证'), Tab(text: '营运证'),
Tab(text: '加氢资格'), Tab(text: '加氢证'),
Tab(text: '登记证'), Tab(text: '登记证'),
], ],
), ),
@@ -379,8 +379,8 @@ class CarInfoPage extends GetView<CarInfoController> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
_buildCertDetailItem('所属公司', '上海羚牛氢运物联网科技有限公司', isFull: true), _buildCertDetailItem('所属公司', controller.rentFromCompany, isFull: true),
_buildCertDetailItem('运营城市', controller.vin), _buildCertDetailItem('运营城市', controller.address),
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@@ -389,10 +389,10 @@ class CarInfoPage extends GetView<CarInfoController> {
children: [ children: [
_buildCertDetailItem( _buildCertDetailItem(
'车辆颜色', '车辆颜色',
'2028-08-14', controller.color,
valueColor: const Color(0xFF52C41A), valueColor: const Color(0xFF52C41A),
), ),
_buildCertDetailItem('氢瓶容量', '货运'), _buildCertDetailItem('氢瓶容量', controller.hydrogenCapacity),
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),

View File

@@ -689,6 +689,18 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
} finally { } finally {
HttpService.to.setBaseUrl(AppTheme.test_service_url); 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 { void getSiteList() async {

View File

@@ -1,6 +1,7 @@
import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:getx_scaffold/getx_scaffold.dart'; import 'package:getx_scaffold/getx_scaffold.dart';
import 'package:ln_jq_app/common/login_util.dart'; import 'package:ln_jq_app/common/login_util.dart';
import 'package:ln_jq_app/common/model/station_model.dart'; import 'package:ln_jq_app/common/model/station_model.dart';
@@ -25,29 +26,24 @@ class ReservationPage extends GetView<C_ReservationController> {
return Scaffold( return Scaffold(
backgroundColor: Color.fromRGBO(247, 249, 251, 1), backgroundColor: Color.fromRGBO(247, 249, 251, 1),
body: GestureDetector( body: GestureDetector(
onTap: () { onTap: () => unfocus(),
hideKeyboard();
},
child: Stack( child: Stack(
children: [ children: [
Positioned( Positioned.fill(
left: 0,
right: 0,
bottom: 10.h,
top: 0,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
_buildUserInfoCard(), _buildUserInfoCard(),
Padding( Padding(
padding: EdgeInsets.only(left: 20.w, right: 20.w), padding: EdgeInsets.symmetric(horizontal: 18.w),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildCarInfoCard(), _buildCarInfoCard(),
SizedBox(height: 32.h), SizedBox(height: 24.h),
_buildReservationFormCard(context), _buildReservationFormCard(context),
SizedBox(height: 180.h),
], ],
), ),
), ),
@@ -58,7 +54,7 @@ class ReservationPage extends GetView<C_ReservationController> {
Positioned( Positioned(
left: 20.w, left: 20.w,
right: 20.w, right: 20.w,
bottom: 90.h, bottom: 110.h,
child: _buildReservationItem(context), child: _buildReservationItem(context),
), ),
], ],
@@ -156,13 +152,7 @@ class ReservationPage extends GetView<C_ReservationController> {
), ),
), ),
IconButton( IconButton(
onPressed: () { onPressed: () => Get.to(() => const MessagePage()),
Get.to(() => const MessagePage());
},
style: IconButton.styleFrom(
backgroundColor: Colors.grey[100],
padding: const EdgeInsets.all(8),
),
icon: Badge( icon: Badge(
smallSize: 8, smallSize: 8,
backgroundColor: controller.isNotice backgroundColor: controller.isNotice
@@ -196,9 +186,6 @@ class ReservationPage extends GetView<C_ReservationController> {
); );
} }
Widget _buildModernStatItem(String title, String subtitle, String value, String unit) { Widget _buildModernStatItem(String title, String subtitle, String value, String unit) {
return Expanded( return Expanded(
child: Container( child: Container(
@@ -241,7 +228,6 @@ class ReservationPage extends GetView<C_ReservationController> {
); );
} }
/// 构建车辆信息卡片
Widget _buildCarInfoCard() { Widget _buildCarInfoCard() {
return Card( return Card(
elevation: 2, elevation: 2,
@@ -251,13 +237,8 @@ class ReservationPage extends GetView<C_ReservationController> {
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Row( child: Row(
children: [ 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), const SizedBox(width: 16),
// 右侧:信息与进度条
Expanded( Expanded(
flex: 6, flex: 6,
child: Column( child: Column(
@@ -268,7 +249,6 @@ class ReservationPage extends GetView<C_ReservationController> {
const SizedBox(height: 8), const SizedBox(height: 8),
_buildCarDataItem('百公里氢耗', '${controller.workEfficiency}Kg'), _buildCarDataItem('百公里氢耗', '${controller.workEfficiency}Kg'),
const SizedBox(height: 12), const SizedBox(height: 12),
// 进度条部分
Column( Column(
children: [ children: [
ClipRRect( ClipRRect(
@@ -276,16 +256,32 @@ class ReservationPage extends GetView<C_ReservationController> {
child: LinearProgressIndicator( child: LinearProgressIndicator(
value: controller.progressValue, value: controller.progressValue,
minHeight: 6, minHeight: 6,
backgroundColor: Color(0xFFF0F2F5), backgroundColor: const Color(0xFFF0F2F5),
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFF006633)), valueColor: const AlwaysStoppedAnimation<Color>(
Color(0xFF006633),
),
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text("剩余氢量", style: TextStyle(fontSize: 10, color: Colors.grey)), const Text(
Text("${controller.leftHydrogen}Kg", style: const TextStyle(fontSize: 10, color: Color(0xFF006633), fontWeight: FontWeight.bold)), "剩余氢量",
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<C_ReservationController> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(label, style: const TextStyle(fontSize: 12, color: Colors.grey)), 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) { Widget _buildReservationFormCard(BuildContext context) {
return Card( return Column(
elevation: 2, children: [
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), // 1. 顶部:日期与时间选择
Card(
elevation: 0,
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(20.0),
child: Obx( child: Column(
() => Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildPickerRow( const Text(
label: '日期', "预约日期与时间",
value: controller.formattedDate, style: TextStyle(
icon: Icons.calendar_today_outlined, fontSize: 14,
onTap: () => controller.pickDate(context), fontWeight: FontWeight.w600,
color: Colors.black87,
), ),
_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), const SizedBox(height: 20),
_buildHorizontalDateSelector(),
const SizedBox(height: 32),
_buildTimeSlider(context),
], ],
), ),
), ),
), ),
const SizedBox(height: 12),
// 2. 底部:氢量与站点
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: _buildAmountSliderSection()),
const SizedBox(width: 12),
Expanded(child: _buildStationCardSection(context)),
],
),
],
); );
} }
Widget _buildReservationItem(BuildContext context) { /// 水平日期选择器
return Row( Widget _buildHorizontalDateSelector() {
children: [ final DateTime today = DateTime(
Expanded( DateTime.now().year,
flex: 1, DateTime.now().month,
child: OutlinedButton( DateTime.now().day,
onPressed: () { );
controller.getReservationList(showPopup: true, addStatus: ''); final DateTime tomorrow = today.add(const Duration(days: 1));
}, final List<DateTime> dates = List.generate(
style: OutlinedButton.styleFrom( 5,
minimumSize: Size(double.infinity, 40.h), // 高度与另一个按钮保持一致 (index) => today.add(Duration(days: index)),
side: const BorderSide(color: Color.fromRGBO(226, 232, 240, 1)), );
backgroundColor: Colors.white, const List<String> weekMap = ['', '', '', '', '', '', ''];
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
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(
width: 58,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFF006633) : const Color(0xFFF8F9FA),
borderRadius: BorderRadius.circular(14),
), ),
child: const Text( child: Column(
'查看预约', mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
weekMap[date.weekday % 7],
style: TextStyle( style: TextStyle(
color: Color.fromRGBO(119, 119, 119, 1), 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]),
),
),
],
),
),
);
},
),
);
}
/// 时间 Slider 选择器
Widget _buildTimeSlider(BuildContext context) {
return Obx(() {
// 这里的逻辑对应 Controller 中的 24 小时可用 Slot
int currentIdx = controller.startTime.value.hour;
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, fontSize: 13,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
), ),
), ),
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( SliderTheme(
'提交预约', data: SliderTheme.of(context).copyWith(
style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold), 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 _buildPickerRow({ Widget _buildAmountSliderSection() {
required String label, return Card(
required String value, elevation: 0,
required IconData icon, color: Colors.white,
required VoidCallback onTap, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
}) { child: Padding(
return Padding( padding: const EdgeInsets.all(13.0),
padding: const EdgeInsets.only(bottom: 12.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 13)), const Text(
const SizedBox(height: 8), "预约氢量",
InkWell( style: TextStyle(
onTap: onTap, fontSize: 14,
child: Container( fontWeight: FontWeight.bold,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), 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( decoration: BoxDecoration(
border: Border.all(color: Colors.grey[400]!), color: const Color(0xFFF8F9FA),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(12),
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(value, style: const TextStyle(fontSize: 14)), IconButton(
Icon(icon, color: Colors.grey, size: 20), 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 _buildTextField({
required String label, Widget _buildStationCardSection(BuildContext context) {
required TextEditingController controller, return Obx(() {
required String hint, return DropdownButtonHideUnderline(
TextInputType? keyboardType, child: DropdownButton2<String>(
bool enabled = true, isExpanded: true,
}) {
bool showCounter = keyboardType == TextInputType.number; /// 当前选中值
return Padding( value: controller.selectedStationId.value,
padding: const EdgeInsets.only(bottom: 12.0),
hint: const Text('请选择加氢站', style: TextStyle(fontSize: 14, color: Colors.grey)),
/// 下拉数据
items: controller.stationOptions
.map(
(station) => DropdownMenuItem<String>(
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( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 13)), Row(
const SizedBox(height: 8), mainAxisAlignment: MainAxisAlignment.spaceBetween,
TextFormField( children: [
controller: controller, const Column(
keyboardType: keyboardType, crossAxisAlignment: CrossAxisAlignment.start,
inputFormatters: [ children: [
FilteringTextInputFormatter.digitsOnly, // 只允许数字输入 Text(
"加氢站",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
Text("Station", style: TextStyle(fontSize: 10, color: Colors.grey)),
], ],
enabled: enabled, ),
style: const TextStyle(fontSize: 14), Icon(Icons.more_vert, color: Colors.grey[300], size: 20),
decoration: InputDecoration( ],
isDense: true, ),
hintText: hint, const SizedBox(height: 24),
hintStyle: TextStyle(fontSize: 14), Container(
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)), width: double.infinity,
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), height: 100,
filled: !enabled, decoration: BoxDecoration(
fillColor: Colors.grey[100], borderRadius: BorderRadius.circular(16),
// 左侧减号按钮 image: const DecorationImage(
prefixIcon: showCounter image: AssetImage("assets/images/bg_map@2x.png"),
? IconButton( fit: BoxFit.cover,
icon: const Icon(Icons.remove, color: Colors.blue), ),
onPressed: () => _updateAmount(-1), ),
padding: EdgeInsets.zero, child: Stack(
constraints: const BoxConstraints(minWidth: 40, minHeight: 40), children: [
) Center(
: null, child: Icon(
Icons.location_on_outlined,
// 右侧加号按钮 color: Colors.grey[300],
suffixIcon: showCounter size: 40,
? IconButton( ),
icon: const Icon(Icons.add, color: Colors.blue), ),
onPressed: () => _updateAmount(1), Align(
padding: EdgeInsets.zero, alignment: Alignment.bottomCenter,
constraints: const BoxConstraints(minWidth: 40, minHeight: 40), child: Container(
) width: double.infinity,
: null, 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) { void _updateAmount(int change) {
// 获取当前输入框的值,默认为 0 // 获取当前输入框的值,默认为 0
double currentAmount = double.tryParse(controller.amountController.text) ?? 0; double currentAmount = double.tryParse(controller.amountController.text) ?? 0;
@@ -522,98 +762,48 @@ class ReservationPage extends GetView<C_ReservationController> {
controller.amountController.selection = TextSelection.fromPosition( controller.amountController.selection = TextSelection.fromPosition(
TextPosition(offset: controller.amountController.text.length), TextPosition(offset: controller.amountController.text.length),
); );
controller.updateUi();
} }
Widget _buildStationSelector() { Widget _buildReservationItem(BuildContext context) {
return Column( return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( Expanded(
padding: EdgeInsets.all(0), flex: 1,
child: Row( child: OutlinedButton(
mainAxisAlignment: MainAxisAlignment.spaceBetween, onPressed: () =>
children: [ controller.getReservationList(showPopup: true, addStatus: ''),
Text('加氢站', style: TextStyle(color: Colors.grey[600], fontSize: 14)), style: OutlinedButton.styleFrom(
TextButton( minimumSize: Size(double.infinity, 50.h),
onPressed: () { side: const BorderSide(color: Color.fromRGBO(226, 232, 240, 1)),
controller.getSiteList(); backgroundColor: Colors.white,
}, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),
child: const Text('刷新'),
), ),
], child: const Text(
'查看预约',
style: TextStyle(
color: Color.fromRGBO(119, 119, 119, 1),
fontSize: 14,
fontWeight: FontWeight.bold,
), ),
), ),
Obx(
() => DropdownButtonHideUnderline(
child: DropdownButton2<String>(
isExpanded: true,
hint: const Text(
'请选择加氢站',
style: TextStyle(fontSize: 14, color: Colors.grey),
),
// items 列表现在从 stationOptions (StationModel列表) 构建
items: controller.stationOptions
.map(
(station) => DropdownMenuItem<String>(
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( const SizedBox(width: 16),
icon: Icon(Icons.arrow_drop_down, color: Colors.grey), Expanded(
iconSize: 24, flex: 2,
), child: ElevatedButton(
dropdownStyleData: DropdownStyleData( onPressed: controller.submitReservation,
maxHeight: 300, style: ElevatedButton.styleFrom(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)), minimumSize: Size(double.infinity, 50.h),
), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),
menuItemStyleData: const MenuItemStyleData( backgroundColor: const Color(0xFF006633),
height: 60, // 增加下拉项的高度 foregroundColor: Colors.white,
elevation: 4,
), ),
child: const Text(
'提交预约',
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
), ),
), ),
), ),