司机 预约 样式修改
This commit is contained in:
@@ -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<C_ReservationController> {
|
||||
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<C_ReservationController> {
|
||||
Positioned(
|
||||
left: 20.w,
|
||||
right: 20.w,
|
||||
bottom: 90.h,
|
||||
bottom: 110.h,
|
||||
child: _buildReservationItem(context),
|
||||
),
|
||||
],
|
||||
@@ -156,13 +152,7 @@ class ReservationPage extends GetView<C_ReservationController> {
|
||||
),
|
||||
),
|
||||
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<C_ReservationController> {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Widget _buildModernStatItem(String title, String subtitle, String value, String unit) {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
@@ -241,7 +228,6 @@ class ReservationPage extends GetView<C_ReservationController> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建车辆信息卡片
|
||||
Widget _buildCarInfoCard() {
|
||||
return Card(
|
||||
elevation: 2,
|
||||
@@ -251,13 +237,8 @@ class ReservationPage extends GetView<C_ReservationController> {
|
||||
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<C_ReservationController> {
|
||||
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>(Color(0xFF006633)),
|
||||
backgroundColor: const Color(0xFFF0F2F5),
|
||||
valueColor: const AlwaysStoppedAnimation<Color>(
|
||||
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<C_ReservationController> {
|
||||
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<DateTime> dates = List.generate(
|
||||
5,
|
||||
(index) => today.add(Duration(days: index)),
|
||||
);
|
||||
const List<String> 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<String>(
|
||||
isExpanded: true,
|
||||
|
||||
/// 当前选中值
|
||||
value: controller.selectedStationId.value,
|
||||
|
||||
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(
|
||||
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<C_ReservationController> {
|
||||
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<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(
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user