915 lines
31 KiB
Dart
915 lines
31 KiB
Dart
import 'package:dropdown_button2/dropdown_button2.dart';
|
||
import 'package:flutter/material.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';
|
||
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
||
import 'package:ln_jq_app/storage_service.dart';
|
||
|
||
import 'controller.dart';
|
||
|
||
///加氢预约
|
||
class ReservationPage extends GetView<C_ReservationController> {
|
||
ReservationPage({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return GetBuilder<C_ReservationController>(
|
||
init: C_ReservationController(),
|
||
id: 'reservation',
|
||
builder: (_) {
|
||
return Scaffold(
|
||
backgroundColor: Color.fromRGBO(247, 249, 251, 1),
|
||
body: GestureDetector(
|
||
onTap: () => unfocus(),
|
||
child: Stack(
|
||
children: [
|
||
Positioned.fill(
|
||
child: SingleChildScrollView(
|
||
child: Column(
|
||
children: [
|
||
_buildUserInfoCard(),
|
||
Padding(
|
||
padding: EdgeInsets.symmetric(horizontal: 18.w),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
SizedBox(height: 16.h),
|
||
_buildCarInfoCard(),
|
||
SizedBox(height: 24.h),
|
||
_buildReservationFormCard(context),
|
||
SizedBox(height: 180.h),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
Positioned(
|
||
left: 20.w,
|
||
right: 20.w,
|
||
bottom: 110.h,
|
||
child: _buildReservationItem(context),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
/// 构建用户信息卡片
|
||
Widget _buildUserInfoCard() {
|
||
return Card(
|
||
elevation: 1,
|
||
color: Colors.white,
|
||
margin: EdgeInsets.zero,
|
||
shape: const RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.only(
|
||
bottomLeft: Radius.circular(20),
|
||
bottomRight: Radius.circular(20),
|
||
),
|
||
),
|
||
child: Column(
|
||
children: [
|
||
Padding(
|
||
padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 16, top: 50),
|
||
child: Row(
|
||
children: [
|
||
Stack(
|
||
children: [
|
||
CircleAvatar(
|
||
radius: 25,
|
||
backgroundColor: Colors.white,
|
||
child: LoginUtil.getAssImg('ic_user_logo@2x'),
|
||
),
|
||
Positioned(
|
||
right: 0,
|
||
bottom: 0,
|
||
child: SizedBox(
|
||
height: 16.h,
|
||
width: 16.w,
|
||
child: LoginUtil.getAssImg('ic_logo@2x'),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
SizedBox(width: 8.w),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Row(
|
||
children: [
|
||
Text(
|
||
"${StorageService.to.name}",
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
),
|
||
SizedBox(width: 8.w),
|
||
Container(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: 8,
|
||
vertical: 2,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: const Color.fromRGBO(236, 255, 234, 1),
|
||
border: Border.all(color: const Color(0xFFB7E19F)),
|
||
borderRadius: BorderRadius.circular(12),
|
||
),
|
||
child: const Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Icon(Icons.eco, size: 12, color: Color(0xFF52C41A)),
|
||
SizedBox(width: 4),
|
||
Text(
|
||
"绿色先锋",
|
||
style: TextStyle(
|
||
color: Color(0xFF52C41A),
|
||
fontSize: 10,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 4),
|
||
Text(
|
||
"羚牛ID:${StorageService.to.phone}",
|
||
style: const TextStyle(color: Colors.grey, fontSize: 11),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
IconButton(
|
||
onPressed: () async {
|
||
var scanResult = await Get.to(() => const MessagePage());
|
||
if (scanResult == null) {
|
||
controller.msgNotice();
|
||
}
|
||
},
|
||
icon: Badge(
|
||
smallSize: 8,
|
||
backgroundColor: controller.isNotice
|
||
? Colors.red
|
||
: Colors.transparent,
|
||
child: const Icon(
|
||
Icons.notifications_outlined,
|
||
color: Colors.black87,
|
||
size: 30,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
Padding(
|
||
padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 20),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
_buildModernStatItem('累计加氢量', '', controller.fillingWeight, ''),
|
||
const SizedBox(width: 8),
|
||
_buildModernStatItem('总加氢次数', '', controller.fillingTimes, ''),
|
||
const SizedBox(width: 8),
|
||
_buildModernStatItem('今日里程', '', "7kg", ''),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildModernStatItem(String title, String subtitle, String value, String unit) {
|
||
return Expanded(
|
||
child: Container(
|
||
padding: const EdgeInsets.all(12.0),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFF8F9FA),
|
||
borderRadius: BorderRadius.circular(12),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
title,
|
||
style: const TextStyle(
|
||
fontSize: 12,
|
||
fontWeight: FontWeight.bold,
|
||
color: Colors.black87,
|
||
),
|
||
),
|
||
Text(subtitle, style: const TextStyle(fontSize: 9, color: Colors.grey)),
|
||
const SizedBox(height: 8),
|
||
Row(
|
||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||
textBaseline: TextBaseline.alphabetic,
|
||
children: [
|
||
Text(
|
||
value,
|
||
style: const TextStyle(
|
||
fontSize: 16,
|
||
fontWeight: FontWeight.w600,
|
||
color: Colors.black87,
|
||
),
|
||
),
|
||
Text(unit, style: const TextStyle(fontSize: 10, color: Colors.black54)),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildCarInfoCard() {
|
||
return Card(
|
||
elevation: 2,
|
||
color: Colors.white,
|
||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||
child: Padding(
|
||
padding: const EdgeInsets.all(16.0),
|
||
child: Row(
|
||
children: [
|
||
Expanded(flex: 4, child: LoginUtil.getAssImg('ic_car_bg@2x')),
|
||
const SizedBox(width: 16),
|
||
Expanded(
|
||
flex: 6,
|
||
child: Column(
|
||
children: [
|
||
_buildCarDataItem('剩余电量', '36.8%'),
|
||
const SizedBox(height: 8),
|
||
_buildCarDataItem('剩余氢量', '${controller.leftHydrogen}Kg'),
|
||
const SizedBox(height: 8),
|
||
_buildCarDataItem('百公里氢耗', '${controller.workEfficiency}Kg'),
|
||
const SizedBox(height: 12),
|
||
Column(
|
||
children: [
|
||
ClipRRect(
|
||
borderRadius: BorderRadius.circular(4),
|
||
child: LinearProgressIndicator(
|
||
value: controller.progressValue,
|
||
minHeight: 6,
|
||
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,
|
||
fontWeight: FontWeight.w400,
|
||
),
|
||
),
|
||
Text(
|
||
"${controller.leftHydrogen}Kg",
|
||
style: const TextStyle(
|
||
fontSize: 10,
|
||
color: Color(0xFF006633),
|
||
fontWeight: FontWeight.w600,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildCarDataItem(String label, String value) {
|
||
return Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(label, style: const TextStyle(fontSize: 12, color: Colors.grey)),
|
||
Text(
|
||
value,
|
||
style: const TextStyle(
|
||
fontSize: 12,
|
||
fontWeight: FontWeight.w600,
|
||
color: Colors.black87,
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// --- 构建预约表单卡片---
|
||
Widget _buildReservationFormCard(BuildContext context) {
|
||
return Column(
|
||
children: [
|
||
// 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(height: 12),
|
||
// 2. 底部:氢量与站点
|
||
Row(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Expanded(child: _buildAmountSliderSection()),
|
||
const SizedBox(width: 12),
|
||
Expanded(child: _buildStationCardSection(context)),
|
||
],
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// 水平日期选择器
|
||
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(
|
||
width: 58,
|
||
margin: const EdgeInsets.only(right: 12),
|
||
decoration: BoxDecoration(
|
||
color: isSelected ? const Color(0xFF006633) : const Color(0xFFF8F9FA),
|
||
borderRadius: BorderRadius.circular(14),
|
||
),
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
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]),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
},
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 时间 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,
|
||
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: 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(
|
||
children: [
|
||
IconButton(
|
||
onPressed: () => _updateAmount(-1),
|
||
icon: Icon(Icons.remove, size: 14.sp, color: Colors.grey),
|
||
),
|
||
Expanded(
|
||
child: Text(
|
||
"${controller.amountController.text}Kg",
|
||
textAlign: TextAlign.center,
|
||
softWrap: false,
|
||
overflow: TextOverflow.fade,
|
||
style: TextStyle(
|
||
fontSize: 11.sp,
|
||
color: Colors.black87,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
),
|
||
IconButton(
|
||
onPressed: () => _updateAmount(1),
|
||
icon: Icon(Icons.add, size: 14.sp, 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;
|
||
|
||
// 获取最大值,注意处理 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.current = newAmount;
|
||
|
||
// 移动光标到末尾,防止光标跳到前面
|
||
controller.amountController.selection = TextSelection.fromPosition(
|
||
TextPosition(offset: controller.amountController.text.length),
|
||
);
|
||
controller.updateUi();
|
||
}
|
||
|
||
Widget _buildReservationItem(BuildContext context) {
|
||
return Row(
|
||
children: [
|
||
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,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
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),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// 构建下拉菜单中的每一项
|
||
Widget _buildDropdownItem(StationModel station) {
|
||
bool isSelectable = (station.isSelect == 1);
|
||
//营运并且可用
|
||
bool isMaintenance = ((station.siteStatusName != '营运中') && isSelectable);
|
||
final textColor = isSelectable ? Colors.black : Colors.grey;
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||
child: Row(
|
||
children: [
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Row(
|
||
crossAxisAlignment: CrossAxisAlignment.center,
|
||
mainAxisAlignment: MainAxisAlignment.start,
|
||
children: [
|
||
Flexible(
|
||
child: Text(
|
||
station.name,
|
||
style: TextStyle(
|
||
color: textColor,
|
||
fontSize: 14,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
overflow: TextOverflow.ellipsis,
|
||
maxLines: 1,
|
||
),
|
||
),
|
||
Text(
|
||
' | ¥${station.price}/kg',
|
||
style: TextStyle(
|
||
color: textColor,
|
||
fontSize: 14,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
if (isMaintenance) const SizedBox(width: 8),
|
||
if (isMaintenance)
|
||
Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||
decoration: BoxDecoration(
|
||
color: Colors.orange[100],
|
||
borderRadius: BorderRadius.circular(4),
|
||
),
|
||
child: Text(
|
||
station.siteStatusName,
|
||
style: TextStyle(
|
||
fontSize: 10,
|
||
color: Colors.orange,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 4),
|
||
Text(
|
||
'${station.address}',
|
||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildSelectedStationButton(StationModel station) {
|
||
return Container(
|
||
// 选中后样式
|
||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(8),
|
||
border: Border.all(color: Colors.grey[400]!),
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Expanded(child: _buildDropdownItem(station)),
|
||
const Icon(Icons.arrow_drop_down, color: Colors.grey, size: 24),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|