diff --git a/ln_jq_app/assets/html/map.html b/ln_jq_app/assets/html/map.html
index 5e71f6b..7e79fb2 100644
--- a/ln_jq_app/assets/html/map.html
+++ b/ln_jq_app/assets/html/map.html
@@ -131,13 +131,13 @@
diff --git a/ln_jq_app/lib/pages/b_page/site/controller.dart b/ln_jq_app/lib/pages/b_page/site/controller.dart
index 29448e5..ca92f57 100644
--- a/ln_jq_app/lib/pages/b_page/site/controller.dart
+++ b/ln_jq_app/lib/pages/b_page/site/controller.dart
@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:getx_scaffold/getx_scaffold.dart';
import 'package:ln_jq_app/common/model/base_model.dart';
import 'package:ln_jq_app/common/styles/theme.dart';
@@ -277,9 +278,10 @@ class SiteController extends GetxController with BaseControllerMixin {
child: TextField(
controller: amountController,
textAlign: TextAlign.center,
- keyboardType: const TextInputType.numberWithOptions(
- decimal: true,
- ),
+ keyboardType: TextInputType.number,
+ inputFormatters: [
+ FilteringTextInputFormatter.digitsOnly, // 只允许数字输入
+ ],
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
diff --git a/ln_jq_app/lib/pages/c_page/reservation/controller.dart b/ln_jq_app/lib/pages/c_page/reservation/controller.dart
index 9cb2d5f..c493200 100644
--- a/ln_jq_app/lib/pages/c_page/reservation/controller.dart
+++ b/ln_jq_app/lib/pages/c_page/reservation/controller.dart
@@ -17,6 +17,7 @@ import 'package:ln_jq_app/pages/qr_code/view.dart';
import 'package:ln_jq_app/storage_service.dart';
import '../../../common/styles/theme.dart';
+import 'reservation_list_bottomsheet.dart';
/// Helper class for managing time slots
class TimeSlot {
@@ -547,6 +548,10 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
num maxHydrogen = 0;
String difference = "";
+ //用来管理查看预约的弹窗
+ Worker? _sheetWorker;
+ bool init = false;
+
@override
bool get listenLifecycleEvent => true;
@@ -555,6 +560,32 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
super.onInit();
getUserBindCarInfo();
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() {
@@ -628,6 +659,11 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
final leftHydrogenNum = double.tryParse(leftHydrogen) ?? 0.0;
difference = (maxHydrogen - leftHydrogenNum).toStringAsFixed(2);
+ int flooredDifference = (maxHydrogen - leftHydrogenNum).floor();
+ if (flooredDifference > 0) {
+ amountController.text = flooredDifference.toString();
+ }
+
updateUi();
} catch (e) {
} finally {
diff --git a/ln_jq_app/lib/pages/c_page/reservation/view.dart b/ln_jq_app/lib/pages/c_page/reservation/view.dart
index 0d95a51..9179e88 100644
--- a/ln_jq_app/lib/pages/c_page/reservation/view.dart
+++ b/ln_jq_app/lib/pages/c_page/reservation/view.dart
@@ -13,7 +13,7 @@ import 'reservation_list_bottomsheet.dart';
class ReservationPage extends GetView {
ReservationPage({super.key});
- bool init = false;
+
@override
Widget build(BuildContext context) {
@@ -21,10 +21,7 @@ class ReservationPage extends GetView {
init: C_ReservationController(),
id: 'reservation',
builder: (_) {
- if (!init) {
- _setupListener(context);
- init = true;
- }
+
return Scaffold(
backgroundColor: Colors.grey[100],
body: GestureDetector(
@@ -335,20 +332,7 @@ class ReservationPage extends GetView {
);
}
- 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({
diff --git a/ln_jq_app/lib/pages/qr_code/controller.dart b/ln_jq_app/lib/pages/qr_code/controller.dart
index f43f9ac..b7ea3ff 100644
--- a/ln_jq_app/lib/pages/qr_code/controller.dart
+++ b/ln_jq_app/lib/pages/qr_code/controller.dart
@@ -24,6 +24,8 @@ class QrCodeController extends GetxController
final RxBool isFlashOn = false.obs;
final RxBool isProcessingResult = false.obs;
+ final RxBool hasPermission = false.obs;
+
@override
void onInit() {
super.onInit();
@@ -47,7 +49,6 @@ class QrCodeController extends GetxController
isProcessingResult.value = true;
scannerController.stop();
animationController.stop();
- print("相机识别到的内容: ${barcode.rawValue!}");
renderResult(barcode.rawValue!);
}
}
@@ -57,10 +58,10 @@ class QrCodeController extends GetxController
isProcessingResult.value = false;
try {
scannerController.start();
+ animationController.repeat(reverse: false);
} catch (e) {
print("无法重启相机: $e");
}
- animationController.repeat(reverse: false);
}
/// 从相册选择图片并扫描二维码
@@ -120,10 +121,19 @@ class QrCodeController extends GetxController
/// 请求相机权限
void requestPermission() async {
var status = await Permission.camera.request();
+
+ hasPermission.value = status.isGranted;
+
if (!status.isGranted) {
- showErrorToast('请授予相机权限以使用扫描功能');
- Get.back();
+ if (status.isPermanentlyDenied) {
+ showErrorToast('相机权限已被永久拒绝,请到系统设置中开启');
+ // 延迟一会再引导用户去设置
+ Future.delayed(const Duration(seconds: 2), () => openAppSettings());
+ } else {
+ showErrorToast('请授予相机权限以使用扫描功能');
+ }
}
+
}
void requestPhotoPermission() async {
diff --git a/ln_jq_app/lib/pages/qr_code/view.dart b/ln_jq_app/lib/pages/qr_code/view.dart
index 2fe4d59..1dd37d0 100644
--- a/ln_jq_app/lib/pages/qr_code/view.dart
+++ b/ln_jq_app/lib/pages/qr_code/view.dart
@@ -29,27 +29,77 @@ class QrCodePage extends GetView {
),
],
),
- body: Stack(
- alignment: Alignment.center,
- children: [
- // 1. 使用 MobileScanner 作为扫描视图
- MobileScanner(
- controller: controller.scannerController,
- onDetect: controller.onDetect,
- // 您可以自定义扫描框的样式
- scanWindow: Rect.fromCenter(
- center: Offset(
- MediaQuery.of(context).size.width / 2,
- MediaQuery.of(context).size.height / 2 - 50,
- ),
- width: 250,
- height: 250,
+ 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,
+ children: [
+ // 使用 MobileScanner 作为扫描视图
+ MobileScanner(
+ controller: controller.scannerController,
+ onDetect: controller.onDetect,
+ // 您可以自定义扫描框的样式
+ scanWindow: Rect.fromCenter(
+ center: Offset(
+ MediaQuery.of(context).size.width / 2,
+ MediaQuery.of(context).size.height / 2 - 50,
),
+ width: 250,
+ height: 250,
+ ),
+ ),
+ // 扫描动画和覆盖层
+ _buildScannerOverlay(context),
+ // 底部的功能按钮
+ 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('授予权限'),
),
- // 扫描动画和覆盖层
- _buildScannerOverlay(context),
- // 底部的功能按钮
- Positioned(bottom: 80, left: 0, right: 0, child: _buildActionButtons()),
],
),
);