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/pages/b_page/history/view.dart'; import 'package:ln_jq_app/pages/c_page/message/view.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'controller.dart'; ///加氢预约 class SitePage extends GetView { const SitePage({super.key}); @override Widget build(BuildContext context) { return GetBuilder( init: SiteController(), id: 'site', builder: (_) { return Scaffold( backgroundColor: Color.fromRGBO(247, 249, 251, 1), body: SmartRefresher( controller: controller.refreshController, enablePullUp: false, onRefresh: controller.onRefresh, child: 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: () { // 手动录入 controller.confirmReservation("", isAdd: true); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all(color: const Color(0xFFEEEEEE)), ), child: Row( children: [ const Icon( Icons.add_circle_outline, size: 18, color: Color(0xFF666666), ), const SizedBox(width: 4), Text( "无预约车辆加氢", style: TextStyle( color: const Color(0xFF666666), fontSize: 13.sp, ), ), ], ), ), ), ], ), ), // 第二个卡片: 预约信息 Column( children: [ _buildSearchView(), SizedBox(height: 15.h), controller.hasReservationData ? _buildReservationListView() : _buildEmptyReservationView(), ], ), SizedBox(height: 35.h), //第三部分 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, ), ), ], ), ], ), ), ], ), ), SizedBox(height: 105.h), ], ); } 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), ), ], ), ), _buildDropdownMenu(), ], ), 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 _buildDropdownMenu() { return PopupMenuButton( icon: Container(child: const Icon(Icons.grid_view_rounded, size: 24)), onSelected: (value) async { if (value == 'message') { var scanResult = await Get.to(() => const MessagePage()); if (scanResult == null) { controller.msgNotice(); } } else if (value == 'history') { Get.to(() => const HistoryPage(), arguments: {'stationName': controller.name}); } }, itemBuilder: (context) => [ const PopupMenuItem( value: 'message', child: Row( children: [ Icon(Icons.notifications_none, size: 20), SizedBox(width: 8), Text('消息中心'), ], ), ), const PopupMenuItem( value: 'history', child: Row( children: [Icon(Icons.history, size: 20), SizedBox(width: 8), Text('加氢历史')], ), ), ], ); } 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: EdgeInsets.fromLTRB(0, 0, 0, 8.h), child: Row( children: [ Expanded( child: SizedBox( height: 42.h, child: TextField( textAlignVertical: TextAlignVertical.center, controller: controller.searchController, // 绑定控制器 decoration: InputDecoration( filled: true, isDense: true, fillColor: Colors.white, hintText: '输入车牌号或手机号搜索...', contentPadding: const EdgeInsets.symmetric( vertical: 12, horizontal: 16, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Color.fromRGBO(229, 231, 235, 1)), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Color.fromRGBO(229, 231, 235, 1)), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Color.fromRGBO(229, 231, 235, 1), width: 1.5, ), ), // 清除按钮 suffix: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.clear, size: 20), onPressed: () { controller.searchController.clear(); controller.fetchReservationData(); // 清除后也刷新一次 }, ), GestureDetector( onTap: () { // 点击“搜索”按钮时触发 FocusScope.of(Get.context!).unfocus(); // 收起键盘 controller.fetchReservationData(); }, child: Container( padding: EdgeInsets.only( left: 16.w, right: 16.h, top: 4.5.h, bottom: 4.5.h, ), decoration: BoxDecoration( color: Color.fromRGBO(1, 113, 55, 1), borderRadius: BorderRadius.circular(8), ), child: Text( "搜索", style: TextStyle( color: Colors.white, fontWeight: FontWeight.w500, fontSize: 12.sp, ), ), ), ), ], ), ), onSubmitted: (value) { // 用户在键盘上点击“完成”或“搜索”时触发 controller.fetchReservationData(); }, ), ), ), ], ), ); } /// 构建“暂无预约数据”的视图 Widget _buildEmptyReservationView() { return Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 48.0), color: Colors.white, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ LoginUtil.getAssImg("ic_no_data@2x"), SizedBox(height: 16.h), Text( '暂无订单', style: TextStyle( fontSize: 16.sp, color: Colors.black54, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 8), ], ), ); } /// 构建“有预约数据”的列表视图 Widget _buildReservationListView() { return ListView.builder( shrinkWrap: true, padding: EdgeInsets.zero, physics: const NeverScrollableScrollPhysics(), // 因为外层已有滚动,这里禁用内部滚动 itemCount: controller.reservationList.length, itemBuilder: (context, index) { final item = controller.reservationList[index]; // 调用新的方法来构建每一项 return _buildReservationItem(index, item); }, ); } Widget _buildReservationItem(int index, ReservationModel item) { const kPrimaryGreen = Color(0xFF006D35); // 深绿 const kLineColor = Color(0xFFE0E6ED); // 线条颜色 return IntrinsicHeight( // 保证左侧竖线能跟右侧内容高度对齐 child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 1. 左侧样式区域(竖线 + 圆点) SizedBox( width: 20, child: Column( children: [ // 顶部的装饰圆点 Container( margin: EdgeInsets.only(top: 3.w), width: 8, height: 8, decoration: BoxDecoration( color: kPrimaryGreen.withOpacity(0.5), shape: BoxShape.circle, ), ), // 延伸的竖线 Expanded(child: Container(width: 1, color: kLineColor)), ], ), ), // 2. 右侧内容区域(时间 + 卡片数据) Expanded( child: Padding( padding: EdgeInsets.only(left: 10.w, bottom: 8.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 时间显示 Text( item.time, // 格式如 "09:00 - 10:00" style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.bold, color: Color(0xFF333333), ), ), const SizedBox(height: 10), // 数据卡片 _buildInfoCard(item), ], ), ), ), ], ), ); } /// 右侧具体数据卡片 Widget _buildInfoCard(ReservationModel item) { return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFFF0F0F0)), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.04), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 车牌与状态标签 Row( children: [ Text( item.plateNumber, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.bold, color: Color(0xFF333333), ), ), const SizedBox(width: 8), _buildStatusTag(item.status), Spacer(), Text( "预约量:${item.amount}", style: TextStyle( color: Color(0xFF00A870), fontSize: 13.sp, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 8), // 联系信息 Text( item.contactPerson.isEmpty || item.contactPhone.isEmpty ? "" : "${item.contactPerson} | ${item.contactPhone}", style: TextStyle( color: Color(0xFF999999), fontSize: 13.sp, fontWeight: FontWeight.w400, ), ), SizedBox(height: 6.h), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ if (item.hasDrivingAttachment) _buildInfoTag('行驶证'), if (item.hasHydrogenationAttachment) ...[ SizedBox(width: 8.w), _buildInfoTag('加氢证'), ], Spacer(), if (item.isEdit == "1") ...[ const SizedBox(height: 15), const Divider(height: 1, color: Color(0xFFF5F5F5)), const SizedBox(height: 12), Align( alignment: Alignment.centerRight, child: _buildSmallButton( "修改信息", isOutline: true, onTap: () { controller.confirmReservation(item.id, isEdit: true); }, ), ), ] else if (item.status == ReservationStatus.pending) ...[ const SizedBox(height: 15), const Divider(height: 1, color: Color(0xFFF5F5F5)), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ _buildSmallButton( "拒绝", isOutline: true, onTap: () { controller.rejectReservation(item.id); }, ), const SizedBox(width: 12), _buildSmallButton( "确认", isOutline: false, onTap: () { controller.confirmReservation(item.id); }, ), ], ), ], ], ), ], ), ); } Widget _buildInfoTag(String label) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: const Color.fromRGBO(242, 243, 245, 1), borderRadius: BorderRadius.circular(8), ), child: Text( label, style: TextStyle(color: Color.fromRGBO(78, 89, 105, 1), fontSize: 11.sp), ), ); } Widget _buildSmallButton( String text, { required bool isOutline, required VoidCallback onTap, }) { const kPrimaryGreen = Color(0xFF006D35); var kDangerRed = text.contains('修改') ? Colors.red : Color.fromRGBO(255, 142, 98, 1); return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), decoration: BoxDecoration( color: isOutline ? Colors.white : kPrimaryGreen, borderRadius: BorderRadius.circular(10), border: Border.all(color: isOutline ? kDangerRed : kPrimaryGreen), ), child: Text( text, style: TextStyle( color: isOutline ? kDangerRed : Colors.white, fontSize: 14, fontWeight: FontWeight.bold, ), ), ), ); } /// 状态标签构建(带圆角和浅色背景) Widget _buildStatusTag(ReservationStatus status) { Color textColor; Color bgColor; String text; switch (status) { case ReservationStatus.pending: text = "待加氢"; textColor = const Color(0xFFFE9E62); // 橘色 bgColor = const Color(0xFFFFF2E9); break; case ReservationStatus.completed: // 假设已加氢状态 text = "已加氢"; textColor = const Color(0xFF00A870); // 绿色 bgColor = const Color(0xFFE6F7F1); break; case ReservationStatus.rejected: text = "拒绝加氢"; textColor = const Color(0xFFFF7D7D); // 红色 bgColor = const Color(0xFFFFEEEE); break; case ReservationStatus.unadded: text = '未加氢'; textColor = const Color(0xFFFF7D7D); // 红色 bgColor = const Color(0xFFFFEEEE); break; case ReservationStatus.cancel: text = '已取消'; textColor = const Color(0xFFFF7D7D); // 红色 bgColor = const Color(0xFFFFEEEE); break; default: text = "未知"; textColor = Colors.grey; bgColor = Colors.grey[100]!; } return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(8), border: Border.all(color: textColor.withOpacity(0.3)), ), child: Text( text, style: TextStyle(color: textColor, fontSize: 11, fontWeight: FontWeight.bold), ), ); } }