加氢站-查看证件

This commit is contained in:
2026-02-28 15:00:56 +08:00
parent 0df1902df2
commit b7caf58adf
6 changed files with 204 additions and 36 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 892 B

View File

@@ -1,11 +1,18 @@
import 'dart:async';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:getx_scaffold/getx_scaffold.dart';
import 'package:image_gallery_saver/image_gallery_saver.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/styles/theme.dart';
import 'package:ln_jq_app/storage_service.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
enum ReservationStatus {
@@ -46,6 +53,8 @@ class ReservationModel {
final int isTruckAttachment; // 1为有证件数据 0为缺少
final bool hasDrivingAttachment; // 是否有行驶证
final bool hasHydrogenationAttachment; // 是否有加氢证
final List<String> drivingAttachments; // 行驶证图片列表
final List<String> hydrogenationAttachments; // 加氢证图片列表
ReservationModel({
required this.id,
@@ -73,6 +82,8 @@ class ReservationModel {
required this.hasDrivingAttachment,
required this.hasHydrogenationAttachment,
required this.isEdit,
required this.drivingAttachments,
required this.hydrogenationAttachments,
});
/// 工厂构造函数用于从JSON创建ReservationModel实例
@@ -146,6 +157,8 @@ class ReservationModel {
hasDrivingAttachment: drivingList.isNotEmpty,
hasHydrogenationAttachment: hydrogenationList.isNotEmpty,
isEdit: json['isEdit']?.toString() ?? '0',
drivingAttachments: drivingList.map((e) => e.toString()).toList(),
hydrogenationAttachments: hydrogenationList.map((e) => e.toString()).toList(),
);
}
}
@@ -348,6 +361,8 @@ class SiteController extends GetxController with BaseControllerMixin {
hasDrivingAttachment: false,
hasHydrogenationAttachment: false,
isEdit: "0",
drivingAttachments: [],
hydrogenationAttachments: [],
);
} else {
item = reservationList.firstWhere(
@@ -416,9 +431,9 @@ class SiteController extends GetxController with BaseControllerMixin {
),
SizedBox(width: 16.w),
if (item.plateNumber != "---" && item.hasDrivingAttachment)
_buildInfoTag('行驶证'),
buildInfoTag('行驶证', item.drivingAttachments),
if (item.plateNumber != "---" && item.hasHydrogenationAttachment)
_buildInfoTag('加氢证'),
buildInfoTag('加氢证', item.hydrogenationAttachments),
],
),
@@ -590,7 +605,7 @@ class SiteController extends GetxController with BaseControllerMixin {
1,
addHydAmount,
"",
item,
item!,
gunNumber: selectedGun.value,
plateNumber: item.plateNumber,
isEdit: true,
@@ -600,7 +615,7 @@ class SiteController extends GetxController with BaseControllerMixin {
}
//订单确认
if (!isEdit &&
(item.plateNumber == "---" ||
(item!.plateNumber == "---" ||
item.isTruckAttachment == 0) &&
!isOfflineChecked.value) {
showToast("车辆未上传加氢证 , 请确保线下登记后点击确认");
@@ -663,14 +678,14 @@ class SiteController extends GetxController with BaseControllerMixin {
child: OutlinedButton(
onPressed: () {
Get.back();
if (!isEdit) {
if (!isEdit && !isAdd) {
upDataService(
id,
0,
2,
0,
"",
item,
item!,
gunNumber: selectedGun.value,
plateNumber: item.plateNumber,
);
@@ -684,7 +699,7 @@ class SiteController extends GetxController with BaseControllerMixin {
),
),
child: Text(
isEdit ? '取消' : '未加氢',
isEdit || isAdd ? '取消' : '未加氢',
style: const TextStyle(color: Colors.grey, fontSize: 16),
),
),
@@ -725,8 +740,58 @@ class SiteController extends GetxController with BaseControllerMixin {
);
}
Widget _buildInfoTag(String label) {
return Container(
/// 保存图片到相册
Future<void> saveImageToLocal(String url) async {
try {
// 1. 权限请求
if (Platform.isAndroid) {
var status = await Permission.storage.request();
if (!status.isGranted) {
showErrorToast("请在系统设置中开启存储权限");
return;
}
} else {
var status = await Permission.photos.request();
if (!status.isGranted) {
showErrorToast("请在系统设置中开启相册权限");
return;
}
}
showLoading("正在保存...");
// 2. 下载图片
var response = await Dio().get(
url,
options: Options(responseType: ResponseType.bytes),
);
// 3. 保存到相册
final result = await ImageGallerySaver.saveImage(
Uint8List.fromList(response.data),
quality: 100,
name: "certificate_${DateTime.now().millisecondsSinceEpoch}",
);
dismissLoading();
if (result != null && result['isSuccess'] == true) {
showSuccessToast("图片已保存至相册");
} else {
showErrorToast("保存失败");
}
} catch (e) {
dismissLoading();
showErrorToast("保存异常");
}
}
Widget buildInfoTag(String label, List<String> images) {
return GestureDetector(
onTap: () {
showImagePreview(images);
},
child: Container(
margin: const EdgeInsets.only(left: 4),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
@@ -737,6 +802,109 @@ class SiteController extends GetxController with BaseControllerMixin {
label,
style: TextStyle(color: Color(0xFF999999), fontSize: 11.sp),
),
),
);
}
/// 显示图片预览弹窗
void showImagePreview(List<String> images) {
if (images.isEmpty) return;
final RxInt currentIndex = 0.obs;
final PageController pageController = PageController();
Get.dialog(
GestureDetector(
onTap: () => Get.back(),
child: Stack(
alignment: Alignment.center,
children: [
Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 图片翻页
SizedBox(
height: Get.height * 0.5,
child: PhotoViewGallery.builder(
scrollPhysics: const BouncingScrollPhysics(),
builder: (BuildContext context, int index) {
return PhotoViewGalleryPageOptions(
imageProvider: NetworkImage(images[index]),
initialScale: PhotoViewComputedScale.contained,
heroAttributes: PhotoViewHeroAttributes(tag: images[index]),
onTapDown: (context, details, controllerValue) {
_showSaveImageMenu(images[index]);
},
);
},
itemCount: images.length,
loadingBuilder: (context, event) => const Center(
child: CircularProgressIndicator(color: Colors.white),
),
backgroundDecoration: const BoxDecoration(
color: Colors.transparent,
),
pageController: pageController,
onPageChanged: (index) => currentIndex.value = index,
),
),
SizedBox(height: 10.h),
// 页码指示器
Center(
child: Text(
"${currentIndex.value + 1} / ${images.length}",
style: const TextStyle(
color: Colors.white,
fontSize: 16,
decoration: TextDecoration.none,
),
),
),
],
),
),
// 关闭按钮
Positioned(
top: 150.h,
right: 20,
child: IconButton(
icon: const Icon(Icons.close, color: Colors.white, size: 30),
onPressed: () => Get.back(),
),
),
],
),
),
useSafeArea: false,
);
}
void _showSaveImageMenu(String url) {
Get.bottomSheet(
Container(
color: Colors.white,
child: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.download),
title: const Text('保存图片到相册'),
onTap: () {
Get.back();
saveImageToLocal(url);
},
),
const Divider(height: 1),
ListTile(
title: const Text('取消', textAlign: TextAlign.center),
onTap: () => Get.back(),
),
],
),
),
),
);
}

View File

@@ -550,10 +550,11 @@ class SitePage extends GetView<SiteController> {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (item.hasDrivingAttachment) _buildInfoTag('行驶证'),
if (item.hasDrivingAttachment)
controller.buildInfoTag('行驶证',item.drivingAttachments),
if (item.hasHydrogenationAttachment) ...[
SizedBox(width: 8.w),
_buildInfoTag('加氢证'),
controller.buildInfoTag('加氢证',item.hydrogenationAttachments)
],
Spacer(),
if (item.isEdit == "1") ...[
@@ -602,19 +603,7 @@ class SitePage extends GetView<SiteController> {
);
}
Widget _buildInfoTag(String label) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: const Color.fromRGBO(242, 243, 245, 1),
borderRadius: BorderRadius.circular(8),
),
child: Text(
label,
style: TextStyle(color: Color.fromRGBO(78, 89, 105, 1), fontSize: 11.sp),
),
);
}
Widget _buildSmallButton(
String text, {

View File

@@ -227,6 +227,8 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
hasHydrogenationAttachment: true,
hasDrivingAttachment: true,
isEdit: '',
drivingAttachments: [],
hydrogenationAttachments: [],
);
//打开预约列表

View File

@@ -589,6 +589,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.7.2"
image_gallery_saver:
dependency: "direct main"
description:
name: image_gallery_saver
sha256: "0aba74216a4d9b0561510cb968015d56b701ba1bd94aace26aacdd8ae5761816"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.3"
image_picker:
dependency: "direct main"
description:

View File

@@ -16,12 +16,12 @@ 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
# 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.
version: 1.2.3+6
version: 1.2.2+5
environment:
sdk: ^3.9.0
# Dependencies specify other packages that your package needs in order to work.
# Dependencies specify other packages tha。。。t your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
@@ -53,6 +53,7 @@ dependencies:
aliyun_push_flutter: ^1.3.6
pull_to_refresh: ^2.0.0
flutter_app_update: ^3.2.2
image_gallery_saver: ^2.0.3
dev_dependencies: