Compare commits
13 Commits
dev_featur
...
45f5035d1b
| Author | SHA1 | Date | |
|---|---|---|---|
| 45f5035d1b | |||
| f792915429 | |||
| edbacc502b | |||
| 5722e3ace0 | |||
| d41b21654a | |||
| 2eb059defd | |||
| fbcc85af2a | |||
| 9a97b56505 | |||
| 8302d7c179 | |||
| e7a9e4483a | |||
| 9b64fdfa52 | |||
| d8f335eb4e | |||
| d1b7a9eb76 |
@@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
61
ln_jq_app/android/app/proguard-rules.pro
vendored
Normal 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.**
|
||||
|
Before Width: | Height: | Size: 6.0 KiB |
BIN
ln_jq_app/android/app/src/main/res/mipmap-xhdpi/logo.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
@@ -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"
|
||||
|
||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 465 B |
|
After Width: | Height: | Size: 849 B |
|
Before Width: | Height: | Size: 848 B After Width: | Height: | Size: 849 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 613 B |
|
After Width: | Height: | Size: 613 B |
|
Before Width: | Height: | Size: 707 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 2.6 KiB |
@@ -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 = "";
|
||||
|
||||
//加氢站相关查询
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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("加载中");
|
||||
@@ -66,10 +104,15 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
||||
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"] ?? "";
|
||||
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;
|
||||
@@ -112,7 +179,9 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.d("解析失败: $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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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成功');
|
||||
|
||||
@@ -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成功');
|
||||
|
||||
@@ -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
|
||||
|
||||