5 Commits

Author SHA1 Message Date
9b6f93ca95 隐私调整 2026-03-17 13:31:58 +08:00
eb41ecaec4 更新插件 2026-03-16 16:00:38 +08:00
384de27f2c Merge branch 'dev' 2026-03-13 17:18:43 +08:00
84b174c4a5 协议通知后注册推送 2026-03-13 17:09:30 +08:00
02e1946319 文字边界 2026-03-05 11:20:14 +08:00
9 changed files with 205 additions and 76 deletions

View File

@@ -61,48 +61,6 @@
<meta-data
android:name="com.alibaba.app.appsecret"
android:value="39628204345a4240b5b645b68a5896c7" />
<!-- 华为通道的参数appid -->
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="" />
<!-- vivo通道的参数api_key为appkey -->
<meta-data
android:name="com.vivo.push.api_key"
android:value="" />
<meta-data
android:name="com.vivo.push.app_id"
android:value="" />
<!-- honor通道的参数-->
<meta-data
android:name="com.hihonor.push.app_id"
android:value="" />
<!-- oppo -->
<meta-data
android:name="com.oppo.push.key"
android:value="" />
<meta-data
android:name="com.oppo.push.secret"
android:value="" />
<!-- 小米-->
<meta-data
android:name="com.xiaomi.push.id"
android:value="id=2222222222222222222" />
<meta-data
android:name="com.xiaomi.push.key"
android:value="id=5555555555555" />
<!-- 魅族-->
<meta-data
android:name="com.meizu.push.id"
android:value="" />
<meta-data
android:name="com.meizu.push.key"
android:value="" />
<!-- 接收推送消息 -->
<receiver

View File

@@ -21,9 +21,7 @@
.amap-callamap,
.amap-lib-driving-callBtn,
.amap-copyright,
.amap-logo {
display: none !important;
}
.amap-logo{bottom: 60px}
/* 去除高德默认的 label 边框 and 背景 */
.amap-marker-label {

View File

@@ -1,9 +1,13 @@
import 'dart:io';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.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/c_page/message/view.dart';
import 'package:ln_jq_app/pages/common/webview/view.dart';
class ReservationPage extends GetView<ReservationController> {
const ReservationPage({super.key});
@@ -36,6 +40,30 @@ class ReservationPage extends GetView<ReservationController> {
_buildSystemTips(),
SizedBox(height: 24),
_buildLogoutButton(),
SizedBox(height: 15.h),
Text.rich(
TextSpan(
style: const TextStyle(color: Colors.grey, fontSize: 13),
children: [
TextSpan(
text: '《用户协议》',
style: TextStyle(color: Colors.blue, fontSize: 13),
recognizer: TapGestureRecognizer()
..onTap = () {
openPage("用户协议", "https://lnh2e.com/user_agreement.html");
},
),
TextSpan(
text: '《隐私政策》',
style: TextStyle(color: Colors.blue, fontSize: 13),
recognizer: TapGestureRecognizer()
..onTap = () {
openPage("隐私政策", "https://lnh2e.com/privacy_agreement.html");
},
),
],
),
),
SizedBox(height: 95.h),
],
),
@@ -48,6 +76,14 @@ class ReservationPage extends GetView<ReservationController> {
);
}
void openPage(String title, String url) {
if (Platform.isIOS) {
openWebPage(url);
return;
}
Get.to(() => const WebViewPage(), arguments: {'title': title, 'url': url});
}
/// 1. 顶部个人信息及统计栏
Widget _buildTopSection(BuildContext context) {
return Container(
@@ -78,11 +114,15 @@ class ReservationPage extends GetView<ReservationController> {
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 +138,11 @@ class ReservationPage extends GetView<ReservationController> {
),
),
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 +150,7 @@ class ReservationPage extends GetView<ReservationController> {
),
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 +269,17 @@ class ReservationPage extends GetView<ReservationController> {
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 +557,15 @@ class ReservationPage extends GetView<ReservationController> {
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)),
),
),
],
),
],
),
);

View File

@@ -1,10 +1,15 @@
import 'dart:io';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:getx_scaffold/common/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/common/webview/view.dart';
import 'package:ln_jq_app/storage_service.dart';
import 'controller.dart';
@@ -38,6 +43,30 @@ class MinePage extends GetView<MineController> {
_buildSafetyReminderCard(),
SizedBox(height: 24.h),
_buildLogoutButton(),
SizedBox(height: 15.h),
Text.rich(
TextSpan(
style: const TextStyle(color: Colors.grey, fontSize: 13),
children: [
TextSpan(
text: '《用户协议》',
style: TextStyle(color: Colors.blue, fontSize: 13),
recognizer: TapGestureRecognizer()
..onTap = () {
openPage("用户协议", "https://lnh2e.com/user_agreement.html");
},
),
TextSpan(
text: '《隐私政策》',
style: TextStyle(color: Colors.blue, fontSize: 13),
recognizer: TapGestureRecognizer()
..onTap = () {
openPage("隐私政策", "https://lnh2e.com/privacy_agreement.html");
},
),
],
),
),
SizedBox(height: 95.h),
],
),
@@ -51,6 +80,15 @@ class MinePage extends GetView<MineController> {
);
}
void openPage(String title, String url) {
if (Platform.isIOS) {
openWebPage(url);
return;
}
Get.to(() => const WebViewPage(), arguments: {'title': title, 'url': url});
}
/// 构建顶部用户信息卡片
Widget _buildUserInfoCard() {
return Card(

View File

@@ -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();

View File

@@ -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<LoginPage> with SingleTickerProviderStateMix
bool _obscureText = true;
bool _rememberPassword = true;
bool _credentialsLoaded = false;
bool isPushInitialized = false;
@override
void initState() {
@@ -146,7 +150,8 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
const SizedBox(height: 10),
buildAgreement(),
const SizedBox(height: 40),
const SizedBox(height: 80),
_buildFooterSlogan()
],
),
),
@@ -155,7 +160,6 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
),
),
),
Positioned(left: 0, right: 0, bottom: 33.h, child: _buildFooterSlogan()),
if (AppTheme.is_show_host)
Positioned(
top: 40.h,
@@ -388,13 +392,28 @@ class _LoginPageState extends State<LoginPage> 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<LoginPage> with SingleTickerProviderStateMix
addAlias(identifier);
}
// 申请通知权限
Future<void> _requestNotificationPermission() async {
final PermissionStatus status = await Permission.notification.request();
if (status.isGranted) {
Logger.d('通知权限已授予');
} else if (status.isPermanentlyDenied) {
Logger.d('通知权限被永久拒绝');
}
}
// 初始化推送服务
Future<void> _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<void> _onNotificationOpened(Map<dynamic, dynamic> message) async {
await Get.to(() => const MessagePage());
}
final _aliyunPush = AliyunPushFlutter();
void addAlias(String alias) async {

View File

@@ -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<StorageService> init() async {
@@ -63,9 +66,12 @@ class StorageService extends GetxService {
String? get stationPassword => _box.read<String?>(_stationPasswordKey);
// 新增:获取绑定车辆”弹窗是否已显示的标志
// 新增:获取绑定车辆”弹窗是否已显示的标志
bool get hasShownBindVehicleDialog => _box.read<bool>(_bindDialogShownKey) ?? false;
// 获取隐私政策是否已同意
bool get isPrivacyAgreed => _box.read<bool>(_privacyAgreedKey) ?? false;
VehicleInfo? get vehicleInfo {
final vehicleJson = _box.read<String?>(_vehicleInfoKey);
if (vehicleJson != null) {
@@ -110,6 +116,11 @@ class StorageService extends GetxService {
await _box.write(_stationPasswordKey, password);
}
// 保存隐私政策同意状态
Future<void> savePrivacyAgreed(bool agreed) async {
await _box.write(_privacyAgreedKey, agreed);
}
// 新增:标记“绑定车辆”弹窗已显示
Future<void> markBindVehicleDialogAsShown() async {
await _box.write(_bindDialogShownKey, true);

View File

@@ -162,13 +162,13 @@ packages:
source: hosted
version: "3.2.4"
device_info_plus:
dependency: transitive
dependency: "direct overridden"
description:
name: device_info_plus
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
sha256: "4df8babf73058181227e18b08e6ea3520cf5fc5d796888d33b7cb0f33f984b7c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "10.1.2"
version: "12.3.0"
device_info_plus_platform_interface:
dependency: transitive
description:
@@ -1230,10 +1230,10 @@ packages:
dependency: transitive
description:
name: win32_registry
sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.5"
version: "2.1.0"
xdg_directories:
dependency: transitive
description:

View File

@@ -65,6 +65,7 @@ dev_dependencies:
dependency_overrides:
intl: 0.19.0
device_info_plus: ^12.3.0
flutter: