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 {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("kotlin-android")
|
id("kotlin-android")
|
||||||
@@ -5,6 +8,14 @@ plugins {
|
|||||||
id("dev.flutter.flutter-gradle-plugin")
|
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 {
|
android {
|
||||||
namespace = "com.lnkj.ln_jq_app"
|
namespace = "com.lnkj.ln_jq_app"
|
||||||
compileSdk = flutter.compileSdkVersion
|
compileSdk = flutter.compileSdkVersion
|
||||||
@@ -26,15 +37,30 @@ android {
|
|||||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
minSdk = flutter.minSdkVersion
|
minSdk = flutter.minSdkVersion
|
||||||
targetSdk = flutter.targetSdkVersion
|
targetSdk = flutter.targetSdkVersion
|
||||||
versionCode = 4
|
versionCode = 5
|
||||||
versionName = "1.2.1"
|
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 {
|
buildTypes {
|
||||||
release {
|
getByName("release") {
|
||||||
// TODO: Add your own signing config for the release build.
|
// 使用上面定义的 release 签名
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
signingConfig = signingConfigs.getByName("release")
|
||||||
signingConfig = signingConfigs.getByName("debug")
|
|
||||||
|
// 修复混淆规则引用语法
|
||||||
|
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 |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 501 KiB |
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 914 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 638 B |
|
Before Width: | Height: | Size: 205 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 779 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 112 KiB |
@@ -13,7 +13,7 @@
|
|||||||
"size" : "20x20"
|
"size" : "20x20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
"filename" : "Icon-App-29x29 1.png",
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "29x29"
|
"size" : "29x29"
|
||||||
@@ -31,25 +31,25 @@
|
|||||||
"size" : "29x29"
|
"size" : "29x29"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-80x80.jpg",
|
"filename" : "Icon-App-80x80.png",
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "40x40"
|
"size" : "40x40"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-120x120.jpg",
|
"filename" : "Icon-App-120x120.png",
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "3x",
|
"scale" : "3x",
|
||||||
"size" : "40x40"
|
"size" : "40x40"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-120x120 1.jpg",
|
"filename" : "Icon-App-120x120 1.png",
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "60x60"
|
"size" : "60x60"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-180.jpg",
|
"filename" : "Icon-App-180.png",
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "3x",
|
"scale" : "3x",
|
||||||
"size" : "60x60"
|
"size" : "60x60"
|
||||||
@@ -61,19 +61,19 @@
|
|||||||
"size" : "20x20"
|
"size" : "20x20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
"filename" : "Icon-App-20x20@2x 1.png",
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "20x20"
|
"size" : "20x20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
"filename" : "Icon-App-29x29.png",
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "29x29"
|
"size" : "29x29"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
"filename" : "Icon-App-29x29@2x 1.png",
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "29x29"
|
"size" : "29x29"
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
"size" : "40x40"
|
"size" : "40x40"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-76x76@1x.png",
|
"filename" : "Icon-App-76x76.png",
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "76x76"
|
"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 |
@@ -1,5 +1,4 @@
|
|||||||
import 'package:encrypt/encrypt.dart';
|
import 'package:encrypt/encrypt.dart';
|
||||||
import 'package:flutter/material.dart' as ui;
|
|
||||||
|
|
||||||
class LoginUtil {
|
class LoginUtil {
|
||||||
static final _keyString = '915eae87951a448c86c47796e44c1fcf';
|
static final _keyString = '915eae87951a448c86c47796e44c1fcf';
|
||||||
@@ -27,9 +26,5 @@ class LoginUtil {
|
|||||||
final decrypted = _encrypter.decrypt(encrypted);
|
final decrypted = _encrypter.decrypt(encrypted);
|
||||||
return decrypted;
|
return decrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ui.Image getAssImg(String imgName){
|
|
||||||
return ui.Image(image: ui.AssetImage('assets/images/$imgName.png'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ class AppTheme {
|
|||||||
|
|
||||||
static const String Font_YuYang = 'YuYang';
|
static const String Font_YuYang = 'YuYang';
|
||||||
|
|
||||||
static const Color themeColor = Color(0xFF017137);
|
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.222:8080/
|
||||||
//http://192.168.110.44: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 = "";
|
static const String release_service_url = "";
|
||||||
|
|
||||||
//加氢站相关查询
|
//加氢站相关查询
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||||
import 'package:get_storage/get_storage.dart';
|
import 'package:get_storage/get_storage.dart';
|
||||||
@@ -9,19 +8,17 @@ import 'package:ln_jq_app/storage_service.dart';
|
|||||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||||
|
|
||||||
import 'common/styles/theme.dart';
|
import 'common/styles/theme.dart';
|
||||||
|
import 'pages/home/view.dart';
|
||||||
import 'pages/login/view.dart';
|
import 'pages/login/view.dart';
|
||||||
import 'pages/welcome/view.dart'; // 引入启动页
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
WidgetsBinding widgetsBinding = await init(
|
WidgetsBinding widgetsBinding = await init(
|
||||||
isDebug: true,
|
isDebug: false,
|
||||||
logTag: '小羚羚',
|
logTag: '小羚羚',
|
||||||
supportedLocales: [const Locale('zh', 'CN')],
|
supportedLocales: [Locale('zh', 'CN')],
|
||||||
);
|
);
|
||||||
|
|
||||||
// 保持原生闪屏页,直到 WelcomeController 调用 remove()
|
|
||||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||||
|
|
||||||
await GetStorage.init();
|
await GetStorage.init();
|
||||||
@@ -31,24 +28,36 @@ void main() async {
|
|||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
GetxApp(
|
GetxApp(
|
||||||
|
// 设计稿尺寸 单位:dp
|
||||||
designSize: const Size(390, 844),
|
designSize: const Size(390, 844),
|
||||||
enableLog: true,
|
// Getx Log
|
||||||
|
enableLog: false,
|
||||||
|
// 默认的跳转动画
|
||||||
defaultTransition: Transition.rightToLeft,
|
defaultTransition: Transition.rightToLeft,
|
||||||
|
// 主题模式
|
||||||
themeMode: GlobalService.to.themeMode,
|
themeMode: GlobalService.to.themeMode,
|
||||||
|
// 主题
|
||||||
theme: AppTheme.light,
|
theme: AppTheme.light,
|
||||||
|
// Dark主题
|
||||||
darkTheme: AppTheme.light,
|
darkTheme: AppTheme.light,
|
||||||
|
// AppTitle
|
||||||
title: '小羚羚',
|
title: '小羚羚',
|
||||||
// 将入口改为启动页
|
// 首页入口
|
||||||
home: const WelcomePage(),
|
home: HomePage(),
|
||||||
fallbackLocale: const Locale('zh', 'CN'),
|
//组件国际化
|
||||||
supportedLocales: const [Locale('zh', 'CN')],
|
fallbackLocale: Locale('zh', 'CN'),
|
||||||
|
supportedLocales: [Locale('zh', 'CN')],
|
||||||
localizationsDelegates: const [
|
localizationsDelegates: const [
|
||||||
|
//pull_to_refresh
|
||||||
RefreshLocalizations.delegate,
|
RefreshLocalizations.delegate,
|
||||||
GlobalMaterialLocalizations.delegate,
|
GlobalMaterialLocalizations.delegate,
|
||||||
GlobalWidgetsLocalizations.delegate,
|
GlobalWidgetsLocalizations.delegate,
|
||||||
GlobalCupertinoLocalizations.delegate,
|
GlobalCupertinoLocalizations.delegate,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// Builder
|
||||||
builder: (context, widget) {
|
builder: (context, widget) {
|
||||||
|
// do something....
|
||||||
return widget!;
|
return widget!;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -58,16 +67,23 @@ void main() async {
|
|||||||
void initHttpSet() {
|
void initHttpSet() {
|
||||||
AppTheme.test_service_url = StorageService.to.hostUrl ?? AppTheme.test_service_url;
|
AppTheme.test_service_url = StorageService.to.hostUrl ?? AppTheme.test_service_url;
|
||||||
|
|
||||||
|
// 设置基础 URL
|
||||||
HttpService.to.setBaseUrl(AppTheme.test_service_url);
|
HttpService.to.setBaseUrl(AppTheme.test_service_url);
|
||||||
|
//指定请求头
|
||||||
HttpService.to.dio.interceptors.add(TokenInterceptor(tokenKey: 'asoco-token'));
|
HttpService.to.dio.interceptors.add(TokenInterceptor(tokenKey: 'asoco-token'));
|
||||||
|
// 设置全局响应处理器
|
||||||
HttpService.to.setOnResponseHandler((response) async {
|
HttpService.to.setOnResponseHandler((response) async {
|
||||||
try {
|
try {
|
||||||
|
if (response.data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final baseModel = BaseModel.fromJson(response.data);
|
final baseModel = BaseModel.fromJson(response.data);
|
||||||
if (baseModel.code == 0 || baseModel.code == 200) {
|
if (baseModel.code == 0 || baseModel.code == 200) {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
} else if (baseModel.code == 401) {
|
} else if (baseModel.code == 401) {
|
||||||
await StorageService.to.clearLoginInfo();
|
await StorageService.to.clearLoginInfo();
|
||||||
Get.offAll(() => const LoginPage());
|
Get.offAll(() => LoginPage());
|
||||||
return baseModel.message;
|
return baseModel.message;
|
||||||
} else {
|
} else {
|
||||||
return (baseModel.error.toString()).isEmpty
|
return (baseModel.error.toString()).isEmpty
|
||||||
|
|||||||
@@ -173,6 +173,10 @@ class HistoryPage extends GetView<HistoryController> {
|
|||||||
text = '未加氢';
|
text = '未加氢';
|
||||||
color = Colors.red;
|
color = Colors.red;
|
||||||
break;
|
break;
|
||||||
|
case ReservationStatus.cancel:
|
||||||
|
text = '已取消';
|
||||||
|
color = Colors.red;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
text = '未知状态';
|
text = '未知状态';
|
||||||
color = Colors.grey;
|
color = Colors.grey;
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
customStartTime = DateTime.now();
|
customStartTime = DateTime.now();
|
||||||
customEndTime = customStartTime!.add(const Duration(days: 1));
|
customEndTime = customStartTime!.add(const Duration(days: 1));
|
||||||
renderData();
|
renderData();
|
||||||
_msgNotice(); // 红点消息
|
|
||||||
startAutoRefresh();
|
startAutoRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +94,6 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
String jobDetailsStr = "";
|
String jobDetailsStr = "";
|
||||||
String jobId = "";
|
String jobId = "";
|
||||||
Timer? _refreshTimer;
|
Timer? _refreshTimer;
|
||||||
bool isNotice = false;
|
|
||||||
|
|
||||||
Future<void> renderData() async {
|
Future<void> renderData() async {
|
||||||
showLoading("加载中");
|
showLoading("加载中");
|
||||||
@@ -170,7 +168,7 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jobDetailsStr =
|
jobDetailsStr =
|
||||||
"当前站点已设置$beginTime至$endTime,共${hoursLeft.toStringAsFixed(2)}小时,为$hydStatusStr状态";
|
"当前站点已设置$beginTime至$endTime,共${hoursLeft.toStringAsFixed(2)}小时,为$hydStatusStr状态";
|
||||||
|
|
||||||
// 如果是处于非营运状态,自动回填开始和结束时间
|
// 如果是处于非营运状态,自动回填开始和结束时间
|
||||||
// 假设 customStartTime 是现在,customEndTime 是接口返回的结束时间
|
// 假设 customStartTime 是现在,customEndTime 是接口返回的结束时间
|
||||||
@@ -212,7 +210,7 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
var customerPriceTemp = result.data["customerPrice"];
|
var customerPriceTemp = result.data["customerPrice"];
|
||||||
customerPrice =
|
customerPrice =
|
||||||
(customerPriceTemp != null && customerPriceTemp.toString().isNotEmpty)
|
(customerPriceTemp != null && customerPriceTemp.toString().isNotEmpty)
|
||||||
? "$customerPriceTemp"
|
? "$customerPriceTemp"
|
||||||
: "暂无价格";
|
: "暂无价格";
|
||||||
|
|
||||||
@@ -248,26 +246,6 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onOperationStatusChanged(String? newValue) {
|
void onOperationStatusChanged(String? newValue) {
|
||||||
if (newValue != null) {
|
if (newValue != null) {
|
||||||
selectedOperationStatus = newValue;
|
selectedOperationStatus = newValue;
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.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/reservation/controller.dart';
|
import 'package:ln_jq_app/pages/b_page/reservation/controller.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
|
||||||
|
|
||||||
class ReservationPage extends GetView<ReservationController> {
|
class ReservationPage extends GetView<ReservationController> {
|
||||||
const ReservationPage({super.key});
|
const ReservationPage({super.key});
|
||||||
|
|
||||||
// 定义主题色
|
|
||||||
static const kPrimaryColor = Color(0xFF006D35); // 效果图深绿色
|
|
||||||
static const kBgColor = Color(0xFFF5F7F9); // 背景灰
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GetBuilder<ReservationController>(
|
return GetBuilder<ReservationController>(
|
||||||
@@ -19,28 +13,21 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
id: 'b_reservation',
|
id: 'b_reservation',
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: kBgColor,
|
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.all(12.0),
|
||||||
_buildTopSection(context),
|
child: Column(
|
||||||
Padding(
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 20.w),
|
children: [
|
||||||
child: Column(
|
_buildHeaderCard(),
|
||||||
children: [
|
const SizedBox(height: 12),
|
||||||
SizedBox(height: 16),
|
_buildInfoFormCard(context),
|
||||||
_buildBasicInfoCard(),
|
const SizedBox(height: 12),
|
||||||
SizedBox(height: 16),
|
_buildTipsCard(),
|
||||||
_buildOperationContentCard(context),
|
const SizedBox(height: 12),
|
||||||
SizedBox(height: 16.h),
|
_buildLogoutButton(),
|
||||||
_buildSystemTips(),
|
],
|
||||||
SizedBox(height: 24),
|
),
|
||||||
_buildLogoutButton(),
|
|
||||||
SizedBox(height: 40),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -48,280 +35,158 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 1. 顶部个人信息及统计栏
|
/// 构建顶部的站点信息头卡片
|
||||||
Widget _buildTopSection(BuildContext context) {
|
Widget _buildHeaderCard() {
|
||||||
return Container(
|
return Card(
|
||||||
width: double.infinity,
|
elevation: 2,
|
||||||
decoration: const BoxDecoration(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.vertical(bottom: Radius.circular(30)),
|
|
||||||
),
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: MediaQuery.of(context).padding.top + 10,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
bottom: 25,
|
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
ListTile(
|
||||||
children: [
|
leading: const Icon(Icons.local_gas_station, color: Colors.blue, size: 40),
|
||||||
CircleAvatar(
|
title: Text(
|
||||||
radius: 25,
|
controller.name,
|
||||||
backgroundColor: Colors.white,
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
|
||||||
child: LoginUtil.getAssImg('ic_user_logo@2x'),
|
),
|
||||||
|
subtitle: Text(controller.address),
|
||||||
|
trailing: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.blue[100],
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
child: Text(
|
||||||
Expanded(
|
controller.selectedOperationStatus,
|
||||||
child: Column(
|
style: const TextStyle(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
color: Colors.blue,
|
||||||
children: [
|
fontWeight: FontWeight.bold,
|
||||||
Row(
|
fontSize: 12,
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
controller.name,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
_buildStatusTag(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
"站点:${controller.address}",
|
|
||||||
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("氢气价格", "Hydrogen price", controller.customerPrice, "/kg"),
|
|
||||||
SizedBox(width: 4.w),
|
|
||||||
_buildStatBox("营业时间", "Opening time", controller.timeStr, ""),
|
|
||||||
SizedBox(width: 4.w),
|
|
||||||
_buildStatBox("设备状态", "Anlagenzustand", "98", "%"),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildStatusTag() {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFE1F5FE),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
controller.selectedOperationStatus,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xFF03A9F4),
|
|
||||||
fontSize: 12.sp,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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: kBgColor,
|
|
||||||
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),
|
const Divider(height: 1, indent: 16, endIndent: 16),
|
||||||
Row(
|
Padding(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
_buildHeaderStat(controller.customerPrice, '氢气价格'),
|
||||||
value,
|
_buildHeaderStat(controller.timeStr, '营业时间'),
|
||||||
style: TextStyle(
|
_buildHeaderStat('98%', '设备状态'),
|
||||||
fontSize: 12.sp,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Color(0xFF333333),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 2),
|
|
||||||
Text(unit, style: const TextStyle(fontSize: 11, color: Colors.grey)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 2. 站点基本信息
|
|
||||||
Widget _buildBasicInfoCard() {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"站点基本信息",
|
|
||||||
style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.bold),
|
|
||||||
),
|
),
|
||||||
SizedBox(height: 15),
|
|
||||||
_buildInfoRow("站点名称", controller.name),
|
|
||||||
_buildInfoRow("运营企业", controller.operatingEnterprise),
|
|
||||||
_buildInfoRow("站点地址", controller.address),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildInfoRow(String label, String value) {
|
/// 构建头部卡片中的单个统计项
|
||||||
return Padding(
|
Widget _buildHeaderStat(String value, String label) {
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
return Column(
|
||||||
child: Row(
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
Text(
|
||||||
children: [
|
value,
|
||||||
Text(
|
style: const TextStyle(
|
||||||
label,
|
color: Colors.blue,
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 11.sp),
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
value,
|
const SizedBox(height: 4),
|
||||||
style: TextStyle(
|
Text(label, style: const TextStyle(color: Colors.grey, fontSize: 12)),
|
||||||
color: Color(0xFF333333),
|
],
|
||||||
fontSize: 12.sp,
|
);
|
||||||
fontWeight: FontWeight.bold,
|
}
|
||||||
|
|
||||||
|
/// 构建包含所有信息表单的卡片(增加 Tab 切换功能)
|
||||||
|
Widget _buildInfoFormCard(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
|
clipBehavior: Clip.antiAlias, // 确保 Tab 背景圆角生效
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// Tab 切换栏
|
||||||
|
Obx(
|
||||||
|
() => Container(
|
||||||
|
color: Colors.grey[50],
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_buildTabItem(0, Icons.business_outlined, '站点信息'),
|
||||||
|
_buildTabItem(1, Icons.campaign_outlined, '站点广播'),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
// 内容区域
|
||||||
|
Obx(
|
||||||
|
() => controller.selectedTabIndex.value == 0
|
||||||
|
? _buildStationInfo(context)
|
||||||
|
: _buildStationBroadcast(context),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 3. 运营信息/站点广播 Tab 及内容
|
/// 构建单个 Tab 项
|
||||||
Widget _buildOperationContentCard(BuildContext context) {
|
Widget _buildTabItem(int index, IconData icon, String label) {
|
||||||
return GestureDetector(
|
bool isSelected = controller.selectedTabIndex.value == index;
|
||||||
onTap: hideKeyboard,
|
return Expanded(
|
||||||
child: Container(
|
child: InkWell(
|
||||||
decoration: BoxDecoration(
|
onTap: () => controller.selectedTabIndex.value = index,
|
||||||
color: Colors.white,
|
child: Container(
|
||||||
borderRadius: BorderRadius.circular(20),
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||||
),
|
decoration: BoxDecoration(
|
||||||
child: Column(
|
border: Border(
|
||||||
children: [
|
bottom: BorderSide(
|
||||||
// 自定义 TabBar
|
color: isSelected ? Colors.blue : Colors.transparent,
|
||||||
Obx(
|
width: 2,
|
||||||
() => Padding(
|
),
|
||||||
padding: const EdgeInsets.only(left: 16, top: 16),
|
),
|
||||||
child: Row(
|
),
|
||||||
children: [
|
child: Row(
|
||||||
_buildTabTitle(0, "运营信息"),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
const SizedBox(width: 30),
|
children: [
|
||||||
_buildTabTitle(1, "站点广播"),
|
Icon(icon, size: 20, color: isSelected ? Colors.blue : Colors.grey[600]),
|
||||||
],
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||||
|
color: isSelected ? Colors.blue : Colors.grey[600],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
Obx(
|
),
|
||||||
() => controller.selectedTabIndex.value == 0
|
|
||||||
? _buildOperatingForm(context)
|
|
||||||
: _buildBroadcastForm(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTabTitle(int index, String title) {
|
/// 站点信息子视图
|
||||||
bool isSelected = controller.selectedTabIndex.value == index;
|
Widget _buildStationInfo(BuildContext context) {
|
||||||
return GestureDetector(
|
|
||||||
onTap: () => controller.selectedTabIndex.value = index,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 17,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: isSelected ? Colors.black87 : Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (isSelected)
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.only(top: 4),
|
|
||||||
width: 25,
|
|
||||||
height: 3,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFF00A870), // 效果图中的亮绿色横线
|
|
||||||
borderRadius: BorderRadius.circular(2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildOperatingForm(BuildContext context) {
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
_buildSectionTitle('基本信息'),
|
||||||
|
_buildDisplayField(label: '站点名称', value: controller.name),
|
||||||
|
_buildDisplayField(label: '运营企业', value: controller.operatingEnterprise),
|
||||||
|
_buildDisplayField(label: '站点地址', value: controller.address),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
_buildSectionTitle('价格信息'),
|
||||||
|
_buildDisplayField(label: '官方价格 (元/kg)', value: controller.customerPrice),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
_buildSectionTitle('运营信息'),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text('运营状态', style: TextStyle(color: Colors.grey[600], fontSize: 14)),
|
||||||
'运营状态',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color.fromRGBO(51, 51, 51, 1),
|
|
||||||
fontSize: 12.sp,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
//加氢站未执行的状态修改任务
|
//加氢站未执行的状态修改任务
|
||||||
if (controller.jobTipStr.isNotEmpty)
|
if (controller.jobTipStr.isNotEmpty)
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
@@ -332,119 +197,164 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
controller.jobTipStr,
|
controller.jobTipStr,
|
||||||
style: TextStyle(color: Colors.yellow[800], fontSize: 14),
|
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]),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 8),
|
||||||
// 状态网格选择
|
DropdownButtonFormField<String>(
|
||||||
Wrap(
|
value: controller.selectedOperationStatus,
|
||||||
spacing: 4,
|
items: controller.operationStatusOptions.map((String value) {
|
||||||
runSpacing: 4,
|
return DropdownMenuItem<String>(value: value, child: Text(value));
|
||||||
children: controller.operationStatusOptions.map((status) {
|
|
||||||
bool isSelected = controller.selectedOperationStatus == status;
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () => controller.onOperationStatusChanged(status),
|
|
||||||
child: Container(
|
|
||||||
width: (Get.width - 80) / 2,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: isSelected ? kPrimaryColor : const Color(0xFFEBEBEB),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Text(
|
|
||||||
status,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 11.sp,
|
|
||||||
color: isSelected ? Colors.white : Color.fromRGBO(51, 51, 51, 1),
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
}).toList(),
|
||||||
|
onChanged: controller.onOperationStatusChanged,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 12.h),
|
const SizedBox(height: 16),
|
||||||
if (controller.selectedOperationStatus == "营运中")
|
if (controller.selectedOperationStatus == "营运中")
|
||||||
_buildDisplayField(label: '营业时间', value: controller.timeStr)
|
_buildDisplayField(label: '营业时间', value: controller.timeStr)
|
||||||
else
|
else
|
||||||
Column(
|
Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
_buildInputLabel("开始时间"),
|
_buildClickField(
|
||||||
_buildDateTimePicker(
|
label: '开始时间',
|
||||||
controller.customStartTimeStr,
|
value: controller.customStartTimeStr,
|
||||||
() => controller.pickDateTime(context, true),
|
onTap: () => controller.pickDateTime(context, true),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15),
|
_buildClickField(
|
||||||
_buildInputLabel("结束时间"),
|
label: '结束时间',
|
||||||
_buildDateTimePicker(
|
value: controller.customEndTimeStr,
|
||||||
controller.customEndTimeStr,
|
onTap: () => controller.pickDateTime(context, false),
|
||||||
() => controller.pickDateTime(context, false),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
_buildDisplayField(label: '联系电话', value: controller.phone),
|
_buildDisplayField(label: '联系电话', value: controller.phone),
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 24),
|
||||||
Row(
|
ElevatedButton(
|
||||||
children: [
|
onPressed: controller.saveInfo,
|
||||||
Expanded(
|
style: ElevatedButton.styleFrom(
|
||||||
flex: 1,
|
minimumSize: const Size(double.infinity, 48),
|
||||||
child: OutlinedButton(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
onPressed: () {
|
),
|
||||||
controller.renderData();
|
child: const Text('保存信息', style: TextStyle(fontSize: 16)),
|
||||||
}, // 重置逻辑
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
side: const BorderSide(color: kPrimaryColor),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Text("重置", style: TextStyle(color: kPrimaryColor)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: controller.saveInfo,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: kPrimaryColor,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Text("保存设置", style: TextStyle(color: Colors.white)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 站点广播子视图
|
||||||
|
Widget _buildStationBroadcast(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.campaign, color: Colors.blue, size: 28),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
const Text(
|
||||||
|
'站点广播通知',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 13),
|
||||||
|
_buildTextFieldLabel('通知标题'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
SizedBox(
|
||||||
|
height: 45.h,
|
||||||
|
child: TextField(
|
||||||
|
controller: controller.broadcastTitleController,
|
||||||
|
maxLength: 30,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: '例如:临时闭站通知',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey[400], fontSize: 14),
|
||||||
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
|
counterText: '', // 隐藏原生计数器,我们可以按需自定义
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildTextFieldLabel('通知内容'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: controller.broadcastContentController,
|
||||||
|
maxLength: 150,
|
||||||
|
maxLines: 5,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: '请输入通知内容...',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey[400], fontSize: 14),
|
||||||
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: controller.sendBroadcast,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
minimumSize: const Size(double.infinity, 50),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'发送',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTextFieldLabel(String label) {
|
||||||
|
return Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 构建带标题的表单区域
|
||||||
|
Widget _buildSectionTitle(String title) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 12.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(width: 4, height: 16, color: Colors.blue),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 构建一个“标签+纯文本”的显示行
|
||||||
Widget _buildDisplayField({required String label, required String value}) {
|
Widget _buildDisplayField({required String label, required String value}) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 12.0),
|
padding: const EdgeInsets.only(bottom: 12.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 14)),
|
||||||
label,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color.fromRGBO(51, 51, 51, 1),
|
|
||||||
fontSize: 12.sp,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
@@ -464,177 +374,118 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBroadcastForm() {
|
/// 构建一个“可点击”的选择行
|
||||||
|
Widget _buildClickField({
|
||||||
|
required String label,
|
||||||
|
required String value,
|
||||||
|
required VoidCallback onTap,
|
||||||
|
}) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.only(bottom: 12.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_buildInputLabel("通知标题"),
|
Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 14)),
|
||||||
TextField(
|
const SizedBox(height: 8),
|
||||||
controller: controller.broadcastTitleController,
|
InkWell(
|
||||||
decoration: _inputDecoration("例如:临时闭站通知"),
|
onTap: onTap,
|
||||||
),
|
child: Container(
|
||||||
const SizedBox(height: 15),
|
width: double.infinity,
|
||||||
_buildInputLabel("通知内容"),
|
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0),
|
||||||
TextField(
|
decoration: BoxDecoration(
|
||||||
controller: controller.broadcastContentController,
|
color: Colors.white,
|
||||||
maxLines: 4,
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
decoration: _inputDecoration("请输入通知内容..."),
|
border: Border.all(color: Colors.blue.withOpacity(0.5)),
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: () {
|
|
||||||
controller.broadcastTitleController.clear();
|
|
||||||
controller.broadcastContentController.clear();
|
|
||||||
}, // 重置逻辑
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
side: const BorderSide(color: kPrimaryColor),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Text("重置", style: TextStyle(color: kPrimaryColor)),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 15),
|
child: Row(
|
||||||
Expanded(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
flex: 2,
|
children: [
|
||||||
child: ElevatedButton(
|
Text(
|
||||||
onPressed: controller.sendBroadcast,
|
value,
|
||||||
style: ElevatedButton.styleFrom(
|
style: const TextStyle(fontSize: 14, color: Colors.black87),
|
||||||
backgroundColor: kPrimaryColor,
|
|
||||||
minimumSize: const Size(double.infinity, 50),
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
|
||||||
),
|
),
|
||||||
child: const Text("发送广播", style: TextStyle(color: Colors.white)),
|
const Icon(Icons.calendar_month, size: 18, color: Colors.blue),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildInputLabel(String label) {
|
/// 构建静态提示信息卡片
|
||||||
return Padding(
|
Widget _buildTipsCard() {
|
||||||
padding: const EdgeInsets.only(left: 0, bottom: 8),
|
return Card(
|
||||||
child: Text(
|
elevation: 2,
|
||||||
label,
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
style: TextStyle(
|
child: Padding(
|
||||||
color: Color.fromRGBO(51, 51, 51, 1),
|
padding: const EdgeInsets.all(16.0),
|
||||||
fontSize: 12.sp,
|
child: Column(
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildDateTimePicker(String value, VoidCallback onTap) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(color: const Color(0xFF00A870).withOpacity(0.5)),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Text(value, style: const TextStyle(color: Colors.black87)),
|
_buildInfoItem(Icons.info_outline, '请确保信息准确无误'),
|
||||||
const Icon(Icons.calendar_today_outlined, size: 18, color: Color(0xFF00A870)),
|
const SizedBox(height: 10),
|
||||||
|
_buildInfoItem(Icons.help_outline, '价格信息将实时更新到用户端'),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildInfoItem(Icons.headset_mic_outlined, '如有疑问请联系技术支持: 400-021-1773'),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.verified_outlined, color: Colors.blue, size: 20),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: FutureBuilder<String>(
|
||||||
|
future: getVersion(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return const Text("");
|
||||||
|
}
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
return TextX.labelSmall(
|
||||||
|
"当前版本: ${snapshot.data}",
|
||||||
|
color: Colors.black54,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return const Text("");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDecoration _inputDecoration(String hint) {
|
/// 构建退出登录按钮
|
||||||
return InputDecoration(
|
|
||||||
hintText: hint,
|
|
||||||
hintStyle: const TextStyle(color: Colors.grey, fontSize: 14),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: const BorderSide(color: Color(0xFFE0E0E0)),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: const BorderSide(color: Color(0xFFE0E0E0)),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 12),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 4. 系统提醒
|
|
||||||
Widget _buildSystemTips() {
|
|
||||||
return 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(
|
|
||||||
"请您确保所提供的信息准确无误,价格信息也将实时\n更新至用户端",
|
|
||||||
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),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 5. 退出登录按钮
|
|
||||||
Widget _buildLogoutButton() {
|
Widget _buildLogoutButton() {
|
||||||
return SizedBox(
|
return ElevatedButton(
|
||||||
width: double.infinity,
|
onPressed: controller.logout,
|
||||||
height: 50,
|
style: ElevatedButton.styleFrom(
|
||||||
child: ElevatedButton(
|
backgroundColor: Colors.red,
|
||||||
onPressed: controller.logout,
|
foregroundColor: Colors.white,
|
||||||
style: ElevatedButton.styleFrom(
|
minimumSize: const Size(double.infinity, 48),
|
||||||
backgroundColor: Color.fromRGBO(204, 52, 46, 1),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),
|
elevation: 2,
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
"退出登录",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
child: const Text(
|
||||||
|
'退出登录',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 构建带图标的提示信息行
|
||||||
|
Widget _buildInfoItem(IconData icon, String text) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Icon(icon, color: Colors.blue, size: 20),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Text(text, style: const TextStyle(fontSize: 14, color: Colors.black54)),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,13 @@ enum ReservationStatus {
|
|||||||
completed, // 完成 ( addStatus: 1)
|
completed, // 完成 ( addStatus: 1)
|
||||||
rejected, // 拒绝 ( -1)
|
rejected, // 拒绝 ( -1)
|
||||||
unadded, // 未加 ( 2)
|
unadded, // 未加 ( 2)
|
||||||
|
cancel, // 取消预约
|
||||||
unknown, // 未知状态
|
unknown, // 未知状态
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReservationModel {
|
class ReservationModel {
|
||||||
final String id;
|
final String id;
|
||||||
|
final String stationId;
|
||||||
final String plateNumber;
|
final String plateNumber;
|
||||||
String amount;
|
String amount;
|
||||||
final String time;
|
final String time;
|
||||||
@@ -40,6 +42,7 @@ class ReservationModel {
|
|||||||
|
|
||||||
ReservationModel({
|
ReservationModel({
|
||||||
required this.id,
|
required this.id,
|
||||||
|
required this.stationId,
|
||||||
required this.plateNumber,
|
required this.plateNumber,
|
||||||
required this.amount,
|
required this.amount,
|
||||||
required this.time,
|
required this.time,
|
||||||
@@ -80,6 +83,9 @@ class ReservationModel {
|
|||||||
case 2:
|
case 2:
|
||||||
currentStatus = ReservationStatus.unadded;
|
currentStatus = ReservationStatus.unadded;
|
||||||
break;
|
break;
|
||||||
|
case 6:
|
||||||
|
currentStatus = ReservationStatus.cancel;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
currentStatus = ReservationStatus.unknown;
|
currentStatus = ReservationStatus.unknown;
|
||||||
}
|
}
|
||||||
@@ -97,6 +103,7 @@ class ReservationModel {
|
|||||||
return ReservationModel(
|
return ReservationModel(
|
||||||
// 原始字段,用于UI兼容
|
// 原始字段,用于UI兼容
|
||||||
id: json['id']?.toString() ?? '',
|
id: json['id']?.toString() ?? '',
|
||||||
|
stationId: json['stationId']?.toString() ?? '',
|
||||||
plateNumber: json['plateNumber']?.toString() ?? '未知车牌',
|
plateNumber: json['plateNumber']?.toString() ?? '未知车牌',
|
||||||
amount: '${json['hydAmount']?.toString() ?? '0'}kg',
|
amount: '${json['hydAmount']?.toString() ?? '0'}kg',
|
||||||
time: timeRange,
|
time: timeRange,
|
||||||
@@ -136,24 +143,32 @@ class SiteController extends GetxController with BaseControllerMixin {
|
|||||||
Timer? _refreshTimer;
|
Timer? _refreshTimer;
|
||||||
|
|
||||||
final TextEditingController searchController = TextEditingController();
|
final TextEditingController searchController = TextEditingController();
|
||||||
bool isNotice = false;
|
|
||||||
|
@override
|
||||||
|
bool get listenLifecycleEvent => true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
renderData();
|
renderData();
|
||||||
_msgNotice();
|
msgNotice();
|
||||||
startAutoRefresh();
|
startAutoRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onPaused() {
|
||||||
|
stopAutoRefresh();
|
||||||
|
super.onPaused();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
stopAutoRefresh();
|
stopAutoRefresh();
|
||||||
searchController.dispose();
|
searchController.dispose();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
bool isNotice = false;
|
||||||
Future<void> _msgNotice() async {
|
Future<void> msgNotice() async {
|
||||||
final Map<String, dynamic> requestData = {
|
final Map<String, dynamic> requestData = {
|
||||||
'appFlag': 1,
|
'appFlag': 1,
|
||||||
'isRead': 1,
|
'isRead': 1,
|
||||||
@@ -169,6 +184,7 @@ class SiteController extends GetxController with BaseControllerMixin {
|
|||||||
if (result.code == 0 && result.data != null) {
|
if (result.code == 0 && result.data != null) {
|
||||||
String total = result.data["total"].toString();
|
String total = result.data["total"].toString();
|
||||||
isNotice = int.parse(total) > 0;
|
isNotice = int.parse(total) > 0;
|
||||||
|
updateUi();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +199,7 @@ class SiteController extends GetxController with BaseControllerMixin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 【6. 新增】停止定时器的方法
|
///停止定时器的方法
|
||||||
void stopAutoRefresh() {
|
void stopAutoRefresh() {
|
||||||
// 如果定时器存在并且是激活状态,就取消它
|
// 如果定时器存在并且是激活状态,就取消它
|
||||||
_refreshTimer?.cancel();
|
_refreshTimer?.cancel();
|
||||||
@@ -205,6 +221,7 @@ class SiteController extends GetxController with BaseControllerMixin {
|
|||||||
'pageNum': 1,
|
'pageNum': 1,
|
||||||
'pageSize': 50, // 暂时不考虑分页,一次获取30条
|
'pageSize': 50, // 暂时不考虑分页,一次获取30条
|
||||||
'keyword': searchText, // 加氢站名称、手机号
|
'keyword': searchText, // 加氢站名称、手机号
|
||||||
|
'stationId': StorageService.to.userId
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:getx_scaffold/common/index.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
import 'package:ln_jq_app/common/login_util.dart';
|
|
||||||
import 'package:ln_jq_app/pages/c_page/car_info/view.dart';
|
import 'package:ln_jq_app/pages/c_page/car_info/view.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/map/view.dart';
|
import 'package:ln_jq_app/pages/c_page/map/view.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/mine/view.dart';
|
import 'package:ln_jq_app/pages/c_page/mine/view.dart';
|
||||||
@@ -10,10 +9,9 @@ import 'package:ln_jq_app/pages/c_page/reservation/view.dart';
|
|||||||
import 'index.dart';
|
import 'index.dart';
|
||||||
|
|
||||||
class BaseWidgetsPage extends GetView<BaseWidgetsController> {
|
class BaseWidgetsPage extends GetView<BaseWidgetsController> {
|
||||||
BaseWidgetsPage({super.key});
|
BaseWidgetsPage({super.key});
|
||||||
|
|
||||||
final PageController _pageController = PageController();
|
final PageController _pageController = PageController();
|
||||||
|
|
||||||
// 主视图
|
// 主视图
|
||||||
Widget _buildView() {
|
Widget _buildView() {
|
||||||
return PageView(
|
return PageView(
|
||||||
@@ -22,68 +20,54 @@ class BaseWidgetsPage extends GetView<BaseWidgetsController> {
|
|||||||
onPageChanged: (index) {
|
onPageChanged: (index) {
|
||||||
jumpTabAndPage(index);
|
jumpTabAndPage(index);
|
||||||
},
|
},
|
||||||
children: _buildPages(),
|
children: _buildPages(), // 页面的列表
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void jumpTabAndPage(int index) {
|
void jumpTabAndPage(int index) {
|
||||||
controller.pageIndex = index;
|
controller.pageIndex = index; // 更新页面索引
|
||||||
controller.updateUi();
|
controller.updateUi(); // 更新 UI
|
||||||
_pageController.jumpToPage(controller.pageIndex);
|
_pageController.jumpToPage(controller.pageIndex);
|
||||||
}
|
}
|
||||||
|
// 对应的页面
|
||||||
List<Widget> _buildPages() {
|
List<Widget> _buildPages() {
|
||||||
return [ReservationPage(), MapPage(), CarInfoPage(), MinePage()];
|
return [
|
||||||
|
ReservationPage(),
|
||||||
|
MapPage(),
|
||||||
|
CarInfoPage(),
|
||||||
|
MinePage(),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自定义导航栏 (悬浮胶囊样式)
|
//导航栏
|
||||||
Widget _buildNavigationBar() {
|
Widget _buildNavigationBar() {
|
||||||
return SafeArea(
|
return NavigationX(
|
||||||
child: Container(
|
currentIndex: controller.pageIndex, // 当前选中的tab索引
|
||||||
height: 50.h,
|
onTap: (index) {
|
||||||
margin: const EdgeInsets.fromLTRB(24, 0, 24, 10), // 悬浮边距
|
jumpTabAndPage(index);
|
||||||
decoration: BoxDecoration(
|
}, // 切换tab事件
|
||||||
color: Color.fromRGBO(240, 244, 247, 1), // 浅灰色背景
|
items: [
|
||||||
borderRadius: BorderRadius.circular(30),
|
NavigationItemModel(
|
||||||
boxShadow: [
|
label: '加氢预约',
|
||||||
BoxShadow(
|
icon: AntdIcon.orderedlist,
|
||||||
color: Colors.black.withOpacity(0.05),
|
selectedIcon: AntdIcon.calendar_fill,
|
||||||
blurRadius: 10,
|
|
||||||
offset: const Offset(0, 5),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
child: Row(
|
NavigationItemModel(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
label: '地图',
|
||||||
children: [
|
icon: AntdIcon.location,
|
||||||
_buildNavItem(0, "ic_h2_select@2x", "ic_h2@2x"),
|
selectedIcon: AntdIcon.location_fill,
|
||||||
_buildNavItem(1, "ic_map_select@2x", "ic_map@2x"),
|
|
||||||
_buildNavItem(2, "ic_car_select@2x", "ic_car@2x"),
|
|
||||||
_buildNavItem(3, "ic_user_select@2x", "ic_user@2x"),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
NavigationItemModel(
|
||||||
);
|
label: '车辆信息',
|
||||||
}
|
icon: AntdIcon.car,
|
||||||
|
selectedIcon: AntdIcon.car_fill,
|
||||||
// 构建单个导航项
|
|
||||||
Widget _buildNavItem(int index, String icon, String selectedIcon) {
|
|
||||||
bool isSelected = controller.pageIndex == index;
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () => jumpTabAndPage(index),
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: isSelected ? const Color(0xFF006633) : Colors.transparent, // 选中时的深绿色背景
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
),
|
||||||
child: SizedBox(
|
NavigationItemModel(
|
||||||
height: 24,
|
label: '我的',
|
||||||
width: 24,
|
icon: AntdIcon.user,
|
||||||
child: LoginUtil.getAssImg(isSelected ? selectedIcon : icon),),
|
selectedIcon: AntdIcon.user,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,10 +78,10 @@ class BaseWidgetsPage extends GetView<BaseWidgetsController> {
|
|||||||
id: 'baseWidgets',
|
id: 'baseWidgets',
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
extendBody: true, // 重要:让 body 延伸到导航栏后面
|
extendBody: false,
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
bottomNavigationBar: _buildNavigationBar(),
|
bottomNavigationBar: _buildNavigationBar(),
|
||||||
body: _buildView(), // 移除 SafeArea 以获得更好的全屏沉浸感
|
body: SafeArea(child: _buildView()),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ class AttachmentViewerPage extends GetView<AttachmentViewerController> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Get.put(AttachmentViewerController());
|
Get.put(AttachmentViewerController());
|
||||||
// final fileName = controller.url.split('/').last;
|
final fileName = controller.url.split('/').last;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
"证件详情",
|
fileName,
|
||||||
style: const TextStyle(fontSize: 16),
|
style: const TextStyle(fontSize: 16),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'package:path_provider/path_provider.dart';
|
|||||||
|
|
||||||
import 'attachment_viewer_page.dart';
|
import 'attachment_viewer_page.dart';
|
||||||
|
|
||||||
class CertificateViewerController extends GetxController with BaseControllerMixin {
|
class CertificateViewerController extends GetxController with BaseControllerMixin{
|
||||||
late final String title;
|
late final String title;
|
||||||
late final List<String> attachments;
|
late final List<String> attachments;
|
||||||
|
|
||||||
@@ -78,11 +78,18 @@ class CertificateViewerController extends GetxController with BaseControllerMixi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Get.to(() => const AttachmentViewerPage(), arguments: {'url': url});
|
Get.to(
|
||||||
|
() => const AttachmentViewerPage(),
|
||||||
|
arguments: {
|
||||||
|
'url': url,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 检查 URL 是否为 PDF (此方法保持不变)
|
/// 检查 URL 是否为 PDF (此方法保持不变)
|
||||||
bool isPdf(String url) {
|
bool isPdf(String url) {
|
||||||
return url.toLowerCase().endsWith('.pdf');
|
return url.toLowerCase().endsWith('.pdf');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import 'package:get/get.dart';
|
|||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
import 'package:ln_jq_app/common/model/base_model.dart';
|
import 'package:ln_jq_app/common/model/base_model.dart';
|
||||||
import 'package:ln_jq_app/common/model/vehicle_info.dart';
|
import 'package:ln_jq_app/common/model/vehicle_info.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/car_info/attachment_viewer_page.dart';
|
|
||||||
import 'package:ln_jq_app/pages/qr_code/view.dart';
|
import 'package:ln_jq_app/pages/qr_code/view.dart';
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'certificate_viewer_page.dart';
|
import 'certificate_viewer_page.dart';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
class CarInfoController extends GetxController with BaseControllerMixin {
|
class CarInfoController extends GetxController with BaseControllerMixin {
|
||||||
@override
|
@override
|
||||||
@@ -24,37 +22,11 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
final RxList<String> operationAttachments = <String>[].obs;
|
final RxList<String> operationAttachments = <String>[].obs;
|
||||||
final RxList<String> hydrogenationAttachments = <String>[].obs;
|
final RxList<String> hydrogenationAttachments = <String>[].obs;
|
||||||
final RxList<String> registerAttachments = <String>[].obs;
|
final RxList<String> registerAttachments = <String>[].obs;
|
||||||
String color = "";
|
|
||||||
String hydrogenCapacity = "";
|
|
||||||
String rentFromCompany = "";
|
|
||||||
String address = "";
|
|
||||||
bool isNotice = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
getUserBindCarInfo();
|
getUserBindCarInfo();
|
||||||
_msgNotice();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -63,6 +35,7 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
// 如果未绑定车辆,且本次会话尚未提示过,则弹出提示
|
// 如果未绑定车辆,且本次会话尚未提示过,则弹出提示
|
||||||
if (!StorageService.to.hasShownBindVehicleDialog &&
|
if (!StorageService.to.hasShownBindVehicleDialog &&
|
||||||
StorageService.to.isLoggedIn &&
|
StorageService.to.isLoggedIn &&
|
||||||
|
StorageService.to.loginChannel == LoginChannel.driver &&
|
||||||
!StorageService.to.hasVehicleInfo) {
|
!StorageService.to.hasVehicleInfo) {
|
||||||
Future.delayed(const Duration(milliseconds: 500), () {
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
DialogX.to.showConfirmDialog(
|
DialogX.to.showConfirmDialog(
|
||||||
@@ -123,21 +96,6 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
parseAttachments(data['hydrogenationAttachment']),
|
parseAttachments(data['hydrogenationAttachment']),
|
||||||
);
|
);
|
||||||
registerAttachments.assignAll(parseAttachments(data['registerAttachment']));
|
registerAttachments.assignAll(parseAttachments(data['registerAttachment']));
|
||||||
|
|
||||||
// 初始化时开始加载所有PDF
|
|
||||||
attachments = [
|
|
||||||
...drivingAttachments,
|
|
||||||
...operationAttachments,
|
|
||||||
...hydrogenationAttachments,
|
|
||||||
...registerAttachments,
|
|
||||||
];
|
|
||||||
|
|
||||||
color = data['color'].toString();
|
|
||||||
hydrogenCapacity = data['hydrogenCapacity'].toString();
|
|
||||||
rentFromCompany = data['rentFromCompany'].toString();
|
|
||||||
address = data['address'].toString();
|
|
||||||
|
|
||||||
loadAllPdfs();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateUi();
|
updateUi();
|
||||||
@@ -159,69 +117,4 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
arguments: {'title': title, 'attachments': attachments},
|
arguments: {'title': title, 'attachments': attachments},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 导航到通用的附件查看器页面
|
|
||||||
void openAttachment(String url) {
|
|
||||||
if (url.isEmpty) {
|
|
||||||
showErrorToast('附件链接无效');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Get.to(() => const AttachmentViewerPage(), arguments: {'url': url});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 检查 URL 是否为 PDF
|
|
||||||
bool isPdf(String url) {
|
|
||||||
return url.toLowerCase().endsWith('.pdf');
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> attachments = [];
|
|
||||||
|
|
||||||
// --- 新增: 状态管理 ---
|
|
||||||
/// 用于存储网络PDF的本地路径,key是网络url,value是本地路径
|
|
||||||
final RxMap<String, String> localPdfPaths = <String, String>{}.obs;
|
|
||||||
|
|
||||||
/// 用于跟踪每个附件的加载状态,key是网络url
|
|
||||||
final RxMap<String, bool> isLoading = <String, bool>{}.obs;
|
|
||||||
|
|
||||||
/// 遍历所有附件,如果是PDF则进行下载
|
|
||||||
void loadAllPdfs() {
|
|
||||||
for (var url in attachments) {
|
|
||||||
if (isPdf(url)) {
|
|
||||||
_downloadPdf(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 下载单个PDF文件
|
|
||||||
Future<void> _downloadPdf(String url) async {
|
|
||||||
if (url.isEmpty) return;
|
|
||||||
|
|
||||||
// 开始加载
|
|
||||||
isLoading[url] = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
final dio = Dio();
|
|
||||||
final Directory tempDir = await getTemporaryDirectory();
|
|
||||||
final String savePath = '${tempDir.path}/${url.split('/').last}';
|
|
||||||
|
|
||||||
// 检查文件是否已存在,避免重复下载
|
|
||||||
if (await File(savePath).exists()) {
|
|
||||||
localPdfPaths[url] = savePath;
|
|
||||||
isLoading[url] = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await dio.download(url, savePath);
|
|
||||||
|
|
||||||
// 下载成功后,更新本地路径
|
|
||||||
localPdfPaths[url] = savePath;
|
|
||||||
} catch (e) {
|
|
||||||
print('PDF download error for $url: $e');
|
|
||||||
// 出错时也可以更新状态,以便UI显示错误提示
|
|
||||||
} finally {
|
|
||||||
// 结束加载
|
|
||||||
isLoading[url] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_pdfview/flutter_pdfview.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:getx_scaffold/getx_scaffold.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/c_page/message/view.dart';
|
import 'package:ln_jq_app/pages/qr_code/view.dart';
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
import 'package:photo_view/photo_view.dart';
|
|
||||||
|
|
||||||
import '../../../common/styles/theme.dart';
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
|
||||||
class CarInfoPage extends GetView<CarInfoController> {
|
class CarInfoPage extends GetView<CarInfoController> {
|
||||||
@@ -20,26 +16,22 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
id: 'car_info',
|
id: 'car_info',
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: const Color.fromRGBO(240, 244, 247, 0.4),
|
backgroundColor: Colors.grey[100],
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.all(12.0),
|
||||||
_buildUserInfoCard(),
|
child: Column(
|
||||||
Padding(
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
padding: EdgeInsets.only(left: 20.w, right: 20.w),
|
children: [
|
||||||
child: Column(
|
_buildDriverInfoCard(),
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
const SizedBox(height: 5),
|
||||||
children: [
|
_buildCarBindingCard(),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 5),
|
||||||
_buildCarInfoCard(),
|
_buildCertificatesCard(),
|
||||||
_buildCertificatesCard(context),
|
const SizedBox(height: 5),
|
||||||
const SizedBox(height: 12),
|
_buildTipsCard(),
|
||||||
_buildSafetyReminderCard(),
|
],
|
||||||
SizedBox(height: 95.h),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -47,229 +39,55 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildUserInfoCard() {
|
/// 构建顶部的司机信息卡片
|
||||||
|
Widget _buildDriverInfoCard() {
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 1,
|
elevation: 2,
|
||||||
color: Colors.white,
|
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
bottomLeft: Radius.circular(20),
|
|
||||||
bottomRight: Radius.circular(20),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 16, top: 50),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Stack(
|
const CircleAvatar(
|
||||||
children: [
|
radius: 20,
|
||||||
CircleAvatar(
|
backgroundColor: Colors.blue,
|
||||||
radius: 25,
|
child: Icon(Icons.person, color: Colors.white, size: 34),
|
||||||
backgroundColor: Colors.white,
|
|
||||||
child: LoginUtil.getAssImg('ic_user_logo@2x'),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: SizedBox(
|
|
||||||
height: 16.h,
|
|
||||||
width: 16.w,
|
|
||||||
child: LoginUtil.getAssImg('ic_logo@2x'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
SizedBox(width: 8.w),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Text(
|
||||||
children: [
|
"${StorageService.to.name}",
|
||||||
Text(
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
"${StorageService.to.name}",
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8.w),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 8,
|
|
||||||
vertical: 2,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color.fromRGBO(236, 255, 234, 1),
|
|
||||||
border: Border.all(color: const Color(0xFFB7E19F)),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: const Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.eco, size: 12, color: Color(0xFF52C41A)),
|
|
||||||
SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
"绿色先锋",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xFF52C41A),
|
|
||||||
fontSize: 10,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
"羚牛ID:${StorageService.to.phone}",
|
"${StorageService.to.phone}",
|
||||||
style: const TextStyle(color: Colors.grey, fontSize: 11),
|
style: TextStyle(color: Colors.grey, fontSize: 11),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
Container(
|
||||||
onPressed: () {
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 20),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
_buildModernStatItem('本月里程数', 'Accumulated', '2,852km', ''),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
_buildModernStatItem('总里程', 'Refuel Count', "2.5W km", ''),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
_buildModernStatItem('服务评分', 'Driver rating', "4.9分", ''),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildModernStatItem(String title, String subtitle, String value, String unit) {
|
|
||||||
return Expanded(
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(12.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFF8F9FA),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(subtitle, style: const TextStyle(fontSize: 9, color: Colors.grey)),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
|
||||||
textBaseline: TextBaseline.alphabetic,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
value,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(unit, style: const TextStyle(fontSize: 10, color: Colors.black54)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildCarInfoCard() {
|
|
||||||
return Card(
|
|
||||||
elevation: 2,
|
|
||||||
color: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(20.0),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
_buildDetailRow('车牌号', controller.plateNumber, isPlate: true),
|
|
||||||
const SizedBox(height: 11),
|
|
||||||
_buildDetailRow('车架号', controller.vin),
|
|
||||||
const SizedBox(height: 11),
|
|
||||||
_buildDetailRow('车辆型号', controller.modelName),
|
|
||||||
const SizedBox(height: 11),
|
|
||||||
_buildDetailRow('车辆品牌', controller.brandName),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
_buildH2LevelProgress(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildDetailRow(String label, String value, {bool isPlate = false}) {
|
|
||||||
return Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(label, style: const TextStyle(fontSize: 13, color: Colors.grey)),
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
if (isPlate)
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => controller.doQrCode(),
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.only(right: 10),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(color: const Color.fromRGBO(71, 174, 208, 1)),
|
color: Colors.blue[50],
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
color: const Color.fromRGBO(235, 250, 255, 1),
|
border: Border.all(color: Colors.blue, width: 0.5),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: const Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
Icon(Icons.shield_outlined, color: Colors.blue, size: 14),
|
||||||
Icons.sync,
|
SizedBox(width: 4),
|
||||||
size: 12,
|
|
||||||
color: Color.fromRGBO(71, 174, 208, 1),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
Text(
|
||||||
StorageService.to.hasVehicleInfo ? "换车牌" : "扫码绑定",
|
'已认证',
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Color.fromRGBO(71, 174, 208, 1),
|
color: Colors.blue,
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
@@ -277,81 +95,18 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
Text(
|
|
||||||
value,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildH2LevelProgress() {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
child: const LinearProgressIndicator(
|
|
||||||
value: 0.75,
|
|
||||||
minHeight: 8,
|
|
||||||
backgroundColor: Color(0xFFF0F2F5),
|
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(Color.fromRGBO(16, 185, 129, 1)),
|
|
||||||
),
|
),
|
||||||
),
|
const Divider(height: 1, indent: 16, endIndent: 16),
|
||||||
const SizedBox(height: 8),
|
Padding(
|
||||||
const Row(
|
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
child: Row(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
Text("H2 Level", style: TextStyle(fontSize: 11, color: Colors.grey)),
|
|
||||||
Text(
|
|
||||||
"75%",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 11,
|
|
||||||
color: Color.fromRGBO(16, 185, 129, 1),
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 3. 构建车辆证件卡片 (重构为 TabView 样式)
|
|
||||||
Widget _buildCertificatesCard(BuildContext context) {
|
|
||||||
return DefaultTabController(
|
|
||||||
length: 4,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
TabBar(
|
|
||||||
isScrollable: false,
|
|
||||||
indicatorColor: Color.fromRGBO(16, 185, 129, 1),
|
|
||||||
labelColor: Color.fromRGBO(16, 185, 129, 1),
|
|
||||||
unselectedLabelColor: Colors.grey,
|
|
||||||
labelStyle: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
|
||||||
indicatorSize: TabBarIndicatorSize.label,
|
|
||||||
tabs: const [
|
|
||||||
Tab(text: '行驶证'),
|
|
||||||
Tab(text: '营运证'),
|
|
||||||
Tab(text: '加氢证'),
|
|
||||||
Tab(text: '登记证'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 9),
|
|
||||||
SizedBox(
|
|
||||||
height: 336.h, // 给定一个高度,或者使用别的方式布局
|
|
||||||
child: TabBarView(
|
|
||||||
children: [
|
children: [
|
||||||
_buildCertificateContent('行驶证', controller.drivingAttachments),
|
_buildStatItem('156', '服务天数'),
|
||||||
_buildCertificateContent('营运证', controller.operationAttachments),
|
_buildStatItem('4.9', '评分'),
|
||||||
_buildCertificateContent('加氢资格证', controller.hydrogenationAttachments),
|
_buildStatItem('98%', '准时率'),
|
||||||
_buildCertificateContent('登记证', controller.registerAttachments),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -360,192 +115,207 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 构建单个证件的展示内容
|
// 司机信息卡片中的小统计项
|
||||||
Widget _buildCertificateContent(String title, RxList<String> attachments) {
|
Widget _buildStatItem(String value, String label) {
|
||||||
return Obx(() {
|
return Column(
|
||||||
return Card(
|
children: [
|
||||||
elevation: 0,
|
Text(
|
||||||
color: Colors.white,
|
value,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
style: const TextStyle(
|
||||||
child: Padding(
|
fontSize: 18,
|
||||||
padding: EdgeInsets.all(16.0),
|
fontWeight: FontWeight.bold,
|
||||||
child: attachments.isEmpty
|
color: Colors.blue,
|
||||||
? const Center(child: Text('暂无相关证件信息'))
|
),
|
||||||
: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
//证件文字
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
_buildCertDetailItem('所属公司', controller.rentFromCompany, isFull: true),
|
|
||||||
_buildCertDetailItem('运营城市', controller.address),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
_buildCertDetailItem(
|
|
||||||
'车辆颜色',
|
|
||||||
controller.color,
|
|
||||||
valueColor: const Color(0xFF52C41A),
|
|
||||||
),
|
|
||||||
_buildCertDetailItem('氢瓶容量', controller.hydrogenCapacity),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
// 附件预览部分
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
controller.navigateToCertificateViewer(title, attachments);
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
height: 184.h,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
border: Border.all(color: Color.fromRGBO(226, 232, 240, 1)),
|
|
||||||
color: Color.fromRGBO(248, 250, 252, 1),
|
|
||||||
),
|
|
||||||
child: Center(child: _buildAttachmentPreview(attachments[0])),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
const SizedBox(height: 4),
|
||||||
});
|
Text(label, style: const TextStyle(color: Colors.grey, fontSize: 12)),
|
||||||
}
|
],
|
||||||
|
|
||||||
Widget _buildCertDetailItem(
|
|
||||||
String label,
|
|
||||||
String value, {
|
|
||||||
Color? valueColor,
|
|
||||||
bool isFull = false,
|
|
||||||
}) {
|
|
||||||
return Container(
|
|
||||||
width: isFull ? null : 140,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(label, style: const TextStyle(fontSize: 12, color: Colors.grey)),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
value,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: valueColor ?? Colors.black87,
|
|
||||||
),
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 附件预览组件 (智能判断 PDF 或图片)
|
/// 构建车辆绑定信息卡片
|
||||||
Widget _buildAttachmentPreview(String url) {
|
Widget _buildCarBindingCard() {
|
||||||
return AspectRatio(
|
return Card(
|
||||||
aspectRatio: 4 / 3,
|
elevation: 2,
|
||||||
child: controller.isPdf(url)
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
? Obx(() {
|
child: Padding(
|
||||||
final bool loading = controller.isLoading[url] ?? true;
|
padding: const EdgeInsets.all(16.0),
|
||||||
final String? localPath = controller.localPdfPaths[url];
|
child: Row(
|
||||||
|
children: [
|
||||||
if (loading) {
|
Expanded(
|
||||||
return _buildLoadingIndicator();
|
child: Column(
|
||||||
} else if (localPath != null && localPath.isNotEmpty) {
|
children: [
|
||||||
return IgnorePointer(
|
_buildInfoRow('车牌号: ${controller.plateNumber}', '扫码绑定'),
|
||||||
ignoring: true, // 设置为 true 来忽略所有指针事件
|
const SizedBox(height: 12),
|
||||||
child: PDFView(
|
_buildInfoRow('车架号:', '${controller.vin}'),
|
||||||
backgroundColor: Color.fromRGBO(248, 250, 252, 1),
|
const SizedBox(height: 12),
|
||||||
fitEachPage: true,
|
_buildInfoRow('车辆型号:', '${controller.modelName}'),
|
||||||
filePath: localPath,
|
const SizedBox(height: 12),
|
||||||
fitPolicy: FitPolicy.WIDTH,
|
_buildInfoRow('车辆品牌:', '${controller.brandName}'),
|
||||||
// 适配宽度
|
],
|
||||||
enableSwipe: false,
|
|
||||||
swipeHorizontal: false,
|
|
||||||
autoSpacing: false,
|
|
||||||
pageFling: false,
|
|
||||||
preventLinkNavigation: true, // 顺便禁用PDF内部链接的跳转
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// PDF加载失败
|
|
||||||
return _buildErrorIndicator();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
: Image.network(
|
|
||||||
url,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
// 图片加载时显示loading
|
|
||||||
loadingBuilder: (context, child, loadingProgress) {
|
|
||||||
if (loadingProgress == null) return child;
|
|
||||||
return _buildLoadingIndicator();
|
|
||||||
},
|
|
||||||
// 图片加载失败时显示错误
|
|
||||||
errorBuilder: (context, error, stackTrace) {
|
|
||||||
return _buildErrorIndicator();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildLoadingIndicator() {
|
|
||||||
return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildErrorIndicator() {
|
|
||||||
return const SizedBox(
|
|
||||||
height: 200,
|
|
||||||
child: Center(child: Icon(Icons.error_outline, color: Colors.red, size: 48)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 安全提醒卡片
|
|
||||||
Widget _buildSafetyReminderCard() {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color.fromRGBO(242, 249, 248, 1),
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.info_outline, color: AppTheme.themeColor, size: 24),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
"安全提醒",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color.fromRGBO(1, 113, 55, 1),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
const Text(
|
|
||||||
"请确保车辆证件齐全有效,定期检查车辆状态和证件有效期,以确保运输作业合规安全。",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
color: Color.fromRGBO(1, 113, 55, 0.8),
|
|
||||||
height: 1.5,
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: 8),
|
||||||
const SizedBox(height: 8),
|
Icon(Icons.propane_rounded, size: 50, color: Colors.blue.withOpacity(0.5)),
|
||||||
const Text(
|
],
|
||||||
"如有疑问请联系客服:400-021-1773",
|
),
|
||||||
style: TextStyle(fontSize: 13, color: Color.fromRGBO(1, 113, 55, 0.8)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 车辆绑定卡片中的信息行
|
||||||
|
Widget _buildInfoRow(String label, String value) {
|
||||||
|
bool isButton = value == '扫码绑定';
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Text(label, style: const TextStyle(fontSize: 13)),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
isButton
|
||||||
|
? GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
controller.doQrCode();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsetsGeometry.only(left: 10.w),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.blue.shade300, width: 1),
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
color: Colors.blue.withOpacity(0.05),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min, // Keep the row compact
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
StorageService.to.hasVehicleInfo ? Icons.repeat : Icons.search,
|
||||||
|
size: 13,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 3),
|
||||||
|
Text(
|
||||||
|
StorageService.to.hasVehicleInfo ? "换车牌" : value,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.blue,
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 3. 构建车辆证件卡片
|
||||||
|
Widget _buildCertificatesCard() {
|
||||||
|
return Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildCertificateRow(
|
||||||
|
icon: Icons.credit_card_rounded,
|
||||||
|
title: '行驶证',
|
||||||
|
attachments: controller.drivingAttachments,
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
_buildCertificateRow(
|
||||||
|
icon: Icons.article_rounded,
|
||||||
|
title: '营运证',
|
||||||
|
attachments: controller.operationAttachments,
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
_buildCertificateRow(
|
||||||
|
icon: Icons.propane_tank_rounded,
|
||||||
|
title: '加氢证',
|
||||||
|
attachments: controller.hydrogenationAttachments,
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
_buildCertificateRow(
|
||||||
|
icon: Icons.app_registration_rounded,
|
||||||
|
title: '登记证',
|
||||||
|
attachments: controller.registerAttachments,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 证件展示
|
||||||
|
Widget _buildCertificateRow({
|
||||||
|
required IconData icon,
|
||||||
|
required String title,
|
||||||
|
required List<String> attachments,
|
||||||
|
}) {
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
leading: CircleAvatar(
|
||||||
|
radius: 24,
|
||||||
|
backgroundColor: Colors.blue.withOpacity(0.1),
|
||||||
|
child: Icon(icon, color: Colors.blue, size: 28),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||||
|
),
|
||||||
|
// 使用 Obx 响应式地显示附件数量
|
||||||
|
subtitle: Obx(
|
||||||
|
() => Text(
|
||||||
|
'共 ${attachments.length} 个附件',
|
||||||
|
style: const TextStyle(color: Colors.grey, fontSize: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[200],
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: const Icon(Icons.find_in_page_outlined, color: AppTheme.themeColor),
|
||||||
|
),
|
||||||
|
// 更新 onTap 逻辑
|
||||||
|
onTap: () => controller.navigateToCertificateViewer(title, attachments),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTipsCard() {
|
||||||
|
return Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildTipItem(Icons.info_outline, '请确保车辆证件齐全有效'),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildTipItem(Icons.rule, '定期检查车辆状态和证件有效期'),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildTipItem(Icons.headset_mic_outlined, '如有疑问请联系客服: 400-021-1773'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提示信息卡片中的列表项
|
||||||
|
Widget _buildTipItem(IconData icon, String text) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Icon(icon, color: Colors.blue, size: 20),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Text(text, style: const TextStyle(fontSize: 12, color: Colors.black54)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ class MessageController extends GetxController {
|
|||||||
_loadData(isRefresh: true);
|
_loadData(isRefresh: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onRefresh() => _loadData(isRefresh: true);
|
||||||
|
|
||||||
|
void onLoading() => _loadData(isRefresh: false);
|
||||||
|
|
||||||
Future<void> _loadData({bool isRefresh = false}) async {
|
Future<void> _loadData({bool isRefresh = false}) async {
|
||||||
final int targetPage = isRefresh ? 1 : _pageNum + 1;
|
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 {
|
void markAllRead() async {
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ class MineController extends GetxController with BaseControllerMixin {
|
|||||||
String historyBreakRules = "";
|
String historyBreakRules = "";
|
||||||
String vin = "";
|
String vin = "";
|
||||||
String plateNumber = "";
|
String plateNumber = "";
|
||||||
String violationTotal = "0";//违章总数
|
String violationTotal = "0";
|
||||||
String violationScore = "0";//扣分总数
|
String violationScore = "0";
|
||||||
String violationDispose = "0";//已处理
|
String violationDispose = "0";
|
||||||
bool isNotice = false;
|
bool isNotice = false;
|
||||||
|
|
||||||
void renderData() async {
|
void renderData() async {
|
||||||
@@ -57,7 +57,7 @@ class MineController extends GetxController with BaseControllerMixin {
|
|||||||
_fetchAccidentCount(), // 请求2:事故数
|
_fetchAccidentCount(), // 请求2:事故数
|
||||||
_fetchBreakRulesCount(), // 请求3:违章数
|
_fetchBreakRulesCount(), // 请求3:违章数
|
||||||
_rating(), // 司机评分
|
_rating(), // 司机评分
|
||||||
_msgNotice(), // 红点消息
|
msgNotice(), // 红点消息
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await renderViolation();
|
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 = {
|
final Map<String, dynamic> requestData = {
|
||||||
'appFlag': 1,
|
'appFlag': 1,
|
||||||
'isRead': 1,
|
'isRead': 1,
|
||||||
@@ -85,6 +85,7 @@ class MineController extends GetxController with BaseControllerMixin {
|
|||||||
if (result.code == 0 && result.data != null) {
|
if (result.code == 0 && result.data != null) {
|
||||||
String total = result.data["total"].toString();
|
String total = result.data["total"].toString();
|
||||||
isNotice = int.parse(total) > 0;
|
isNotice = int.parse(total) > 0;
|
||||||
|
updateUi();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:getx_scaffold/common/index.dart';
|
import 'package:getx_scaffold/common/index.dart';
|
||||||
import 'package:getx_scaffold/common/widgets/index.dart';
|
import 'package:getx_scaffold/common/widgets/index.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/c_page/message/view.dart';
|
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
@@ -18,32 +17,25 @@ class MinePage extends GetView<MineController> {
|
|||||||
id: 'mine',
|
id: 'mine',
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: const Color.fromRGBO(247, 249, 251, 1),
|
backgroundColor: Colors.grey[100],
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.all(12.0),
|
||||||
_buildUserInfoCard(),
|
child: Column(
|
||||||
const SizedBox(height: 8),
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
// 新 UI 模块开始
|
children: [
|
||||||
Padding(
|
_buildUserInfoCard(),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
const SizedBox(height: 5),
|
||||||
child: Column(
|
_buildDriverScoreCard(),
|
||||||
children: [
|
const SizedBox(height: 5),
|
||||||
_buildWalletCard(),
|
_buildMonthlyRecordCard(),
|
||||||
SizedBox(height: 16.h),
|
const SizedBox(height: 5),
|
||||||
_buildGridMenu(),
|
_buildTipsCard(),
|
||||||
SizedBox(height: 16.h),
|
const SizedBox(height: 20),
|
||||||
_buildRecommendCard(context),
|
_buildLogoutButton(),
|
||||||
SizedBox(height: 8.h),
|
const SizedBox(height: 20),
|
||||||
_buildSafetyReminderCard(),
|
],
|
||||||
SizedBox(height: 24.h),
|
),
|
||||||
_buildLogoutButton(),
|
|
||||||
SizedBox(height: 95.h),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// 新 UI 模块结束
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -51,127 +43,82 @@ class MinePage extends GetView<MineController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 构建顶部用户信息卡片
|
/// 1. 构建顶部用户信息卡片
|
||||||
Widget _buildUserInfoCard() {
|
Widget _buildUserInfoCard() {
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 1,
|
elevation: 2,
|
||||||
color: Colors.white,
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
shape: const RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
bottomLeft: Radius.circular(20),
|
|
||||||
bottomRight: Radius.circular(20),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 16, top: 40),
|
padding: const EdgeInsets.all(16.0),
|
||||||
// 增加了顶部 padding 适配状态栏
|
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Stack(
|
const CircleAvatar(
|
||||||
children: [
|
radius: 25,
|
||||||
CircleAvatar(
|
backgroundColor: Colors.blue,
|
||||||
radius: 25,
|
child: Icon(Icons.person, color: Colors.white, size: 40),
|
||||||
backgroundColor: Colors.white,
|
|
||||||
child: LoginUtil.getAssImg('ic_user_logo@2x'),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: SizedBox(
|
|
||||||
height: 16.h,
|
|
||||||
width: 16.w,
|
|
||||||
child: LoginUtil.getAssImg('ic_logo@2x'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
SizedBox(width: 8.w),
|
const SizedBox(width: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Text(
|
||||||
children: [
|
"${StorageService.to.name}",
|
||||||
Text(
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
"${StorageService.to.name}",
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8.w),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 8,
|
|
||||||
vertical: 2,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color.fromRGBO(236, 255, 234, 1), // 极浅绿色背景
|
|
||||||
border: Border.all(color: const Color(0xFFB7E19F)), // 边框
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: const Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.eco, size: 12, color: Color(0xFF52C41A)),
|
|
||||||
// 叶子图标
|
|
||||||
SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
"绿色先锋",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xFF52C41A),
|
|
||||||
fontSize: 10,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
"羚牛ID:${StorageService.to.phone}",
|
"${StorageService.to.phone}",
|
||||||
style: const TextStyle(color: Colors.grey, fontSize: 11),
|
style: TextStyle(color: Colors.grey, fontSize: 11),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
StorageService.to.hasVehicleInfo ? "已绑定车辆" : '未绑定车辆',
|
||||||
|
style: TextStyle(color: Colors.orange, fontSize: 12),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
Get.to(() => const MessagePage());
|
// 跳转消息中心
|
||||||
|
var scanResult = await Get.to(() => const MessagePage());
|
||||||
|
if (scanResult == null) {
|
||||||
|
controller.msgNotice();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
// 这里的 style 是为了模拟你图片里的灰色圆形背景
|
||||||
style: IconButton.styleFrom(
|
style: IconButton.styleFrom(
|
||||||
backgroundColor: Colors.grey[100],
|
backgroundColor: Colors.grey[100],
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
),
|
),
|
||||||
icon: Badge(
|
icon: Badge(
|
||||||
|
// label: Text('3'), // 如果你想显示数字,就加 label
|
||||||
smallSize: 8,
|
smallSize: 8,
|
||||||
backgroundColor: controller.isNotice
|
// 红点的大小
|
||||||
? Colors.red
|
backgroundColor: controller.isNotice ? Colors.red : Colors.white,
|
||||||
: Colors.transparent,
|
// 红点颜色
|
||||||
child: const Icon(
|
child: Icon(
|
||||||
Icons.notifications_outlined,
|
Icons.notifications_outlined,
|
||||||
color: Colors.black87,
|
color: Colors.black87,
|
||||||
size: 30,
|
size: 25,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 20),
|
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
_buildModernStatItem('服务天数', 'service days', '156', ''),
|
_buildStatItem(controller.violationTotal, '违章总数'),
|
||||||
const SizedBox(width: 8),
|
_buildStatItem(controller.violationScore, '扣分总数'),
|
||||||
_buildModernStatItem('准时率', 'Punctuality', controller.rate, ''),
|
_buildStatItem(controller.violationDispose, '已处理'),
|
||||||
const SizedBox(width: 8),
|
|
||||||
_buildModernStatItem('司机评分', 'Driver rating', controller.rating, ''),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -180,155 +127,88 @@ class MinePage extends GetView<MineController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统计项
|
// 用户信息卡片中的小统计项
|
||||||
Widget _buildModernStatItem(String title, String subtitle, String value, String unit) {
|
Widget _buildStatItem(String value, String label) {
|
||||||
return Expanded(
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(12.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFF8F9FA),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(subtitle, style: const TextStyle(fontSize: 9, color: Colors.grey)),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
|
||||||
textBaseline: TextBaseline.alphabetic,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
value,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(unit, style: const TextStyle(fontSize: 10, color: Colors.black54)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 我的钱包卡片
|
|
||||||
Widget _buildWalletCard() {
|
|
||||||
return Card(
|
|
||||||
elevation: 1,
|
|
||||||
color: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"我的钱包",
|
|
||||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
Text("User wallet", style: TextStyle(fontSize: 12, color: Colors.grey)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"¥ 0,00元",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.green[700],
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 2x2 功能网格菜单
|
|
||||||
Widget _buildGridMenu() {
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Text(value, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||||
children: [
|
const SizedBox(height: 4),
|
||||||
_buildGridItem(Icons.person_search_outlined, "客服评价", "3项可评"),
|
Text(label, style: const TextStyle(color: Colors.grey, fontSize: 12)),
|
||||||
const SizedBox(width: 19),
|
|
||||||
_buildGridItem(
|
|
||||||
Icons.assignment_late_outlined,
|
|
||||||
"违章处理",
|
|
||||||
"${controller.historyBreakRules}项待办",
|
|
||||||
countColor: Colors.red,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
_buildGridItem(Icons.book_outlined, "安全培训", "0个待看"),
|
|
||||||
const SizedBox(width: 19),
|
|
||||||
_buildGridItem(
|
|
||||||
Icons.verified_user_outlined,
|
|
||||||
"诚信加氢值",
|
|
||||||
"845",
|
|
||||||
isSpecial: true,
|
|
||||||
backgroundColor: const Color(0xFF006633),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildGridItem(
|
/// 2. 构建驾驶得分卡片
|
||||||
IconData icon,
|
Widget _buildDriverScoreCard() {
|
||||||
String title,
|
return Card(
|
||||||
String subtitle, {
|
elevation: 2,
|
||||||
Color? countColor,
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
bool isSpecial = false,
|
child: Padding(
|
||||||
Color? backgroundColor,
|
padding: const EdgeInsets.all(16.0),
|
||||||
}) {
|
|
||||||
return Expanded(
|
|
||||||
child: Container(
|
|
||||||
height: 100,
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: isSpecial ? backgroundColor : Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Icon(icon, color: isSpecial ? Colors.white : Colors.black87, size: 28),
|
const Text(
|
||||||
const SizedBox(height: 8),
|
'驾驶得分',
|
||||||
Text(
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
title,
|
),
|
||||||
style: TextStyle(
|
const Text('本月表现', style: TextStyle(fontSize: 12, color: Colors.grey)),
|
||||||
fontSize: 14,
|
const SizedBox(height: 20),
|
||||||
fontWeight: FontWeight.bold,
|
Center(
|
||||||
color: isSpecial ? Colors.white : Colors.black87,
|
child: SizedBox(
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
child: Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
CircularProgressIndicator(
|
||||||
|
value: (double.tryParse(controller.rating) ?? 0) / 10,
|
||||||
|
strokeWidth: 8,
|
||||||
|
backgroundColor: Colors.grey[200],
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
controller.rating,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
const SizedBox(height: 20),
|
||||||
subtitle,
|
_buildScoreDetailRow(Icons.directions_car, '安全驾驶', '无违章记录', true),
|
||||||
style: TextStyle(
|
const Divider(),
|
||||||
fontSize: 12,
|
_buildScoreDetailRow(Icons.timer, '准时率', '100%准时到达', true),
|
||||||
color: isSpecial
|
const Divider(),
|
||||||
? Colors.white.withOpacity(0.8)
|
_buildScoreDetailRow(Icons.thumb_up, '服务质量', '用户满意度高', true),
|
||||||
: (countColor ?? Colors.grey),
|
const Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 12.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'优秀驾驶员',
|
||||||
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.blue,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'A+',
|
||||||
|
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -337,194 +217,152 @@ class MinePage extends GetView<MineController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 我要推荐卡片
|
// 驾驶得分卡片中的评分项
|
||||||
Widget _buildRecommendCard(BuildContext context) {
|
Widget _buildScoreDetailRow(
|
||||||
|
IconData icon,
|
||||||
|
String title,
|
||||||
|
String subtitle,
|
||||||
|
bool isCompleted,
|
||||||
|
) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 20,
|
||||||
|
backgroundColor: Colors.blue.withOpacity(0.1),
|
||||||
|
child: Icon(icon, color: Colors.blue, size: 24),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
Text(subtitle, style: const TextStyle(fontSize: 12, color: Colors.grey)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (isCompleted) const Icon(Icons.check_circle, color: Colors.blue),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 3. 构建本月记录卡片
|
||||||
|
Widget _buildMonthlyRecordCard() {
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 0,
|
elevation: 2,
|
||||||
color: Colors.white,
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'本月记录',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildRecordRow(Icons.rate_review, '加氢预约践行率', controller.rate),
|
||||||
|
const Divider(),
|
||||||
|
_buildRecordRow(
|
||||||
|
Icons.report_problem_outlined,
|
||||||
|
'违章',
|
||||||
|
"${controller.historyBreakRules}起",
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
_buildRecordRow(Icons.car_crash_outlined, '交通事故', "${controller.accident}起"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 本月记录中的列表项
|
||||||
|
Widget _buildRecordRow(IconData icon, String title, String value) {
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
leading: CircleAvatar(
|
||||||
|
radius: 20,
|
||||||
|
backgroundColor: Colors.blue.withOpacity(0.1),
|
||||||
|
child: Icon(icon, color: Colors.blue, size: 24),
|
||||||
|
),
|
||||||
|
title: Text(title, style: const TextStyle(fontSize: 14)),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(value, style: const TextStyle(color: AppTheme.themeColor, fontSize: 14)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
// TODO: 处理点击事件
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 4. 构建提示信息卡片
|
||||||
|
Widget _buildTipsCard() {
|
||||||
|
return Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const Row(
|
_buildInfoItem(Icons.info_outline, '保持良好的驾驶习惯,提高安全评分'),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
const SizedBox(height: 10),
|
||||||
|
_buildInfoItem(Icons.rule, '遵守交通规则,避免违章扣分'),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildInfoItem(Icons.headset_mic_outlined, '如有疑问请联系客服: 400-021-1773'),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Icon(Icons.verified_outlined, color: Colors.blue, size: 20),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
const SizedBox(width: 10),
|
||||||
children: [
|
Expanded(
|
||||||
Text(
|
child: FutureBuilder<String>(
|
||||||
"我要推荐",
|
future: getVersion(),
|
||||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
builder: (context, snapshot) {
|
||||||
),
|
// 判断是否还在加载
|
||||||
Text("Recommend", style: TextStyle(fontSize: 12, color: Colors.grey)),
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
],
|
return const Text("");
|
||||||
),
|
}
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
// 如果加载完成且有数据
|
||||||
children: [
|
if (snapshot.hasData) {
|
||||||
Text("累计奖励(积分)", style: TextStyle(fontSize: 11, color: Colors.grey)),
|
return TextX.labelSmall(
|
||||||
Text(
|
"当前版本: ${snapshot.data}",
|
||||||
"0,00",
|
color: Colors.black54,
|
||||||
style: TextStyle(
|
);
|
||||||
fontSize: 18,
|
}
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.green,
|
// 错误处理
|
||||||
),
|
return const Text("");
|
||||||
),
|
},
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
if (GetPlatform.isIOS) {
|
|
||||||
// 跳转到 iOS 应用商店 (这里使用一个通用的应用商店链接模板,请确保替换为正式的 AppID)
|
|
||||||
openWebPage("https://apps.apple.com/cn/app/羚牛氢能/6756245815");
|
|
||||||
} else if (GetPlatform.isAndroid) {
|
|
||||||
// Android 弹出二维码图片
|
|
||||||
_showAndroidDownloadDialog(context);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: const Color(0xFF006633),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
minimumSize: const Size(double.infinity, 48),
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
"下载推荐",
|
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Android 端下载二维码弹窗
|
// 提示信息卡片中的列表项
|
||||||
void _showAndroidDownloadDialog(BuildContext context) {
|
Widget _buildInfoItem(IconData icon, String text) {
|
||||||
Get.dialog(
|
return Row(
|
||||||
Center(
|
children: [
|
||||||
child: Container(
|
Icon(icon, color: Colors.blue, size: 20),
|
||||||
width: 280.w,
|
const SizedBox(width: 10),
|
||||||
decoration: BoxDecoration(
|
Expanded(
|
||||||
color: Colors.white,
|
child: Text(text, style: const TextStyle(fontSize: 12, color: Colors.black54)),
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(24.0),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
"扫描二维码下载",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
// 使用 LoginUtil.getAssImg 加载你的图片 android_apk_img.png
|
|
||||||
SizedBox(
|
|
||||||
width: 180.w,
|
|
||||||
height: 180.w,
|
|
||||||
child: LoginUtil.getAssImg('android_apk_img'),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Text(
|
|
||||||
"请让被推荐人扫描上方二维码进行下载安装",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(fontSize: 13, color: Colors.grey),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Divider(height: 1),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Get.back(),
|
|
||||||
style: TextButton.styleFrom(minimumSize: const Size(double.infinity, 50)),
|
|
||||||
child: const Text(
|
|
||||||
"确 定",
|
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 安全提醒卡片
|
|
||||||
Widget _buildSafetyReminderCard() {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color.fromRGBO(242, 249, 248, 1), // 极浅绿色背景
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.info_outline, color: Colors.green[700], size: 24),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
"安全提醒",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.green[900],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Text(
|
|
||||||
"请保持良好驾驶习惯,提高安全评分,遵守交通规则,避免违章扣分。",
|
|
||||||
style: TextStyle(fontSize: 13, color: Colors.green[800], height: 1.5),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
"如有疑问请联系客服:400-021-1773",
|
|
||||||
style: TextStyle(fontSize: 13, color: Colors.green[800]),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: FutureBuilder<String>(
|
|
||||||
future: getVersion(),
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
// 判断是否还在加载
|
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
|
||||||
return const Text("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果加载完成且有数据
|
|
||||||
if (snapshot.hasData) {
|
|
||||||
return TextX.labelSmall(
|
|
||||||
"当前版本: ${snapshot.data}",
|
|
||||||
color: Colors.green[800],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 错误处理
|
|
||||||
return const Text("");
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,7 +372,7 @@ class MinePage extends GetView<MineController> {
|
|||||||
controller.logout();
|
controller.logout();
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Color.fromRGBO(204, 52, 46, 1),
|
backgroundColor: Colors.red[400],
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
minimumSize: const Size(double.infinity, 48),
|
minimumSize: const Size(double.infinity, 48),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
|
||||||
|
|||||||
@@ -335,12 +335,12 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
final startTimeStr =
|
final startTimeStr =
|
||||||
'$dateStr ${_formatTimeOfDay(startTime.value)}:00'; // Use helper directly
|
'$dateStr ${_formatTimeOfDay(startTime.value)}:00'; // Use helper directly
|
||||||
|
|
||||||
if (lastSuccessfulReservation != null &&
|
/*if (lastSuccessfulReservation != null &&
|
||||||
lastSuccessfulReservation!.id == selectedStationId.value &&
|
lastSuccessfulReservation!.id == selectedStationId.value &&
|
||||||
lastSuccessfulReservation!.startTime == startTimeStr) {
|
lastSuccessfulReservation!.startTime == startTimeStr) {
|
||||||
showToast("请勿重复提交相同时间段的预约,可在“查看预约”中修改");
|
showToast("请勿重复提交相同时间段的预约,可在“查看预约”中修改");
|
||||||
return;
|
return;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
final reservationEndDateTime = DateTime(
|
final reservationEndDateTime = DateTime(
|
||||||
selectedDate.value.year,
|
selectedDate.value.year,
|
||||||
@@ -401,6 +401,7 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
lastSuccessfulReservation = ReservationModel(
|
lastSuccessfulReservation = ReservationModel(
|
||||||
id: selectedStationId.value!,
|
id: selectedStationId.value!,
|
||||||
|
stationId: '',
|
||||||
hydAmount: ampuntStr,
|
hydAmount: ampuntStr,
|
||||||
startTime: startTimeStr,
|
startTime: startTimeStr,
|
||||||
endTime: endTimeStr,
|
endTime: endTimeStr,
|
||||||
@@ -535,11 +536,11 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
String leftHydrogen = "0";
|
String leftHydrogen = "0";
|
||||||
num maxHydrogen = 0;
|
num maxHydrogen = 0;
|
||||||
String difference = "";
|
String difference = "";
|
||||||
var progressValue = 0.0;
|
|
||||||
|
|
||||||
//用来管理查看预约的弹窗
|
//用来管理查看预约的弹窗
|
||||||
Worker? _sheetWorker;
|
Worker? _sheetWorker;
|
||||||
bool init = false;
|
bool init = false;
|
||||||
|
Timer? _refreshTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get listenLifecycleEvent => true;
|
bool get listenLifecycleEvent => true;
|
||||||
@@ -549,40 +550,34 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
super.onInit();
|
super.onInit();
|
||||||
getUserBindCarInfo();
|
getUserBindCarInfo();
|
||||||
getSiteList();
|
getSiteList();
|
||||||
_msgNotice();
|
startAutoRefresh();
|
||||||
|
|
||||||
if (!init) {
|
if (!init) {
|
||||||
_setupListener();
|
_setupListener();
|
||||||
init = true;
|
init = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isNotice = false;
|
@override
|
||||||
|
void onPaused() {
|
||||||
Future<void> _msgNotice() async {
|
stopAutoRefresh();
|
||||||
final Map<String, dynamic> requestData = {
|
super.onPaused();
|
||||||
'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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
void startAutoRefresh() {
|
||||||
void dispose() {
|
// 先停止已存在的定时器,防止重复启动
|
||||||
_sheetWorker?.dispose();
|
stopAutoRefresh();
|
||||||
super.dispose();
|
|
||||||
|
// 创建一个每1分钟执行一次的周期性定时器
|
||||||
|
_refreshTimer = Timer.periodic(const Duration(minutes: 1), (timer) {
|
||||||
|
getSiteList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
///停止定时器的方法
|
||||||
|
void stopAutoRefresh() {
|
||||||
|
// 如果定时器存在并且是激活状态,就取消它
|
||||||
|
_refreshTimer?.cancel();
|
||||||
|
_refreshTimer = null; // 置为null,方便判断
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setupListener() {
|
void _setupListener() {
|
||||||
@@ -649,8 +644,9 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
void getCatinfo() async {
|
void getCatinfo() async {
|
||||||
try {
|
try {
|
||||||
|
HttpService.to.setBaseUrl(AppTheme.car_service_url);
|
||||||
var responseData = await HttpService.to.post(
|
var responseData = await HttpService.to.post(
|
||||||
'appointment/vehicle/getHydrogenInfoByPlateNumber',
|
'VehicleData/getHydrogenInfoByPlateNumber',
|
||||||
data: {
|
data: {
|
||||||
'userName': "xll@lingniu",
|
'userName': "xll@lingniu",
|
||||||
'password': "4q%3!l6s0p",
|
'password': "4q%3!l6s0p",
|
||||||
@@ -675,29 +671,11 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
amountController.text = flooredDifference.toString();
|
amountController.text = flooredDifference.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxHydrogen > 0) {
|
|
||||||
progressValue = leftHydrogenNum / maxHydrogen;
|
|
||||||
|
|
||||||
// 边界处理:确保值在 0 到 1 之间
|
|
||||||
if (progressValue > 1.0) progressValue = 1.0;
|
|
||||||
if (progressValue < 0.0) progressValue = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUi();
|
updateUi();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
HttpService.to.setBaseUrl(AppTheme.test_service_url);
|
||||||
}
|
}
|
||||||
renderSliderTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
double current = 0.0;
|
|
||||||
double maxVal = 0.0;
|
|
||||||
|
|
||||||
void renderSliderTheme() {
|
|
||||||
current = double.tryParse(amountController.text) ?? 0.0;
|
|
||||||
maxVal = double.tryParse(difference) ?? 100.0;
|
|
||||||
if (maxVal <= 0) maxVal = 100.0;
|
|
||||||
|
|
||||||
updateUi();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void getSiteList() async {
|
void getSiteList() async {
|
||||||
@@ -779,6 +757,7 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
// 如果未绑定车辆,且本次会话尚未提示过,则弹出提示
|
// 如果未绑定车辆,且本次会话尚未提示过,则弹出提示
|
||||||
if (!StorageService.to.hasShownBindVehicleDialog &&
|
if (!StorageService.to.hasShownBindVehicleDialog &&
|
||||||
StorageService.to.isLoggedIn &&
|
StorageService.to.isLoggedIn &&
|
||||||
|
StorageService.to.loginChannel == LoginChannel.driver &&
|
||||||
!StorageService.to.hasVehicleInfo) {
|
!StorageService.to.hasVehicleInfo) {
|
||||||
Future.delayed(const Duration(milliseconds: 500), () {
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
DialogX.to.showConfirmDialog(
|
DialogX.to.showConfirmDialog(
|
||||||
@@ -809,6 +788,8 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
if (_debounce != null) {
|
if (_debounce != null) {
|
||||||
_debounce?.cancel();
|
_debounce?.cancel();
|
||||||
}
|
}
|
||||||
|
_sheetWorker?.dispose();
|
||||||
|
stopAutoRefresh();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ class ReservationEditController extends GetxController with BaseControllerMixin
|
|||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
final isToday =
|
final isToday =
|
||||||
selectedDate.value.year == now.year &&
|
selectedDate.value.year == now.year &&
|
||||||
selectedDate.value.month == now.month &&
|
selectedDate.value.month == now.month &&
|
||||||
selectedDate.value.day == now.day;
|
selectedDate.value.day == now.day;
|
||||||
|
|
||||||
final List<TimeSlot> availableSlots = [];
|
final List<TimeSlot> availableSlots = [];
|
||||||
for (int i = 0; i < 24; i++) {
|
for (int i = 0; i < 24; i++) {
|
||||||
@@ -123,7 +123,7 @@ class ReservationEditController extends GetxController with BaseControllerMixin
|
|||||||
|
|
||||||
// 查找当前选中的时间对应的新列表中的索引
|
// 查找当前选中的时间对应的新列表中的索引
|
||||||
int initialItem = availableSlots.indexWhere(
|
int initialItem = availableSlots.indexWhere(
|
||||||
(slot) => slot.start.hour == startTime.value.hour,
|
(slot) => slot.start.hour == startTime.value.hour,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (initialItem == -1) {
|
if (initialItem == -1) {
|
||||||
@@ -132,7 +132,6 @@ class ReservationEditController extends GetxController with BaseControllerMixin
|
|||||||
|
|
||||||
TimeSlot tempSlot = availableSlots[initialItem];
|
TimeSlot tempSlot = availableSlots[initialItem];
|
||||||
|
|
||||||
|
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
Container(
|
Container(
|
||||||
height: 300,
|
height: 300,
|
||||||
@@ -228,6 +227,7 @@ class ReservationEditController extends GetxController with BaseControllerMixin
|
|||||||
'appointment/orderAddHyd/saveOrUpdate',
|
'appointment/orderAddHyd/saveOrUpdate',
|
||||||
data: {
|
data: {
|
||||||
'id': reservation.id,
|
'id': reservation.id,
|
||||||
|
'stationId': reservation.stationId,
|
||||||
'startTime': startTimeStr,
|
'startTime': startTimeStr,
|
||||||
'endTime': endTimeStr,
|
'endTime': endTimeStr,
|
||||||
'hydAmount': amountStr,
|
'hydAmount': amountStr,
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
|
|
||||||
class WebController extends GetxController {
|
|
||||||
late String title;
|
|
||||||
late String url;
|
|
||||||
|
|
||||||
final RxDouble progress = 0.0.obs;
|
|
||||||
InAppWebViewController? webViewController;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onInit() {
|
|
||||||
super.onInit();
|
|
||||||
// 从参数中获取标题和URL
|
|
||||||
title = Get.arguments['title'] ?? '详情';
|
|
||||||
url = Get.arguments['url'] ?? '';
|
|
||||||
}
|
|
||||||
|
|
||||||
void onWebViewCreated(InAppWebViewController controller) {
|
|
||||||
webViewController = controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onProgressChanged(InAppWebViewController controller, int progressValue) {
|
|
||||||
progress.value = progressValue / 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
|
|
||||||
import 'controller.dart';
|
|
||||||
|
|
||||||
class WebViewPage extends GetView<WebController> {
|
|
||||||
const WebViewPage({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Get.put(WebController());
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(controller.title),
|
|
||||||
centerTitle: true,
|
|
||||||
bottom: PreferredSize(
|
|
||||||
preferredSize: const Size.fromHeight(2.0),
|
|
||||||
child: Obx(
|
|
||||||
() => controller.progress.value < 1.0
|
|
||||||
? LinearProgressIndicator(
|
|
||||||
value: controller.progress.value,
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(
|
|
||||||
Theme.of(context).primaryColor,
|
|
||||||
),
|
|
||||||
minHeight: 2.0,
|
|
||||||
)
|
|
||||||
: const SizedBox(height: 2.0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: InAppWebView(
|
|
||||||
initialUrlRequest: URLRequest(url: WebUri(controller.url)),
|
|
||||||
initialSettings: InAppWebViewSettings(
|
|
||||||
isInspectable: true,
|
|
||||||
javaScriptEnabled: true,
|
|
||||||
javaScriptCanOpenWindowsAutomatically: true,
|
|
||||||
useShouldOverrideUrlLoading: true,
|
|
||||||
mixedContentMode: MixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW,
|
|
||||||
mediaPlaybackRequiresUserGesture: false,
|
|
||||||
allowsInlineMediaPlayback: true,
|
|
||||||
),
|
|
||||||
onWebViewCreated: controller.onWebViewCreated,
|
|
||||||
onProgressChanged: controller.onProgressChanged,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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/common/styles/theme.dart';
|
||||||
import 'package:ln_jq_app/pages/b_page/base_widgets/view.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/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 'package:ln_jq_app/pages/login/view.dart';
|
||||||
|
|
||||||
import '../../storage_service.dart';
|
import '../../storage_service.dart';
|
||||||
@@ -156,6 +157,7 @@ class HomeController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
Future<void> _onNotificationOpened(Map<dynamic, dynamic> message) async {
|
Future<void> _onNotificationOpened(Map<dynamic, dynamic> message) async {
|
||||||
Logger.d('onNotificationOpened ====> $message');
|
Logger.d('onNotificationOpened ====> $message');
|
||||||
|
await Get.to(() => const MessagePage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onNotificationRemoved(Map<dynamic, dynamic> message) async {
|
Future<void> _onNotificationRemoved(Map<dynamic, dynamic> message) async {
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
import 'package:ln_jq_app/common/model/base_model.dart';
|
|
||||||
|
|
||||||
class LoginController extends GetxController with BaseControllerMixin {
|
class LoginController extends GetxController with BaseControllerMixin {
|
||||||
@override
|
@override
|
||||||
@@ -10,73 +7,19 @@ class LoginController extends GetxController with BaseControllerMixin {
|
|||||||
LoginController();
|
LoginController();
|
||||||
|
|
||||||
// 控制输入框的 TextEditingController
|
// 控制输入框的 TextEditingController
|
||||||
final TextEditingController phoneController = TextEditingController();
|
final TextEditingController driverIdentityController = TextEditingController();
|
||||||
final TextEditingController codeController = TextEditingController();
|
|
||||||
|
|
||||||
final TextEditingController stationIdController = TextEditingController();
|
final TextEditingController stationIdController = TextEditingController();
|
||||||
final TextEditingController passwordController = TextEditingController();
|
final TextEditingController passwordController = TextEditingController();
|
||||||
|
|
||||||
|
|
||||||
// --- 倒计时逻辑 ---
|
|
||||||
final RxInt countdown = 0.obs;
|
|
||||||
Timer? _timer;
|
|
||||||
|
|
||||||
void startCountdown() async {
|
|
||||||
if (phoneController.text.isEmpty || !phoneController.text.isPhoneNumber) {
|
|
||||||
showToast("请输入正确的手机号");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (countdown.value > 0) return;
|
|
||||||
|
|
||||||
// 调用发送验证码接口
|
|
||||||
var responseData = await HttpService.to.post(
|
|
||||||
'appointment/login/sendCode',
|
|
||||||
data: {"mobile": phoneController.text},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (responseData == null) {
|
|
||||||
showToast('验证码发送失败,请稍后重试');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
var result = BaseModel.fromJson(responseData.data);
|
|
||||||
|
|
||||||
if (result.code != 0) {
|
|
||||||
showToast(result.error);
|
|
||||||
dismissLoading();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
showToast("验证码已发送");
|
|
||||||
|
|
||||||
countdown.value = 60;
|
|
||||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|
||||||
if (countdown.value > 0) {
|
|
||||||
countdown.value--;
|
|
||||||
} else {
|
|
||||||
_timer?.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
showToast('验证码服务异常,请稍后重试');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
|
|
||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
_timer?.cancel();
|
|
||||||
phoneController.dispose();
|
|
||||||
codeController.dispose();
|
|
||||||
|
|
||||||
stationIdController.dispose();
|
|
||||||
passwordController.dispose();
|
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,12 @@ class UrlHostController extends GetxController {
|
|||||||
// 预设的域名列表
|
// 预设的域名列表
|
||||||
final List<String> presetUrls = [
|
final List<String> presetUrls = [
|
||||||
'https://beta-esg.api.lnh2e.com/', // 测试环境
|
'https://beta-esg.api.lnh2e.com/', // 测试环境
|
||||||
'http://47.101.201.13:8443/api/', // 测试环境
|
|
||||||
'http://192.168.110.44:8080/', // 沈辰本地
|
'http://192.168.110.44:8080/', // 沈辰本地
|
||||||
'http://192.168.110.222:8080/', // 何斐本地
|
'http://192.168.110.222:8080/', // 何斐本地
|
||||||
];
|
];
|
||||||
|
|
||||||
final List<String> urlNames = [
|
final List<String> urlNames = [
|
||||||
'测试环境',
|
'测试环境',
|
||||||
'线上环境',
|
|
||||||
'沈辰本地环境',
|
'沈辰本地环境',
|
||||||
'何斐本地环境',
|
'何斐本地环境',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:ln_jq_app/pages/home/view.dart';
|
|
||||||
import 'package:ln_jq_app/pages/login/view.dart';
|
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
|
||||||
|
|
||||||
class WelcomeController extends GetxController {
|
|
||||||
@override
|
|
||||||
void onReady() {
|
|
||||||
super.onReady();
|
|
||||||
// 移除原生闪屏页(如果有的话)
|
|
||||||
FlutterNativeSplash.remove();
|
|
||||||
_startTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _startTimer() {
|
|
||||||
// 1.5秒后执行跳转逻辑
|
|
||||||
Future.delayed(const Duration(milliseconds: 1500), () {
|
|
||||||
Get.offAll(() => const HomePage());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _jumpToNextPage() {
|
|
||||||
if (StorageService.to.isLoggedIn) {
|
|
||||||
// 已登录,跳转到首页
|
|
||||||
Get.offAll(() => const HomePage());
|
|
||||||
} else {
|
|
||||||
// 未登录,跳转到登录页
|
|
||||||
Get.offAll(() => const LoginPage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:ln_jq_app/common/login_util.dart';
|
|
||||||
import 'controller.dart';
|
|
||||||
|
|
||||||
class WelcomePage extends GetView<WelcomeController> {
|
|
||||||
const WelcomePage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// 初始化控制器
|
|
||||||
Get.put(WelcomeController());
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
body: SizedBox.expand(
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned(
|
|
||||||
top: 0,
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/images/welcome.png',
|
|
||||||
fit: BoxFit.fill
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -142,7 +142,7 @@ class StorageService extends GetxService {
|
|||||||
|
|
||||||
void delAlias() async {
|
void delAlias() async {
|
||||||
String phoen = StorageService.to.phone ?? "";
|
String phoen = StorageService.to.phone ?? "";
|
||||||
var result = await _aliyunPush.removeAlias(phoen);
|
var result = await await _aliyunPush.unbindAccount();
|
||||||
var code = result['code'];
|
var code = result['code'];
|
||||||
if (code == kAliyunPushSuccessCode) {
|
if (code == kAliyunPushSuccessCode) {
|
||||||
Logger.d('删除别名$phoen成功');
|
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
|
# 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
|
# 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.
|
# 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:
|
environment:
|
||||||
sdk: ^3.9.0
|
sdk: ^3.9.0
|
||||||
|
|||||||