Compare commits

..

2 Commits

Author SHA1 Message Date
6629c8047f Merge branch 'dev' 2025-12-25 10:41:04 +08:00
bfa615a7f4 扫码无权限优化,司机预约多弹窗 2025-12-25 10:40:26 +08:00
6 changed files with 128 additions and 46 deletions

View File

@@ -131,13 +131,13 @@
<!-- 1. 配置安全密钥 --> <!-- 1. 配置安全密钥 -->
<script type="text/javascript"> <script type="text/javascript">
window._AMapSecurityConfig = { window._AMapSecurityConfig = {
securityJsCode: '0529b72df6bf0c577ff2182cb8b1d970', securityJsCode: 'aa3a22c19ed76b27f8a587555d6981c8',
} }
</script> </script>
<!-- 2. 加载地图和插件 (去掉了 Geolocation 插件,避免弹窗) --> <!-- 2. 加载地图和插件 (去掉了 Geolocation 插件,避免弹窗) -->
<script <script
src="https://webapi.amap.com/maps?v=2.0&key=2cc1d822e313307fe311c3127a1deeb5&plugin=AMap.MoveAnimation,AMap.Driving,AMap.TruckDriving,AMap.AutoComplete,AMap.ToolBar,AMap.Scale,AMap.Geocoder"> src="https://webapi.amap.com/maps?v=2.0&key=ecd74ece8cb14c9dad67675f83c3274d&plugin=AMap.MoveAnimation,AMap.Driving,AMap.TruckDriving,AMap.AutoComplete,AMap.ToolBar,AMap.Scale,AMap.Geocoder">
</script> </script>
</head> </head>

View File

@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.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/styles/theme.dart'; import 'package:ln_jq_app/common/styles/theme.dart';
@@ -277,9 +278,10 @@ class SiteController extends GetxController with BaseControllerMixin {
child: TextField( child: TextField(
controller: amountController, controller: amountController,
textAlign: TextAlign.center, textAlign: TextAlign.center,
keyboardType: const TextInputType.numberWithOptions( keyboardType: TextInputType.number,
decimal: true, inputFormatters: [
), FilteringTextInputFormatter.digitsOnly, // 只允许数字输入
],
style: TextStyle( style: TextStyle(
fontSize: 22, fontSize: 22,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,

View File

@@ -17,6 +17,7 @@ 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 '../../../common/styles/theme.dart'; import '../../../common/styles/theme.dart';
import 'reservation_list_bottomsheet.dart';
/// Helper class for managing time slots /// Helper class for managing time slots
class TimeSlot { class TimeSlot {
@@ -547,6 +548,10 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
num maxHydrogen = 0; num maxHydrogen = 0;
String difference = ""; String difference = "";
//用来管理查看预约的弹窗
Worker? _sheetWorker;
bool init = false;
@override @override
bool get listenLifecycleEvent => true; bool get listenLifecycleEvent => true;
@@ -555,6 +560,32 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
super.onInit(); super.onInit();
getUserBindCarInfo(); getUserBindCarInfo();
getSiteList(); getSiteList();
if (!init) {
_setupListener();
init = true;
}
}
@override
void dispose() {
_sheetWorker?.dispose();
super.dispose();
}
void _setupListener() {
_sheetWorker = ever(shouldShowReservationList, (bool shouldShow) {
if (shouldShow) {
Get.bottomSheet(
const ReservationListBottomSheet(),
isScrollControlled: true, // 允许弹窗使用更多屏幕高度
backgroundColor: Colors.transparent,
);
// 重要:显示后立即将信号重置为 false防止不必要的重复弹出
shouldShowReservationList.value = false;
}
});
} }
void getUserBindCarInfo() { void getUserBindCarInfo() {
@@ -628,6 +659,11 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
final leftHydrogenNum = double.tryParse(leftHydrogen) ?? 0.0; final leftHydrogenNum = double.tryParse(leftHydrogen) ?? 0.0;
difference = (maxHydrogen - leftHydrogenNum).toStringAsFixed(2); difference = (maxHydrogen - leftHydrogenNum).toStringAsFixed(2);
int flooredDifference = (maxHydrogen - leftHydrogenNum).floor();
if (flooredDifference > 0) {
amountController.text = flooredDifference.toString();
}
updateUi(); updateUi();
} catch (e) { } catch (e) {
} finally { } finally {

View File

@@ -13,7 +13,7 @@ import 'reservation_list_bottomsheet.dart';
class ReservationPage extends GetView<C_ReservationController> { class ReservationPage extends GetView<C_ReservationController> {
ReservationPage({super.key}); ReservationPage({super.key});
bool init = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -21,10 +21,7 @@ class ReservationPage extends GetView<C_ReservationController> {
init: C_ReservationController(), init: C_ReservationController(),
id: 'reservation', id: 'reservation',
builder: (_) { builder: (_) {
if (!init) {
_setupListener(context);
init = true;
}
return Scaffold( return Scaffold(
backgroundColor: Colors.grey[100], backgroundColor: Colors.grey[100],
body: GestureDetector( body: GestureDetector(
@@ -335,20 +332,7 @@ class ReservationPage extends GetView<C_ReservationController> {
); );
} }
void _setupListener(BuildContext context) {
ever(controller.shouldShowReservationList, (bool shouldShow) {
if (shouldShow) {
Get.bottomSheet(
const ReservationListBottomSheet(),
isScrollControlled: true, // 允许弹窗使用更多屏幕高度
backgroundColor: Colors.transparent,
);
// 重要:显示后立即将信号重置为 false防止不必要的重复弹出
controller.shouldShowReservationList.value = false;
}
});
}
// 表单中的可点击行 (用于日期和时间选择) // 表单中的可点击行 (用于日期和时间选择)
Widget _buildPickerRow({ Widget _buildPickerRow({

View File

@@ -24,6 +24,8 @@ class QrCodeController extends GetxController
final RxBool isFlashOn = false.obs; final RxBool isFlashOn = false.obs;
final RxBool isProcessingResult = false.obs; final RxBool isProcessingResult = false.obs;
final RxBool hasPermission = false.obs;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@@ -47,7 +49,6 @@ class QrCodeController extends GetxController
isProcessingResult.value = true; isProcessingResult.value = true;
scannerController.stop(); scannerController.stop();
animationController.stop(); animationController.stop();
print("相机识别到的内容: ${barcode.rawValue!}");
renderResult(barcode.rawValue!); renderResult(barcode.rawValue!);
} }
} }
@@ -57,10 +58,10 @@ class QrCodeController extends GetxController
isProcessingResult.value = false; isProcessingResult.value = false;
try { try {
scannerController.start(); scannerController.start();
animationController.repeat(reverse: false);
} catch (e) { } catch (e) {
print("无法重启相机: $e"); print("无法重启相机: $e");
} }
animationController.repeat(reverse: false);
} }
/// 从相册选择图片并扫描二维码 /// 从相册选择图片并扫描二维码
@@ -120,12 +121,21 @@ class QrCodeController extends GetxController
/// 请求相机权限 /// 请求相机权限
void requestPermission() async { void requestPermission() async {
var status = await Permission.camera.request(); var status = await Permission.camera.request();
hasPermission.value = status.isGranted;
if (!status.isGranted) { if (!status.isGranted) {
if (status.isPermanentlyDenied) {
showErrorToast('相机权限已被永久拒绝,请到系统设置中开启');
// 延迟一会再引导用户去设置
Future.delayed(const Duration(seconds: 2), () => openAppSettings());
} else {
showErrorToast('请授予相机权限以使用扫描功能'); showErrorToast('请授予相机权限以使用扫描功能');
Get.back();
} }
} }
}
void requestPhotoPermission() async { void requestPhotoPermission() async {
var status = await Permission.photos.request(); var status = await Permission.photos.request();
if (status.isGranted) { if (status.isGranted) {

View File

@@ -29,10 +29,26 @@ class QrCodePage extends GetView<QrCodeController> {
), ),
], ],
), ),
body: Stack( body: Obx(() { // 1. 使用 Obx 包裹整个 body
// 根据权限状态来决定显示什么
if (controller.hasPermission.value) {
// 如果有权限,显示扫描器
return _buildScannerView(context);
} else {
// 如果没有权限,显示引导界面
return _buildPermissionDeniedView();
}
}),
);
}
Widget _buildScannerView(BuildContext context){
if (!controller.animationController.isAnimating) {
controller.animationController.repeat(reverse: false);
}
return Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
// 1. 使用 MobileScanner 作为扫描视图 // 使用 MobileScanner 作为扫描视图
MobileScanner( MobileScanner(
controller: controller.scannerController, controller: controller.scannerController,
onDetect: controller.onDetect, onDetect: controller.onDetect,
@@ -51,6 +67,40 @@ class QrCodePage extends GetView<QrCodeController> {
// 底部的功能按钮 // 底部的功能按钮
Positioned(bottom: 80, left: 0, right: 0, child: _buildActionButtons()), Positioned(bottom: 80, left: 0, right: 0, child: _buildActionButtons()),
], ],
);
}
Widget _buildPermissionDeniedView() {
// 确保动画是停止的
if (controller.animationController.isAnimating) {
controller.animationController.stop();
}
return Container(
color: Colors.black,
alignment: Alignment.center,
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.no_photography, color: Colors.white70, size: 64),
const SizedBox(height: 16),
const Text(
'需要相机权限',
style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const Text(
'请授予相机权限以使用扫码功能。',
style: TextStyle(color: Colors.white70, fontSize: 14),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: controller.requestPermission, // 点击按钮重新请求权限
child: const Text('授予权限'),
),
],
), ),
); );
} }