586 lines
19 KiB
Dart
586 lines
19 KiB
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/styles/theme.dart';
|
||
import 'package:ln_jq_app/pages/b_page/history/view.dart';
|
||
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
||
|
||
import 'controller.dart';
|
||
|
||
///加氢预约
|
||
class SitePage extends GetView<SiteController> {
|
||
const SitePage({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return GetBuilder<SiteController>(
|
||
init: SiteController(),
|
||
id: 'site',
|
||
builder: (_) {
|
||
return Scaffold(
|
||
backgroundColor: Color(0xFFF5F7F9),
|
||
body: SingleChildScrollView(child: _buildView(context)),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
// 主视图
|
||
Widget _buildView(BuildContext context) {
|
||
return Column(
|
||
children: [
|
||
// 第一个卡片: 今日预约统计
|
||
_buildTopSection(context),
|
||
Padding(
|
||
padding: EdgeInsets.only(left: 20.w, right: 20.w),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
Padding(
|
||
padding: EdgeInsets.only(top: 17.h, bottom: 10.h),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
"预约信息",
|
||
style: TextStyle(
|
||
color: Color.fromRGBO(51, 51, 51, 1),
|
||
fontWeight: FontWeight.w600,
|
||
fontSize: 14.sp,
|
||
),
|
||
),
|
||
GestureDetector(
|
||
onTap: () {
|
||
Get.to(
|
||
() => HistoryPage(),
|
||
arguments: {'stationName': controller.name},
|
||
);
|
||
},
|
||
child: Text(
|
||
'历史记录',
|
||
style: TextStyle(
|
||
fontSize: 14.sp,
|
||
fontWeight: FontWeight.w500,
|
||
color: Color.fromRGBO(156, 163, 175, 1),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
// 第二个卡片: 预约信息
|
||
Column(
|
||
children: [
|
||
_buildSearchView(),
|
||
controller.hasReservationData
|
||
? _buildReservationListView()
|
||
: _buildEmptyReservationView(),
|
||
],
|
||
),
|
||
|
||
//第三部分
|
||
Container(
|
||
padding: const EdgeInsets.all(15),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFF1F9F6), // 极浅绿色背景
|
||
borderRadius: BorderRadius.circular(10),
|
||
),
|
||
child: Row(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Icon(
|
||
Icons.info_outline,
|
||
color: Color.fromRGBO(1, 113, 55, 1),
|
||
size: 20,
|
||
),
|
||
SizedBox(width: 8.w),
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
"系统提醒",
|
||
style: TextStyle(
|
||
color: Color.fromRGBO(1, 113, 55, 1),
|
||
fontWeight: FontWeight.bold,
|
||
fontSize: 14.sp,
|
||
),
|
||
),
|
||
SizedBox(height: 6.h),
|
||
Text(
|
||
"数据每五分钟自动刷新,如需实时更新请下拉页面",
|
||
style: TextStyle(
|
||
color: Color.fromRGBO(1, 113, 55, 0.8),
|
||
fontSize: 12.sp,
|
||
),
|
||
),
|
||
SizedBox(height: 6.h),
|
||
Text(
|
||
"如有疑问请联系客服:400-021-1773",
|
||
style: TextStyle(
|
||
color: Color.fromRGBO(1, 113, 55, 0.8),
|
||
fontSize: 12.sp,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
Widget _buildTopSection(BuildContext context) {
|
||
return Container(
|
||
width: double.infinity,
|
||
decoration: const BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: BorderRadius.vertical(bottom: Radius.circular(20)),
|
||
),
|
||
padding: EdgeInsets.only(
|
||
top: MediaQuery.of(context).padding.top + 10,
|
||
left: 20,
|
||
right: 20,
|
||
bottom: 25,
|
||
),
|
||
child: Column(
|
||
children: [
|
||
Row(
|
||
children: [
|
||
CircleAvatar(
|
||
radius: 25,
|
||
backgroundColor: Colors.white,
|
||
child: LoginUtil.getAssImg('ic_user_logo@2x'),
|
||
),
|
||
const SizedBox(width: 12),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Row(
|
||
children: [
|
||
Text(
|
||
"今日预约统计",
|
||
style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w400),
|
||
),
|
||
const SizedBox(width: 8),
|
||
],
|
||
),
|
||
const SizedBox(height: 4),
|
||
Text(
|
||
"Today's Reservation Statistics",
|
||
style: TextStyle(color: Colors.grey[500], fontSize: 13),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
IconButton(
|
||
onPressed: () {
|
||
Get.to(() => const MessagePage());
|
||
},
|
||
style: IconButton.styleFrom(
|
||
backgroundColor: Colors.grey[100],
|
||
padding: const EdgeInsets.all(8),
|
||
),
|
||
icon: Badge(
|
||
smallSize: 8,
|
||
backgroundColor: controller.isNotice ? Colors.red : Colors.transparent,
|
||
child: const Icon(
|
||
Icons.notifications_outlined,
|
||
color: Colors.black87,
|
||
size: 30,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 25),
|
||
Row(
|
||
children: [
|
||
_buildStatBox("剩余氢量", "remaining quantity", controller.leftHydrogen, "kg"),
|
||
SizedBox(width: 4.w),
|
||
_buildStatBox(
|
||
"今日加氢量",
|
||
"Have been added",
|
||
controller.orderTotalAmount,
|
||
"kg",
|
||
),
|
||
SizedBox(width: 4.w),
|
||
_buildStatBox(
|
||
"未加氢总量",
|
||
"No quantity added",
|
||
controller.orderUnfinishedAmount,
|
||
"kg",
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildStatBox(String title, String enTitle, String value, String unit) {
|
||
return Expanded(
|
||
child: Container(
|
||
padding: EdgeInsets.only(left: 12.w, top: 4.h, bottom: 4.h),
|
||
decoration: BoxDecoration(
|
||
color: Color(0xFFF5F7F9),
|
||
borderRadius: BorderRadius.circular(12),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
title,
|
||
style: TextStyle(
|
||
fontSize: 12.sp,
|
||
color: Color.fromRGBO(51, 51, 51, 0.8),
|
||
fontWeight: FontWeight.w400,
|
||
),
|
||
),
|
||
Text(enTitle, style: const TextStyle(fontSize: 9, color: Colors.grey)),
|
||
const SizedBox(height: 8),
|
||
Row(
|
||
crossAxisAlignment: CrossAxisAlignment.end,
|
||
children: [
|
||
Text(
|
||
value,
|
||
style: TextStyle(
|
||
fontSize: 12.sp,
|
||
fontWeight: FontWeight.w500,
|
||
color: Color(0xFF333333),
|
||
),
|
||
),
|
||
const SizedBox(width: 2),
|
||
Text(unit, style: const TextStyle(fontSize: 11, color: Colors.grey)),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
//搜索输入框,提示可以输入车牌或者手机
|
||
Widget _buildSearchView() {
|
||
return Padding(
|
||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
|
||
child: Row(
|
||
children: [
|
||
Expanded(
|
||
child: SizedBox(
|
||
height: 44,
|
||
child: TextField(
|
||
controller: controller.searchController, // 绑定控制器
|
||
decoration: InputDecoration(
|
||
hintText: '输入车牌号或完整手机号查询',
|
||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||
border: OutlineInputBorder(
|
||
borderRadius: BorderRadius.circular(22),
|
||
borderSide: BorderSide(color: Colors.grey.shade300),
|
||
),
|
||
enabledBorder: OutlineInputBorder(
|
||
borderRadius: BorderRadius.circular(22),
|
||
borderSide: BorderSide(color: Colors.grey.shade300),
|
||
),
|
||
focusedBorder: OutlineInputBorder(
|
||
borderRadius: BorderRadius.circular(22),
|
||
borderSide: BorderSide(color: Get.theme.primaryColor, width: 1.5),
|
||
),
|
||
// 清除按钮
|
||
suffixIcon: IconButton(
|
||
icon: const Icon(Icons.clear, size: 20),
|
||
onPressed: () {
|
||
controller.searchController.clear();
|
||
controller.fetchReservationData(); // 清除后也刷新一次
|
||
},
|
||
),
|
||
),
|
||
onSubmitted: (value) {
|
||
// 用户在键盘上点击“完成”或“搜索”时触发
|
||
controller.fetchReservationData();
|
||
},
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(width: 10),
|
||
ElevatedButton(
|
||
onPressed: () {
|
||
// 点击“搜索”按钮时触发
|
||
FocusScope.of(Get.context!).unfocus(); // 收起键盘
|
||
controller.fetchReservationData();
|
||
},
|
||
style: ElevatedButton.styleFrom(
|
||
shape: const CircleBorder(),
|
||
padding: const EdgeInsets.all(8),
|
||
),
|
||
child: const Icon(Icons.search_rounded),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建单个统计项
|
||
Widget _buildStatItem(String value, String label, {Color valueColor = Colors.blue}) {
|
||
return Expanded(
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Text(
|
||
value,
|
||
style: TextStyle(
|
||
fontSize: 18,
|
||
fontWeight: FontWeight.bold,
|
||
color: valueColor,
|
||
),
|
||
),
|
||
const SizedBox(height: 4),
|
||
Text(label, style: const TextStyle(fontSize: 14, color: Colors.grey)),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建“暂无预约数据”的视图
|
||
Widget _buildEmptyReservationView() {
|
||
return Container(
|
||
width: double.infinity,
|
||
padding: const EdgeInsets.symmetric(vertical: 48.0),
|
||
color: Colors.white,
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Icon(Icons.inventory_2_outlined, size: 80, color: Colors.grey[300]),
|
||
const SizedBox(height: 16),
|
||
const Text('暂无预约数据', style: TextStyle(fontSize: 16, color: Colors.black54)),
|
||
const SizedBox(height: 8),
|
||
const Text(
|
||
'点击右上角刷新按钮获取最新数据',
|
||
style: TextStyle(fontSize: 14, color: Colors.grey),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建“有预约数据”的列表视图
|
||
Widget _buildReservationListView() {
|
||
return Container(
|
||
color: Colors.white,
|
||
child: ListView.separated(
|
||
shrinkWrap: true,
|
||
physics: const NeverScrollableScrollPhysics(),
|
||
// 因为外层已有滚动,这里禁用内部滚动
|
||
itemCount: controller.reservationList.length,
|
||
padding: const EdgeInsets.all(12.0),
|
||
itemBuilder: (context, index) {
|
||
final item = controller.reservationList[index];
|
||
// 调用新的方法来构建每一项
|
||
return _buildReservationItem(index, item);
|
||
},
|
||
separatorBuilder: (context, index) => const SizedBox(height: 12), // 列表项之间的间距
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildReservationItem(int index, ReservationModel item) {
|
||
return Container(
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: BorderRadius.circular(8.0),
|
||
border: Border.all(color: Colors.grey[200]!, width: 1.0),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: Colors.grey.withOpacity(0.1),
|
||
spreadRadius: 1,
|
||
blurRadius: 3,
|
||
offset: const Offset(0, 2),
|
||
),
|
||
],
|
||
),
|
||
child: Column(
|
||
children: [
|
||
// 顶部:序号、车牌号、状态
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(8, 12, 16, 12),
|
||
child: Row(
|
||
children: [
|
||
// 序号
|
||
Container(
|
||
width: 24,
|
||
height: 24,
|
||
alignment: Alignment.center,
|
||
decoration: const BoxDecoration(
|
||
color: Colors.blue,
|
||
shape: BoxShape.circle,
|
||
),
|
||
child: Text(
|
||
"${index + 1}",
|
||
style: const TextStyle(
|
||
color: Colors.white,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(width: 8),
|
||
const Icon(Icons.directions_car, color: Colors.black54),
|
||
const SizedBox(width: 4),
|
||
// 车牌号
|
||
Expanded(
|
||
child: Text(
|
||
item.plateNumber,
|
||
style: const TextStyle(
|
||
fontSize: 16,
|
||
fontWeight: FontWeight.bold,
|
||
color: AppTheme.themeColor,
|
||
),
|
||
),
|
||
),
|
||
// 状态标签
|
||
_buildStatusChip(item.status),
|
||
],
|
||
),
|
||
),
|
||
const Divider(height: 1),
|
||
// 中部:详细信息
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
||
child: Column(
|
||
children: [
|
||
_buildDetailRow(
|
||
Icons.local_gas_station,
|
||
'加氢量',
|
||
item.amount,
|
||
valueColor: Colors.red,
|
||
),
|
||
const SizedBox(height: 8),
|
||
_buildDetailRow(Icons.access_time, '预约时间', item.time),
|
||
const SizedBox(height: 8),
|
||
_buildDetailRow(Icons.person, '联系人', item.contactPerson),
|
||
const SizedBox(height: 8),
|
||
_buildDetailRow(
|
||
Icons.phone,
|
||
'联系电话',
|
||
item.contactPhone,
|
||
valueColor: AppTheme.themeColor,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
// 底部:操作按钮 (仅在待处理时显示)
|
||
if (item.status == ReservationStatus.pending)
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
|
||
child: Row(
|
||
children: [
|
||
Expanded(
|
||
child: ElevatedButton(
|
||
onPressed: () => controller.confirmReservation(item.id),
|
||
style: ElevatedButton.styleFrom(backgroundColor: Colors.blue),
|
||
child: const Text('确认'),
|
||
),
|
||
),
|
||
const SizedBox(width: 16),
|
||
Expanded(
|
||
child: ElevatedButton(
|
||
onPressed: () => controller.rejectReservation(item.id),
|
||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
||
child: const Text('拒绝'),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建状态标签
|
||
Widget _buildStatusChip(ReservationStatus status) {
|
||
String text;
|
||
Color color;
|
||
switch (status) {
|
||
case ReservationStatus.pending:
|
||
text = '待加氢';
|
||
color = Colors.orange;
|
||
break;
|
||
case ReservationStatus.completed:
|
||
text = '已加氢';
|
||
color = Colors.greenAccent;
|
||
break;
|
||
case ReservationStatus.rejected:
|
||
text = '拒绝加氢';
|
||
color = Colors.red;
|
||
break;
|
||
case ReservationStatus.unadded:
|
||
text = '未加氢';
|
||
color = Colors.red;
|
||
break;
|
||
default:
|
||
text = '未知状态';
|
||
color = Colors.grey;
|
||
break;
|
||
}
|
||
return Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||
decoration: BoxDecoration(
|
||
color: color.withOpacity(0.1),
|
||
borderRadius: BorderRadius.circular(12),
|
||
),
|
||
child: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Icon(Icons.circle, color: color, size: 8),
|
||
const SizedBox(width: 4),
|
||
Text(
|
||
text,
|
||
style: TextStyle(color: color, fontSize: 12, fontWeight: FontWeight.bold),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建信息详情行
|
||
Widget _buildDetailRow(
|
||
IconData icon,
|
||
String label,
|
||
String value, {
|
||
Color valueColor = Colors.black87,
|
||
}) {
|
||
return Row(
|
||
children: [
|
||
Icon(icon, color: Colors.grey, size: 20),
|
||
const SizedBox(width: 8),
|
||
Text('$label: ', style: const TextStyle(fontSize: 14, color: Colors.grey)),
|
||
Expanded(
|
||
child: Text(
|
||
value,
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
color: valueColor,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// 底部构建带图标的提示信息行
|
||
Widget _buildInfoItem(IconData icon, String text) {
|
||
return Row(
|
||
children: [
|
||
Icon(icon, color: Colors.blue, size: 20),
|
||
const SizedBox(width: 8),
|
||
Text(text, style: const TextStyle(fontSize: 14, color: Colors.black54)),
|
||
],
|
||
);
|
||
}
|
||
}
|