13 Commits

Author SHA1 Message Date
45f5035d1b 更换logo 2026-01-19 10:13:37 +08:00
f792915429 bugfix 2026-01-16 17:47:53 +08:00
edbacc502b 线上域名修改 2026-01-16 17:25:52 +08:00
5722e3ace0 ios build 2026-01-16 15:00:45 +08:00
d41b21654a 改用账号推送 2026-01-16 14:35:11 +08:00
2eb059defd 定时器黄字2位小数 2026-01-16 13:35:38 +08:00
fbcc85af2a 站点增加消息入口 2026-01-16 13:06:35 +08:00
9a97b56505 定时器显示调整 2026-01-15 17:55:07 +08:00
8302d7c179 优化问题修改 2026-01-15 16:28:59 +08:00
e7a9e4483a 优化定时器 弹窗 2026-01-15 13:29:02 +08:00
9b64fdfa52 优化定时器 2026-01-15 13:24:02 +08:00
d8f335eb4e 优化 2026-01-15 09:30:47 +08:00
d1b7a9eb76 v1.2.2 version 2026-01-14 13:21:54 +08:00
46 changed files with 372 additions and 114 deletions

View File

@@ -1,3 +1,6 @@
import java.util.Properties
import java.io.FileInputStream
plugins {
id("com.android.application")
id("kotlin-android")
@@ -5,6 +8,14 @@ plugins {
id("dev.flutter.flutter-gradle-plugin")
}
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystorePropertiesFile.inputStream().use { input ->
keystoreProperties.load(input.reader(Charsets.UTF_8))
}
}
android {
namespace = "com.lnkj.ln_jq_app"
compileSdk = flutter.compileSdkVersion
@@ -26,15 +37,30 @@ android {
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = 4
versionName = "1.2.1"
versionCode = 5
versionName = "1.2.2"
}
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String?
keyPassword = keystoreProperties["keyPassword"] as String?
val storeFilePath = keystoreProperties["storeFile"] as String?
storeFile = if (storeFilePath != null) file(storeFilePath) else null
storePassword = keystoreProperties["storePassword"] as String?
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
getByName("release") {
// 使用上面定义的 release 签名
signingConfig = signingConfigs.getByName("release")
// 修复混淆规则引用语法
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}

View File

@@ -0,0 +1,61 @@
# Please add these rules to your existing keep rules in order to suppress warnings.
# This is generated automatically by the Android Gradle plugin.
# 忽略 Google Play Core 相关的缺失警告(解决你目前的报错)
-dontwarn com.google.android.play.core.**
# Flutter 基础规则
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-dontwarn com.huawei.android.os.BuildEx$VERSION
-dontwarn com.huawei.hianalytics.process.HiAnalyticsConfig$Builder
-dontwarn com.huawei.hianalytics.process.HiAnalyticsConfig
-dontwarn com.huawei.hianalytics.process.HiAnalyticsInstance$Builder
-dontwarn com.huawei.hianalytics.process.HiAnalyticsInstance
-dontwarn com.huawei.hianalytics.process.HiAnalyticsManager
-dontwarn com.huawei.hianalytics.util.HiAnalyticTools
-dontwarn com.huawei.hms.availableupdate.UpdateAdapterMgr
-dontwarn com.huawei.libcore.io.ExternalStorageFile
-dontwarn com.huawei.libcore.io.ExternalStorageFileInputStream
-dontwarn com.huawei.libcore.io.ExternalStorageFileOutputStream
-dontwarn com.huawei.libcore.io.ExternalStorageRandomAccessFile
-dontwarn org.android.netutil.PingEntry
-dontwarn org.android.netutil.PingResponse
-dontwarn org.android.netutil.PingTask
-dontwarn org.bouncycastle.crypto.BlockCipher
-dontwarn org.bouncycastle.crypto.engines.AESEngine
-dontwarn org.bouncycastle.crypto.prng.SP800SecureRandom
-dontwarn org.bouncycastle.crypto.prng.SP800SecureRandomBuilder
-keepclasseswithmembernames class ** {
native <methods>;
}
-keepattributes Signature
-keep class sun.misc.Unsafe { *; }
-keep class com.taobao.** {*;}
-keep class com.alibaba.** {*;}
-keep class com.alipay.** {*;}
-keep class com.ut.** {*;}
-keep class com.ta.** {*;}
-keep class anet.**{*;}
-keep class anetwork.**{*;}
-keep class org.android.spdy.**{*;}
-keep class org.android.agoo.**{*;}
-keep class android.os.**{*;}
-keep class org.json.**{*;}
-dontwarn com.taobao.**
-dontwarn com.alibaba.**
-dontwarn com.alipay.**
-dontwarn anet.**
-dontwarn org.android.spdy.**
-dontwarn org.android.agoo.**
-dontwarn anetwork.**
-dontwarn com.ut.**
-dontwarn com.ta.**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -13,7 +13,7 @@
"size" : "20x20"
},
{
"filename" : "Icon-App-29x29@1x.png",
"filename" : "Icon-App-29x29 1.png",
"idiom" : "iphone",
"scale" : "1x",
"size" : "29x29"
@@ -31,25 +31,25 @@
"size" : "29x29"
},
{
"filename" : "Icon-App-80x80.jpg",
"filename" : "Icon-App-80x80.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "Icon-App-120x120.jpg",
"filename" : "Icon-App-120x120.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"filename" : "Icon-App-120x120 1.jpg",
"filename" : "Icon-App-120x120 1.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "Icon-App-180.jpg",
"filename" : "Icon-App-180.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
@@ -61,19 +61,19 @@
"size" : "20x20"
},
{
"filename" : "Icon-App-20x20@2x.png",
"filename" : "Icon-App-20x20@2x 1.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "Icon-App-29x29@1x.png",
"filename" : "Icon-App-29x29.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "Icon-App-29x29@2x.png",
"filename" : "Icon-App-29x29@2x 1.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
@@ -91,7 +91,7 @@
"size" : "40x40"
},
{
"filename" : "Icon-App-76x76@1x.png",
"filename" : "Icon-App-76x76.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 B

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 848 B

After

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -8,11 +8,11 @@ class AppTheme {
static const Color themeColor = Color(0xFF0c83c3);
//是否开放域名切换
static const bool is_show_host = true;
static const bool is_show_host = false;
//http://192.168.110.222:8080/
//http://192.168.110.44:8080/
static String test_service_url = "https://beta-esg.api.lnh2e.com/";
static String test_service_url = "http://47.101.201.13:8443/api/";
static const String release_service_url = "";
//加氢站相关查询

View File

@@ -15,7 +15,7 @@ void main() async {
WidgetsFlutterBinding.ensureInitialized();
WidgetsBinding widgetsBinding = await init(
isDebug: true,
isDebug: false,
logTag: '小羚羚',
supportedLocales: [Locale('zh', 'CN')],
);
@@ -31,7 +31,7 @@ void main() async {
// 设计稿尺寸 单位dp
designSize: const Size(390, 844),
// Getx Log
enableLog: true,
enableLog: false,
// 默认的跳转动画
defaultTransition: Transition.rightToLeft,
// 主题模式
@@ -74,6 +74,9 @@ void initHttpSet() {
// 设置全局响应处理器
HttpService.to.setOnResponseHandler((response) async {
try {
if (response.data == null) {
return null;
}
final baseModel = BaseModel.fromJson(response.data);
if (baseModel.code == 0 || baseModel.code == 200) {

View File

@@ -173,6 +173,10 @@ class HistoryPage extends GetView<HistoryController> {
text = '未加氢';
color = Colors.red;
break;
case ReservationStatus.cancel:
text = '已取消';
color = Colors.red;
break;
default:
text = '未知状态';
color = Colors.grey;

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -33,6 +35,9 @@ class ReservationController extends GetxController with BaseControllerMixin {
final TextEditingController broadcastContentController = TextEditingController();
final RxInt selectedTabIndex = 0.obs;
@override
bool get listenLifecycleEvent => true;
@override
void onInit() {
super.onInit();
@@ -40,6 +45,38 @@ class ReservationController extends GetxController with BaseControllerMixin {
customStartTime = DateTime.now();
customEndTime = customStartTime!.add(const Duration(days: 1));
renderData();
startAutoRefresh();
}
@override
void onPaused() {
stopAutoRefresh();
super.onPaused();
}
@override
void onClose() {
stopAutoRefresh();
broadcastTitleController.dispose();
broadcastContentController.dispose();
super.onClose();
}
void startAutoRefresh() {
// 先停止已存在的定时器,防止重复启动
stopAutoRefresh();
// 创建一个每5分钟执行一次的周期性定时器
_refreshTimer = Timer.periodic(const Duration(minutes: 5), (timer) {
renderData();
});
}
///停止定时器的方法
void stopAutoRefresh() {
// 如果定时器存在并且是激活状态,就取消它
_refreshTimer?.cancel();
_refreshTimer = null; // 置为null方便判断
}
String name = "";
@@ -56,6 +93,7 @@ class ReservationController extends GetxController with BaseControllerMixin {
String jobTipStr = "";
String jobDetailsStr = "";
String jobId = "";
Timer? _refreshTimer;
Future<void> renderData() async {
showLoading("加载中");
@@ -65,11 +103,16 @@ class ReservationController extends GetxController with BaseControllerMixin {
if (jobData != null) {
final jobDataResult = BaseModel.fromJson(jobData.data);
if (jobDataResult.code == 0) {
try{
jobId = jobDataResult.data["id"] ?? "";
String endTime = jobDataResult.data["endTime"] ?? "";
String beginTime = jobDataResult.data["beginTime"] ?? "";
String hydStatus = jobDataResult.data["hydStatus"] ?? "";
try {
final List<dynamic> dataList = jobDataResult.data is List
? jobDataResult.data
: [];
final firstJob = dataList[0];
jobId = firstJob["id"] ?? "";
String endTime = firstJob["endTime"] ?? "";
String beginTime = firstJob["beginTime"] ?? "";
String hydStatus = firstJob["hydStatus"].toString() ?? "";
String hydStatusStr = "";
if (hydStatus == "0") {
hydStatusStr = "营运中";
@@ -81,28 +124,52 @@ class ReservationController extends GetxController with BaseControllerMixin {
hydStatusStr = "暂停营业";
}
jobDetailsStr = "当前站点已设置$beginTime-$endTime为$hydStatusStr状态";
//现在的时间晚于开始时间就不显示文案
bool isJobStarted = false;
try {
if (beginTime.isNotEmpty) {
DateTime beginDateTime = DateTime.parse(beginTime);
if (DateTime.now().isAfter(beginDateTime)) {
isJobStarted = true;
}
}
} catch (e) {
print("开始时间解析失败: $e");
}
if (isJobStarted) {
jobTipStr = "";
}
//结束时间
if (endTime.isNotEmpty) {
try {
// 解析时间字符串
DateTime endDateTime = DateTime.parse(endTime);
DateTime beginDateTime = DateTime.parse(beginTime);
DateTime now = DateTime.now(); // 2. 计算时间差 (endTime - now)
DateTime now = DateTime.now(); //计算时间差 (endTime - now)
Duration diff = endDateTime.difference(now);
// 计算小时数 (允许小数,例如 0.5)
// inMinutes / 60 可以得到更精确的小数小时
double hoursLeft = diff.inMinutes / 60.0;
if (hoursLeft > 0) {
//计算当前时间-开始时间
Duration startDiff = beginDateTime.difference(now);
double hoursUntilStart = startDiff.inMinutes / 60.0;
// 只有在【当前时间早于开始时间】且【剩余时间大于0】时才显示文案
if (now.isBefore(beginDateTime) && hoursLeft > 0) {
// 如果是正数,表示还有多久结束
String timeTip = "${hoursLeft.toStringAsFixed(1)}小时后";
String timeTip = " ${hoursUntilStart.toStringAsFixed(2)}小时后";
jobTipStr = "$timeTip$hydStatusStr";
} else {
jobTipStr = "";
}
jobDetailsStr =
"当前站点已设置$beginTime至$endTime,共${hoursLeft.toStringAsFixed(2)}小时,为$hydStatusStr状态";
// 如果是处于非营运状态,自动回填开始和结束时间
// 假设 customStartTime 是现在customEndTime 是接口返回的结束时间
customStartTime = beginDateTime;
@@ -111,8 +178,10 @@ class ReservationController extends GetxController with BaseControllerMixin {
print("时间解析失败: $e");
}
}
}catch (e){
Logger.d("解析失败: $e");
} catch (e) {
Logger.d("解析失败或者没返回信息: $e");
jobTipStr = "";
}
}
}
@@ -282,6 +351,7 @@ class ReservationController extends GetxController with BaseControllerMixin {
'appointment/station/updateStationStatus',
data: {
'hydrogenId': hydrogenId,
'name': name,
'siteStatus': selectedOperationStatus == "营运中"
? "0"
: selectedOperationStatus == "维修中"
@@ -309,6 +379,9 @@ class ReservationController extends GetxController with BaseControllerMixin {
var result = BaseModel.fromJson(responseData.data);
if (result.code == 0) {
showSuccessToast("保存成功,已同步通知对应司机");
//重新刷新页面
renderData();
}
dismissLoading();
} catch (e) {
@@ -325,10 +398,7 @@ class ReservationController extends GetxController with BaseControllerMixin {
DialogX.to.showConfirmDialog(
title: '当前设置详情',
content: Text(
jobDetailsStr,
style: const TextStyle(fontSize: 15, height: 1.5),
),
content: Text(jobDetailsStr, style: const TextStyle(fontSize: 15, height: 1.5)),
confirmText: '好的',
cancelText: '取消设置',
onCancel: () {
@@ -342,9 +412,7 @@ class ReservationController extends GetxController with BaseControllerMixin {
void _cancelJob() async {
showLoading("正在取消...");
try {
var response = await HttpService.to.delete(
'appointment/job/hyd/$jobId',
);
var response = await HttpService.to.delete('appointment/job/hyd/$jobId');
dismissLoading();
if (response != null) {
@@ -404,11 +472,4 @@ class ReservationController extends GetxController with BaseControllerMixin {
await StorageService.to.clearLoginInfo();
Get.offAll(() => LoginPage());
}
@override
void onClose() {
broadcastTitleController.dispose();
broadcastContentController.dispose();
super.onClose();
}
}

View File

@@ -197,7 +197,8 @@ class ReservationPage extends GetView<ReservationController> {
controller.jobTipStr,
style: TextStyle(color: Colors.yellow[800], fontSize: 14),
),
Icon(AntdIcon.question_circle, size: 14, color: Colors.yellow[800]),
SizedBox(width: 2.w),
Icon(AntdIcon.info_circle, size: 14, color: Colors.yellow[800]),
],
),
),

View File

@@ -12,11 +12,13 @@ enum ReservationStatus {
completed, // 完成 ( addStatus: 1)
rejected, // 拒绝 ( -1)
unadded, // 未加 ( 2)
cancel, // 取消预约
unknown, // 未知状态
}
class ReservationModel {
final String id;
final String stationId;
final String plateNumber;
String amount;
final String time;
@@ -40,6 +42,7 @@ class ReservationModel {
ReservationModel({
required this.id,
required this.stationId,
required this.plateNumber,
required this.amount,
required this.time,
@@ -80,6 +83,9 @@ class ReservationModel {
case 2:
currentStatus = ReservationStatus.unadded;
break;
case 6:
currentStatus = ReservationStatus.cancel;
break;
default:
currentStatus = ReservationStatus.unknown;
}
@@ -97,6 +103,7 @@ class ReservationModel {
return ReservationModel(
// 原始字段用于UI兼容
id: json['id']?.toString() ?? '',
stationId: json['stationId']?.toString() ?? '',
plateNumber: json['plateNumber']?.toString() ?? '未知车牌',
amount: '${json['hydAmount']?.toString() ?? '0'}kg',
time: timeRange,
@@ -137,20 +144,50 @@ class SiteController extends GetxController with BaseControllerMixin {
final TextEditingController searchController = TextEditingController();
@override
bool get listenLifecycleEvent => true;
@override
void onInit() {
super.onInit();
renderData();
msgNotice();
startAutoRefresh();
}
@override
void onPaused() {
stopAutoRefresh();
super.onPaused();
}
@override
void onClose() {
stopAutoRefresh();
searchController.dispose();
super.onClose();
}
bool isNotice = false;
Future<void> msgNotice() async {
final Map<String, dynamic> requestData = {
'appFlag': 1,
'isRead': 1,
'pageNum': 1,
'pageSize': 5,
};
final response = await HttpService.to.get(
'appointment/unread_notice/page',
params: requestData,
);
if (response != null) {
final result = BaseModel.fromJson(response.data);
if (result.code == 0 && result.data != null) {
String total = result.data["total"].toString();
isNotice = int.parse(total) > 0;
updateUi();
}
}
}
void startAutoRefresh() {
// 先停止已存在的定时器,防止重复启动
@@ -162,7 +199,7 @@ class SiteController extends GetxController with BaseControllerMixin {
});
}
/// 【6. 新增】停止定时器的方法
///停止定时器的方法
void stopAutoRefresh() {
// 如果定时器存在并且是激活状态,就取消它
_refreshTimer?.cancel();
@@ -184,6 +221,7 @@ class SiteController extends GetxController with BaseControllerMixin {
'pageNum': 1,
'pageSize': 50, // 暂时不考虑分页一次获取30条
'keyword': searchText, // 加氢站名称、手机号
'stationId': StorageService.to.userId
},
);
@@ -580,7 +618,6 @@ class SiteController extends GetxController with BaseControllerMixin {
}
} catch (e) {
} finally {
//加载列表数据
fetchReservationData();
}

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:getx_scaffold/getx_scaffold.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' show MessagePage;
import 'controller.dart';
@@ -42,9 +43,11 @@ class SitePage extends GetView<SiteController> {
children: [
const Icon(Icons.calendar_today, color: Colors.blue, size: 32),
const SizedBox(width: 12),
const Expanded(
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'今日预约统计',
@@ -53,15 +56,9 @@ class SitePage extends GetView<SiteController> {
fontWeight: FontWeight.bold,
),
),
Text(
"Today's Reservation Statistics",
style: TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
),
SizedBox(width: 5.w,),
Container(
padding: const EdgeInsets.symmetric(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 4,
),
@@ -69,7 +66,7 @@ class SitePage extends GetView<SiteController> {
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: const Text(
child: Text(
'实时',
style: TextStyle(
color: Colors.blue,
@@ -80,6 +77,43 @@ class SitePage extends GetView<SiteController> {
),
],
),
Text(
"Today's Reservation Statistics",
style: TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
),
IconButton(
onPressed: () async {
// 跳转消息中心
var scanResult = await Get.to(() => const MessagePage());
if (scanResult == null) {
controller.msgNotice();
}
},
// 这里的 style 是为了模拟你图片里的灰色圆形背景
style: IconButton.styleFrom(
backgroundColor: Colors.grey[100],
padding: const EdgeInsets.all(8),
),
icon: Badge(
// label: Text('3'), // 如果你想显示数字,就加 label
smallSize: 8,
// 红点的大小
backgroundColor: controller.isNotice
? Colors.red
: Colors.white,
// 红点颜色
child: Icon(
Icons.notifications_outlined,
color: Colors.black87,
size: 25,
),
),
),
],
),
),
const SizedBox(height: 10),
const Divider(height: 1, indent: 16, endIndent: 16),
@@ -132,7 +166,7 @@ class SitePage extends GetView<SiteController> {
child: Row(
children: [
Text(
'今日预约信息',
'预约信息',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
@@ -156,9 +190,7 @@ class SitePage extends GetView<SiteController> {
onPressed: () {
Get.to(
() => HistoryPage(),
arguments: {
'stationName': controller.name,
},
arguments: {'stationName': controller.name},
);
},
style: ElevatedButton.styleFrom(
@@ -465,6 +497,10 @@ class SitePage extends GetView<SiteController> {
text = '未加氢';
color = Colors.red;
break;
case ReservationStatus.cancel:
text = '已取消';
color = Colors.red;
break;
default:
text = '未知状态';
color = Colors.grey;

View File

@@ -35,6 +35,7 @@ class CarInfoController extends GetxController with BaseControllerMixin {
// 如果未绑定车辆,且本次会话尚未提示过,则弹出提示
if (!StorageService.to.hasShownBindVehicleDialog &&
StorageService.to.isLoggedIn &&
StorageService.to.loginChannel == LoginChannel.driver &&
!StorageService.to.hasVehicleInfo) {
Future.delayed(const Duration(milliseconds: 500), () {
DialogX.to.showConfirmDialog(

View File

@@ -19,6 +19,10 @@ class MessageController extends GetxController {
_loadData(isRefresh: true);
}
void onRefresh() => _loadData(isRefresh: true);
void onLoading() => _loadData(isRefresh: false);
Future<void> _loadData({bool isRefresh = false}) async {
final int targetPage = isRefresh ? 1 : _pageNum + 1;
@@ -100,9 +104,7 @@ class MessageController extends GetxController {
}
}
void onRefresh() => _loadData(isRefresh: true);
void onLoading() => _loadData(isRefresh: false);
// 标记全部已读
void markAllRead() async {

View File

@@ -57,7 +57,7 @@ class MineController extends GetxController with BaseControllerMixin {
_fetchAccidentCount(), // 请求2事故数
_fetchBreakRulesCount(), // 请求3违章数
_rating(), // 司机评分
_msgNotice(), // 红点消息
msgNotice(), // 红点消息
]);
await renderViolation();
@@ -69,7 +69,7 @@ class MineController extends GetxController with BaseControllerMixin {
}
}
Future<void> _msgNotice() async {
Future<void> msgNotice() async {
final Map<String, dynamic> requestData = {
'appFlag': 1,
'isRead': 1,
@@ -85,6 +85,7 @@ class MineController extends GetxController with BaseControllerMixin {
if (result.code == 0 && result.data != null) {
String total = result.data["total"].toString();
isNotice = int.parse(total) > 0;
updateUi();
}
}
}

View File

@@ -82,9 +82,12 @@ class MinePage extends GetView<MineController> {
),
),
IconButton(
onPressed: () {
onPressed: () async {
// 跳转消息中心
Get.to(() => const MessagePage());
var scanResult = await Get.to(() => const MessagePage());
if (scanResult == null) {
controller.msgNotice();
}
},
// 这里的 style 是为了模拟你图片里的灰色圆形背景
style: IconButton.styleFrom(

View File

@@ -2,17 +2,11 @@ import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:getx_scaffold/common/common.dart';
import 'package:getx_scaffold/common/services/http.dart';
import 'package:getx_scaffold/getx_scaffold.dart';
import 'package:intl/intl.dart';
import 'package:ln_jq_app/common/model/base_model.dart';
import 'package:ln_jq_app/common/model/station_model.dart';
import 'package:ln_jq_app/common/model/vehicle_info.dart';
import 'package:ln_jq_app/pages/b_page/site/controller.dart';
import 'package:ln_jq_app/pages/c_page/reservation_edit/controller.dart';
import 'package:ln_jq_app/pages/c_page/reservation_edit/view.dart';
import 'package:ln_jq_app/pages/qr_code/view.dart';
import 'package:ln_jq_app/storage_service.dart';
@@ -341,12 +335,12 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
final startTimeStr =
'$dateStr ${_formatTimeOfDay(startTime.value)}:00'; // Use helper directly
if (lastSuccessfulReservation != null &&
/*if (lastSuccessfulReservation != null &&
lastSuccessfulReservation!.id == selectedStationId.value &&
lastSuccessfulReservation!.startTime == startTimeStr) {
showToast("请勿重复提交相同时间段的预约,可在“查看预约”中修改");
return;
}
}*/
final reservationEndDateTime = DateTime(
selectedDate.value.year,
@@ -395,7 +389,6 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
);
var result = BaseModel.fromJson(responseData?.data);
if (responseData == null || result.code != 0) {
dismissLoading();
showToast(result.error);
@@ -408,6 +401,7 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
lastSuccessfulReservation = ReservationModel(
id: selectedStationId.value!,
stationId: '',
hydAmount: ampuntStr,
startTime: startTimeStr,
endTime: endTimeStr,
@@ -546,6 +540,7 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
//用来管理查看预约的弹窗
Worker? _sheetWorker;
bool init = false;
Timer? _refreshTimer;
@override
bool get listenLifecycleEvent => true;
@@ -555,7 +550,7 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
super.onInit();
getUserBindCarInfo();
getSiteList();
startAutoRefresh();
if (!init) {
_setupListener();
init = true;
@@ -563,9 +558,26 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
}
@override
void dispose() {
_sheetWorker?.dispose();
super.dispose();
void onPaused() {
stopAutoRefresh();
super.onPaused();
}
void startAutoRefresh() {
// 先停止已存在的定时器,防止重复启动
stopAutoRefresh();
// 创建一个每1分钟执行一次的周期性定时器
_refreshTimer = Timer.periodic(const Duration(minutes: 1), (timer) {
getSiteList();
});
}
///停止定时器的方法
void stopAutoRefresh() {
// 如果定时器存在并且是激活状态,就取消它
_refreshTimer?.cancel();
_refreshTimer = null; // 置为null方便判断
}
void _setupListener() {
@@ -691,7 +703,9 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
try {
showLoading("加氢站数据加载中");
var responseData = await HttpService.to.get("appointment/station/queryHydrogenSiteInfo");
var responseData = await HttpService.to.get(
"appointment/station/queryHydrogenSiteInfo",
);
if (responseData == null || responseData.data == null) {
showToast('暂时无法获取站点信息');
@@ -743,6 +757,7 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
// 如果未绑定车辆,且本次会话尚未提示过,则弹出提示
if (!StorageService.to.hasShownBindVehicleDialog &&
StorageService.to.isLoggedIn &&
StorageService.to.loginChannel == LoginChannel.driver &&
!StorageService.to.hasVehicleInfo) {
Future.delayed(const Duration(milliseconds: 500), () {
DialogX.to.showConfirmDialog(
@@ -773,6 +788,8 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
if (_debounce != null) {
_debounce?.cancel();
}
_sheetWorker?.dispose();
stopAutoRefresh();
super.onClose();
}
}

View File

@@ -3,11 +3,9 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:getx_scaffold/getx_scaffold.dart';
import 'package:ln_jq_app/common/model/station_model.dart';
import 'package:ln_jq_app/pages/qr_code/view.dart';
import 'package:ln_jq_app/storage_service.dart';
import 'controller.dart';
import 'reservation_list_bottomsheet.dart';
///加氢预约
class ReservationPage extends GetView<C_ReservationController> {

View File

@@ -132,7 +132,6 @@ class ReservationEditController extends GetxController with BaseControllerMixin
TimeSlot tempSlot = availableSlots[initialItem];
Get.bottomSheet(
Container(
height: 300,
@@ -228,6 +227,7 @@ class ReservationEditController extends GetxController with BaseControllerMixin
'appointment/orderAddHyd/saveOrUpdate',
data: {
'id': reservation.id,
'stationId': reservation.stationId,
'startTime': startTimeStr,
'endTime': endTimeStr,
'hydAmount': amountStr,

View File

@@ -7,6 +7,7 @@ import 'package:getx_scaffold/getx_scaffold.dart';
import 'package:ln_jq_app/common/styles/theme.dart';
import 'package:ln_jq_app/pages/b_page/base_widgets/view.dart';
import 'package:ln_jq_app/pages/c_page/base_widgets/view.dart';
import 'package:ln_jq_app/pages/c_page/message/view.dart';
import 'package:ln_jq_app/pages/login/view.dart';
import '../../storage_service.dart';
@@ -156,6 +157,7 @@ class HomeController extends GetxController with BaseControllerMixin {
Future<void> _onNotificationOpened(Map<dynamic, dynamic> message) async {
Logger.d('onNotificationOpened ====> $message');
await Get.to(() => const MessagePage());
}
Future<void> _onNotificationRemoved(Map<dynamic, dynamic> message) async {

View File

@@ -208,6 +208,7 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
addAlias(phone);
//登录后查询已绑定车辆信息
try {
var carInfo = await HttpService.to.get(
"appointment/driver/getTruckInfoByDriver?phone=$phone",
);
@@ -221,6 +222,9 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
await StorageService.to.saveVehicleInfo(vehicle);
}
}
} catch (e) {
Logger.d("暂时不处理 查询车辆信息失败的情况");
}
//页面操作
dismissLoading();
@@ -400,7 +404,8 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
final _aliyunPush = AliyunPushFlutter();
void addAlias(String alias) async {
var result = await _aliyunPush.addAlias(alias);
var result = await _aliyunPush.bindAccount(alias);
var code = result['code'];
if (code == kAliyunPushSuccessCode) {
Logger.d('添加别名$alias成功');

View File

@@ -142,7 +142,7 @@ class StorageService extends GetxService {
void delAlias() async {
String phoen = StorageService.to.phone ?? "";
var result = await _aliyunPush.removeAlias(phoen);
var result = await await _aliyunPush.unbindAccount();
var code = result['code'];
if (code == kAliyunPushSuccessCode) {
Logger.d('删除别名$phoen成功');

View File

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.2.1+4
version: 1.2.2+5
environment:
sdk: ^3.9.0