From 02e19463190226c1183745b56de387055480cc06 Mon Sep 17 00:00:00 2001 From: userGyl Date: Thu, 5 Mar 2026 11:20:14 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=96=87=E5=AD=97=E8=BE=B9=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/pages/b_page/reservation/view.dart | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/ln_jq_app/lib/pages/b_page/reservation/view.dart b/ln_jq_app/lib/pages/b_page/reservation/view.dart index ae6d7a5..ed89f3b 100644 --- a/ln_jq_app/lib/pages/b_page/reservation/view.dart +++ b/ln_jq_app/lib/pages/b_page/reservation/view.dart @@ -78,11 +78,15 @@ class ReservationPage extends GetView { children: [ Row( children: [ - Text( - controller.name, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, + Flexible( + child: Text( + controller.name, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 8), @@ -98,12 +102,11 @@ class ReservationPage extends GetView { ), ), IconButton( - onPressed: () async{ + onPressed: () async { var scanResult = await Get.to(() => const MessagePage()); if (scanResult == null) { controller.msgNotice(); } - }, style: IconButton.styleFrom( backgroundColor: Colors.grey[100], @@ -111,9 +114,7 @@ class ReservationPage extends GetView { ), icon: Badge( smallSize: 8, - backgroundColor: controller.isNotice - ? Colors.red - : Colors.transparent, + backgroundColor: controller.isNotice ? Colors.red : Colors.transparent, child: const Icon( Icons.notifications_outlined, color: Colors.black87, @@ -232,12 +233,17 @@ class ReservationPage extends GetView { label, style: TextStyle(color: Colors.grey, fontSize: 11.sp), ), - Text( - value, - style: TextStyle( - color: Color(0xFF333333), - fontSize: 12.sp, - fontWeight: FontWeight.bold, + Expanded( + child: Text( + value, + textAlign: TextAlign.right, + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle( + color: const Color(0xFF333333), + fontSize: 12.sp, + fontWeight: FontWeight.bold, + ), ), ), ], @@ -515,14 +521,15 @@ class ReservationPage extends GetView { style: ElevatedButton.styleFrom( backgroundColor: kPrimaryColor, minimumSize: const Size(double.infinity, 50), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), ), child: const Text("发送广播", style: TextStyle(color: Colors.white)), ), ), ], ), - ], ), ); From 84b174c4a59010b651c0214b90ac63d861bdb715 Mon Sep 17 00:00:00 2001 From: userGyl Date: Fri, 13 Mar 2026 17:09:30 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E5=90=8E=E6=B3=A8=E5=86=8C=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ln_jq_app/lib/pages/home/controller.dart | 11 +++- ln_jq_app/lib/pages/login/view.dart | 77 +++++++++++++++++++++++- ln_jq_app/lib/storage_service.dart | 15 ++++- 3 files changed, 97 insertions(+), 6 deletions(-) diff --git a/ln_jq_app/lib/pages/home/controller.dart b/ln_jq_app/lib/pages/home/controller.dart index 9588648..998664f 100644 --- a/ln_jq_app/lib/pages/home/controller.dart +++ b/ln_jq_app/lib/pages/home/controller.dart @@ -29,8 +29,14 @@ class HomeController extends GetxController with BaseControllerMixin { @override void onInit() { super.onInit(); - initAliyunPush(); - addPushCallback(); + + // 检查是否同意过隐私政策,只有同意后才初始化推送 + if (StorageService.to.isPrivacyAgreed) { + requestPermission(); + initAliyunPush(); + addPushCallback(); + } + FlutterNativeSplash.remove(); log('page-init'); @@ -152,7 +158,6 @@ class HomeController extends GetxController with BaseControllerMixin { // 根据登录状态和登录渠道返回不同的首页 Widget getHomePage() { - requestPermission(); if (StorageService.to.isLoggedIn) { if (StorageService.to.loginChannel == LoginChannel.station) { return B_BaseWidgetsPage(); diff --git a/ln_jq_app/lib/pages/login/view.dart b/ln_jq_app/lib/pages/login/view.dart index ce47516..d23150c 100644 --- a/ln_jq_app/lib/pages/login/view.dart +++ b/ln_jq_app/lib/pages/login/view.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:getx_scaffold/getx_scaffold.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:ln_jq_app/common/login_util.dart'; import 'package:ln_jq_app/common/model/base_model.dart'; import 'package:ln_jq_app/common/model/vehicle_info.dart'; @@ -17,6 +18,8 @@ import 'package:ln_jq_app/pages/login/controller.dart'; import 'package:ln_jq_app/pages/url_host/view.dart'; import 'package:ln_jq_app/storage_service.dart'; +import '../c_page/message/view.dart'; + class LoginPage extends StatefulWidget { const LoginPage({super.key}); @@ -30,6 +33,7 @@ class _LoginPageState extends State with SingleTickerProviderStateMix bool _obscureText = true; bool _rememberPassword = true; bool _credentialsLoaded = false; + bool isPushInitialized = false; @override void initState() { @@ -388,13 +392,28 @@ class _LoginPageState extends State with SingleTickerProviderStateMix content: _buildDialogContent(), confirmText: '同意', cancelText: '拒绝', - onConfirm: () { + onConfirm: () async { _isAgreed = true; controller.updateUi(); + + // 保存隐私政策同意状态 + await StorageService.to.savePrivacyAgreed(true); + + // 申请通知权限 + await _requestNotificationPermission(); + + // 初始化阿里云推送 + await _initPushService(); }, ); return; } + + // 如果已经同意过,但推送还没初始化,则初始化 + if (!isPushInitialized) { + await _initPushService(); + } + _tabController.index == 0 ? _handleDriverLogin(controller) : _handleStationLogin(controller); @@ -536,6 +555,62 @@ class _LoginPageState extends State with SingleTickerProviderStateMix addAlias(identifier); } + // 申请通知权限 + Future _requestNotificationPermission() async { + final PermissionStatus status = await Permission.notification.request(); + if (status.isGranted) { + Logger.d('通知权限已授予'); + } else if (status.isPermanentlyDenied) { + Logger.d('通知权限被永久拒绝'); + } + } + + // 初始化推送服务 + Future _initPushService() async { + try { + final _aliyunPush = AliyunPushFlutter(); + + // 初始化推送 + final String appKey = Platform.isIOS ? AppTheme.ios_key : AppTheme.android_key; + final String appSecret = Platform.isIOS + ? AppTheme.ios_appsecret + : AppTheme.android_appsecret; + + final result = await _aliyunPush.initPush(appKey: appKey, appSecret: appSecret); + if (result['code'] != kAliyunPushSuccessCode) { + Logger.d('推送初始化失败: ${result['errorMsg']}'); + return; + } + + // 配置平台特定设置 + if (Platform.isIOS) { + await _aliyunPush.showIOSNoticeWhenForeground(true); + } else if (Platform.isAndroid) { + await _aliyunPush.setNotificationInGroup(true); + await _aliyunPush.createAndroidChannel( + "xll_push_android", + '新消息通知', + 4, + '用于接收加氢站实时状态提醒', + ); + } + + // 添加推送回调 + _aliyunPush.addMessageReceiver( + onNotificationOpened: _onNotificationOpened, + ); + + isPushInitialized = true; + Logger.d('推送服务初始化成功'); + } catch (e) { + Logger.d('推送服务初始化异常: $e'); + } + } + + Future _onNotificationOpened(Map message) async { + await Get.to(() => const MessagePage()); + } + final _aliyunPush = AliyunPushFlutter(); void addAlias(String alias) async { diff --git a/ln_jq_app/lib/storage_service.dart b/ln_jq_app/lib/storage_service.dart index ec73139..6549205 100644 --- a/ln_jq_app/lib/storage_service.dart +++ b/ln_jq_app/lib/storage_service.dart @@ -25,11 +25,14 @@ class StorageService extends GetxService { static const String _stationAccountKey = 'station_account'; static const String _stationPasswordKey = 'station_password'; - // 新增:用于标记“绑定车辆”弹窗是否已在本会话中显示过 + // 新增:用于标记”绑定车辆”弹窗是否已在本会话中显示过 static const String _bindDialogShownKey = 'bind_vehicle_dialog_shown'; static const String _hostUrlKey = 'host_url'; + // 隐私政策相关 + static const String _privacyAgreedKey = 'privacy_agreed'; + static StorageService get to => Get.find(); Future init() async { @@ -63,9 +66,12 @@ class StorageService extends GetxService { String? get stationPassword => _box.read(_stationPasswordKey); - // 新增:获取“绑定车辆”弹窗是否已显示的标志 + // 新增:获取”绑定车辆”弹窗是否已显示的标志 bool get hasShownBindVehicleDialog => _box.read(_bindDialogShownKey) ?? false; + // 获取隐私政策是否已同意 + bool get isPrivacyAgreed => _box.read(_privacyAgreedKey) ?? false; + VehicleInfo? get vehicleInfo { final vehicleJson = _box.read(_vehicleInfoKey); if (vehicleJson != null) { @@ -110,6 +116,11 @@ class StorageService extends GetxService { await _box.write(_stationPasswordKey, password); } + // 保存隐私政策同意状态 + Future savePrivacyAgreed(bool agreed) async { + await _box.write(_privacyAgreedKey, agreed); + } + // 新增:标记“绑定车辆”弹窗已显示 Future markBindVehicleDialogAsShown() async { await _box.write(_bindDialogShownKey, true);