显示证件照 pdf待测试
This commit is contained in:
@@ -0,0 +1,67 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
|
class AttachmentViewerController extends GetxController {
|
||||||
|
late final String url;
|
||||||
|
late final String fileType;
|
||||||
|
|
||||||
|
final RxBool isLoading = true.obs;
|
||||||
|
// This is the correct state variable: it stores the local file path.
|
||||||
|
final RxString localFilePath = ''.obs;
|
||||||
|
final RxString loadingText = '加载中...'.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
url = Get.arguments['url'] ?? '';
|
||||||
|
if (url.isEmpty) {
|
||||||
|
showErrorToast('无效的附件链接');
|
||||||
|
Get.back();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.toLowerCase().endsWith('.pdf')) {
|
||||||
|
fileType = 'pdf';
|
||||||
|
// This is the correct logic: download the file first.
|
||||||
|
_downloadPdf();
|
||||||
|
} else {
|
||||||
|
fileType = 'image';
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downloads the PDF file to a temporary directory and stores its path.
|
||||||
|
Future<void> _downloadPdf() async {
|
||||||
|
try {
|
||||||
|
final dio = Dio();
|
||||||
|
final tempDir = await getTemporaryDirectory();
|
||||||
|
// Use a unique name to avoid caching issues
|
||||||
|
final fileName = '${DateTime.now().millisecondsSinceEpoch}_${url.split('/').last}';
|
||||||
|
final savePath = '${tempDir.path}/$fileName';
|
||||||
|
|
||||||
|
await dio.download(
|
||||||
|
url,
|
||||||
|
savePath,
|
||||||
|
onReceiveProgress: (received, total) {
|
||||||
|
if (total != -1) {
|
||||||
|
loadingText.value = '下载中...${(received / total * 100).toStringAsFixed(0)}%';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// On success, update the local file path
|
||||||
|
localFilePath.value = savePath;
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
showErrorToast('PDF文件加载失败,请检查网络或文件链接');
|
||||||
|
print('PDF Download Error: $e');
|
||||||
|
Get.back();
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_pdfview/flutter_pdfview.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
|
import 'package:photo_view/photo_view.dart';
|
||||||
|
|
||||||
|
import 'attachment_viewer_controller.dart';
|
||||||
|
|
||||||
|
class AttachmentViewerPage extends GetView<AttachmentViewerController> {
|
||||||
|
const AttachmentViewerPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Get.put(AttachmentViewerController());
|
||||||
|
final fileName = controller.url.split('/').last;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(
|
||||||
|
fileName,
|
||||||
|
style: const TextStyle(fontSize: 16),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: Obx(() {
|
||||||
|
if (controller.isLoading.value) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const CircularProgressIndicator(),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Text(controller.loadingText.value),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controller.fileType == 'pdf') {
|
||||||
|
if (controller.localFilePath.isNotEmpty) {
|
||||||
|
return PDFView(
|
||||||
|
key: ValueKey(controller.localFilePath.value),
|
||||||
|
filePath: controller.localFilePath.value,
|
||||||
|
enableSwipe: true,
|
||||||
|
swipeHorizontal: false,
|
||||||
|
autoSpacing: false,
|
||||||
|
pageFling: true,
|
||||||
|
onRender: (pages) {
|
||||||
|
print("PDF rendered with $pages pages.");
|
||||||
|
},
|
||||||
|
onError: (error) {
|
||||||
|
print(error.toString());
|
||||||
|
showErrorToast('渲染PDF时出错');
|
||||||
|
},
|
||||||
|
onPageError: (page, error) {
|
||||||
|
print('$page: ${error.toString()}');
|
||||||
|
showErrorToast('渲染第$page页时出错');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(child: Text('无法加载PDF文件'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return PhotoView(
|
||||||
|
imageProvider: NetworkImage(controller.url),
|
||||||
|
minScale: PhotoViewComputedScale.contained * 0.8,
|
||||||
|
maxScale: PhotoViewComputedScale.covered * 2.0,
|
||||||
|
loadingBuilder: (context, event) => const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
backgroundDecoration: const BoxDecoration(
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
|
|
||||||
|
import 'attachment_viewer_page.dart';
|
||||||
|
|
||||||
|
class CertificateViewerController extends GetxController {
|
||||||
|
late final String title;
|
||||||
|
late final List<String> attachments;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
// 从 Get.to 的 arguments 中获取标题和附件列表
|
||||||
|
title = Get.arguments['title'] ?? '证件详情';
|
||||||
|
attachments = List<String>.from(Get.arguments['attachments'] ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 导航到通用的附件查看器页面
|
||||||
|
void openAttachment(String url) {
|
||||||
|
if (url.isEmpty) {
|
||||||
|
showErrorToast('附件链接无效');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Get.to(
|
||||||
|
() => const AttachmentViewerPage(),
|
||||||
|
arguments: {
|
||||||
|
'url': url,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查 URL 是否为 PDF,以便在视图中显示不同的图标
|
||||||
|
bool isPdf(String url) {
|
||||||
|
return url.toLowerCase().endsWith('.pdf');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'certificate_viewer_controller.dart';
|
||||||
|
|
||||||
|
class CertificateViewerPage extends GetView<CertificateViewerController> {
|
||||||
|
const CertificateViewerPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Get.put(CertificateViewerController());
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(controller.title),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: ListView.builder(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
|
||||||
|
itemCount: controller.attachments.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final url = controller.attachments[index];
|
||||||
|
// 从 URL 中提取文件名用于显示
|
||||||
|
final fileName = url.split('/').last;
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
elevation: 2,
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(
|
||||||
|
controller.isPdf(url)
|
||||||
|
? Icons.picture_as_pdf_rounded // PDF 图标
|
||||||
|
: Icons.image_rounded, // 图片图标
|
||||||
|
color: controller.isPdf(url) ? Colors.red.shade700 : Colors.blue.shade700,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
fileName,
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
trailing: const Icon(Icons.arrow_forward_ios_rounded, size: 16, color: Colors.grey),
|
||||||
|
onTap: () => controller.openAttachment(url),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,72 +1,85 @@
|
|||||||
|
import 'package:get/get.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/model/vehicle_info.dart';
|
import 'package:ln_jq_app/common/model/vehicle_info.dart';
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
|
|
||||||
|
import 'certificate_viewer_page.dart';
|
||||||
|
|
||||||
class CarInfoController extends GetxController with BaseControllerMixin {
|
class CarInfoController extends GetxController with BaseControllerMixin {
|
||||||
@override
|
@override
|
||||||
String get builderId => 'car_info';
|
String get builderId => 'car_info';
|
||||||
|
|
||||||
CarInfoController();
|
// --- 车辆基本信息 ---
|
||||||
|
|
||||||
String plateNumber = "";
|
String plateNumber = "";
|
||||||
String vin = "未知";
|
String vin = "未知";
|
||||||
String modelName = "未知";
|
String modelName = "未知";
|
||||||
String brandName = "未知";
|
String brandName = "未知";
|
||||||
|
|
||||||
@override
|
// --- 证件附件列表 ---
|
||||||
void onInit() {
|
final RxList<String> drivingAttachments = <String>[].obs;
|
||||||
getUserBindCarInfo();
|
final RxList<String> operationAttachments = <String>[].obs;
|
||||||
super.onInit();
|
final RxList<String> hydrogenationAttachments = <String>[].obs;
|
||||||
}
|
final RxList<String> registerAttachments = <String>[].obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onInit() {
|
||||||
super.onClose();
|
super.onInit();
|
||||||
|
getUserBindCarInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
void getUserBindCarInfo() async {
|
void getUserBindCarInfo() async {
|
||||||
if (StorageService.to.hasVehicleInfo) {
|
if (StorageService.to.hasVehicleInfo) {
|
||||||
VehicleInfo? bean = StorageService.to.vehicleInfo;
|
VehicleInfo? bean = StorageService.to.vehicleInfo;
|
||||||
if (bean == null) {
|
if (bean == null) return;
|
||||||
return;
|
|
||||||
}
|
// 填充基本信息
|
||||||
plateNumber = bean.plateNumber;
|
plateNumber = bean.plateNumber;
|
||||||
vin = bean.vin;
|
vin = bean.vin;
|
||||||
modelName = bean.modelName;
|
modelName = bean.modelName;
|
||||||
brandName = bean.brandName;
|
brandName = bean.brandName;
|
||||||
|
|
||||||
|
// 获取证件信息
|
||||||
final response = await HttpService.to.get(
|
final response = await HttpService.to.get(
|
||||||
'appointment/vehicle/getPicInfoByVin?vin=${vin}',
|
'appointment/vehicle/getPicInfoByVin?vin=$vin',
|
||||||
);
|
);
|
||||||
if (response != null) {
|
|
||||||
|
if (response != null && response.data != null) {
|
||||||
final result = BaseModel.fromJson(response.data);
|
final result = BaseModel.fromJson(response.data);
|
||||||
if (result.code == 0 && result.data != null) {
|
if (result.code == 0 && result.data != null) {
|
||||||
result.data;
|
final data = result.data as Map<String, dynamic>;
|
||||||
/*{
|
|
||||||
"plateNumber": "浙F08860F",
|
List<String> parseAttachments(dynamic list) {
|
||||||
"vin": "LA9GG64L9NBAF4174",
|
if (list is List) {
|
||||||
"drivingAttachment": [
|
// 确保列表中的每一项都是字符串
|
||||||
"https://lnh2e.com/api/lingniu-export-v1/v1/resource/file/2024/05/202405162107520002.pdf",
|
return List<String>.from(list.map((item) => item.toString()));
|
||||||
"https://lnh2e.com/api/lingniu-export-v1/v1/resource/file/2024/05/202405162107530002.jpg",
|
}
|
||||||
"https://lnh2e.com/api/lingniu-export-v1/v1/resource/file/2024/12/202412101043260001.jpg"
|
return [];
|
||||||
],
|
}
|
||||||
"operationAttachment": [
|
|
||||||
"https://lnh2e.com/api/lingniu-export-v1/v1/resource/file/2024/05/202405162107530003.pdf",
|
// 将解析出的 URL 列表赋值给对应的 RxList
|
||||||
"https://lnh2e.com/api/lingniu-export-v1/v1/resource/file/2024/12/202412101043460001.jpg",
|
drivingAttachments.assignAll(parseAttachments(data['drivingAttachment']));
|
||||||
"https://lnh2e.com/api/lingniu-export-v1/v1/resource/file/2024/12/202412181551190001.pdf"
|
operationAttachments.assignAll(parseAttachments(data['operationAttachment']));
|
||||||
],
|
hydrogenationAttachments.assignAll(parseAttachments(data['hydrogenationAttachment']));
|
||||||
"hydrogenationAttachment": [
|
registerAttachments.assignAll(parseAttachments(data['registerAttachment']));
|
||||||
"https://lnh2e.com/api/lingniu-export-v1/v1/resource/file/2024/05/202405162107570001.pdf"
|
|
||||||
],
|
|
||||||
"registerAttachment": [
|
|
||||||
"https://lnh2e.com/api/lingniu-export-v1/v1/resource/file/2024/05/202405162108010001.pdf"
|
|
||||||
]
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUi();
|
updateUi();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 跳转到证件查看页面
|
||||||
|
void navigateToCertificateViewer(String title, List<String> attachments) {
|
||||||
|
if (attachments.isEmpty) {
|
||||||
|
showToast('暂无相关证件附件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Get.to(
|
||||||
|
() => const CertificateViewerPage(),
|
||||||
|
arguments: {
|
||||||
|
'title': title,
|
||||||
|
'attachments': attachments,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
import 'package:ln_jq_app/common/styles/theme.dart';
|
import 'package:ln_jq_app/common/styles/theme.dart';
|
||||||
import 'package:ln_jq_app/pages/qr_code/view.dart';
|
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 'package:getx_scaffold/getx_scaffold.dart'; // 如果不使用其中的扩展,可以注释掉
|
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
|
||||||
@@ -17,10 +15,8 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
init: CarInfoController(),
|
init: CarInfoController(),
|
||||||
id: 'car_info',
|
id: 'car_info',
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
// 将所有 UI 构建逻辑都放在这里
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.grey[100],
|
backgroundColor: Colors.grey[100],
|
||||||
// 我们不再使用单独的 AppBar,而是通过自定义的 Container 来实现类似效果
|
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
@@ -43,7 +39,7 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 1. 构建顶部的司机信息卡片(包含蓝色背景)
|
/// 构建顶部的司机信息卡片
|
||||||
Widget _buildDriverInfoCard() {
|
Widget _buildDriverInfoCard() {
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
@@ -65,13 +61,13 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"${StorageService.to.name}",
|
"${StorageService.to.name}",
|
||||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
"${StorageService.to.phone}",
|
"${StorageService.to.phone}",
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 11),
|
style: TextStyle(color: Colors.grey, fontSize: 11),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -137,7 +133,7 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 2. 构建车辆绑定信息卡片
|
/// 构建车辆绑定信息卡片
|
||||||
Widget _buildCarBindingCard() {
|
Widget _buildCarBindingCard() {
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
@@ -176,46 +172,46 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
isButton
|
isButton
|
||||||
? GestureDetector(
|
? GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
//判断是否绑定成功
|
//判断是否绑定成功
|
||||||
var scanResult = await Get.to(() => const QrCodePage());
|
var scanResult = await Get.to(() => const QrCodePage());
|
||||||
if (scanResult == true) {
|
if (scanResult == true) {
|
||||||
controller.getUserBindCarInfo();
|
controller.getUserBindCarInfo();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: EdgeInsetsGeometry.only(left: 10.w),
|
margin: EdgeInsetsGeometry.only(left: 10.w),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(color: Colors.blue.shade300, width: 1),
|
border: Border.all(color: Colors.blue.shade300, width: 1),
|
||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(5),
|
||||||
color: Colors.blue.withOpacity(0.05),
|
color: Colors.blue.withOpacity(0.05),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min, // Keep the row compact
|
mainAxisSize: MainAxisSize.min, // Keep the row compact
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
StorageService.to.hasVehicleInfo ? Icons.repeat : Icons.search,
|
StorageService.to.hasVehicleInfo ? Icons.repeat : Icons.search,
|
||||||
size: 13,
|
size: 13,
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 3),
|
const SizedBox(width: 3),
|
||||||
Text(
|
Text(
|
||||||
StorageService.to.hasVehicleInfo ? "换车牌" : value,
|
StorageService.to.hasVehicleInfo ? "换车牌" : value,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
)
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Text(
|
: Text(
|
||||||
value,
|
value,
|
||||||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
|
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -226,30 +222,44 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
elevation: 2,
|
elevation: 2,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
_buildCertificateRow(
|
||||||
'车辆证件',
|
icon: Icons.credit_card_rounded,
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
title: '行驶证',
|
||||||
|
attachments: controller.drivingAttachments,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
|
||||||
_buildCertificateRow(Icons.local_gas_station, '驾驶证', '车辆驾驶证相关证件'),
|
|
||||||
const Divider(),
|
const Divider(),
|
||||||
_buildCertificateRow(Icons.article_outlined, '营运证', '道路运输经营许可证'),
|
_buildCertificateRow(
|
||||||
|
icon: Icons.article_rounded,
|
||||||
|
title: '运营证',
|
||||||
|
attachments: controller.operationAttachments,
|
||||||
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
_buildCertificateRow(Icons.person_pin_outlined, '加氢证', '车辆加氢许可证'),
|
_buildCertificateRow(
|
||||||
|
icon: Icons.propane_tank_rounded,
|
||||||
|
title: '氢瓶证',
|
||||||
|
attachments: controller.hydrogenationAttachments,
|
||||||
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
_buildCertificateRow(Icons.credit_card_outlined, '行驶证', '车辆行驶证相关证件'),
|
_buildCertificateRow(
|
||||||
|
icon: Icons.app_registration_rounded,
|
||||||
|
title: '登记证',
|
||||||
|
attachments: controller.registerAttachments,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 车辆证件列表项
|
/// 证件展示
|
||||||
Widget _buildCertificateRow(IconData icon, String title, String subtitle) {
|
Widget _buildCertificateRow({
|
||||||
|
required IconData icon,
|
||||||
|
required String title,
|
||||||
|
required List<String> attachments,
|
||||||
|
}) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
@@ -257,24 +267,30 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
backgroundColor: Colors.blue.withOpacity(0.1),
|
backgroundColor: Colors.blue.withOpacity(0.1),
|
||||||
child: Icon(icon, color: Colors.blue, size: 28),
|
child: Icon(icon, color: Colors.blue, size: 28),
|
||||||
),
|
),
|
||||||
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold,fontSize: 14)),
|
title: Text(
|
||||||
subtitle: Text(subtitle, style: const TextStyle(color: Colors.grey, fontSize: 12)),
|
title,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||||
|
),
|
||||||
|
// 使用 Obx 响应式地显示附件数量
|
||||||
|
subtitle: Obx(
|
||||||
|
() => Text(
|
||||||
|
'共 ${attachments.length} 个附件',
|
||||||
|
style: const TextStyle(color: Colors.grey, fontSize: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
trailing: Container(
|
trailing: Container(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.grey[200],
|
color: Colors.grey[200],
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
// 图片中的图标是“查看”的意思,这里用一个类似的图标代替
|
|
||||||
child: const Icon(Icons.find_in_page_outlined, color: AppTheme.themeColor),
|
child: const Icon(Icons.find_in_page_outlined, color: AppTheme.themeColor),
|
||||||
),
|
),
|
||||||
onTap: () {
|
// 更新 onTap 逻辑
|
||||||
// TODO: 查看证件详情逻辑
|
onTap: () => controller.navigateToCertificateViewer(title, attachments),
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 4. 构建提示信息卡片
|
|
||||||
Widget _buildTipsCard() {
|
Widget _buildTipsCard() {
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
|
|||||||
@@ -323,6 +323,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.7"
|
version: "2.4.7"
|
||||||
|
flutter_pdfview:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_pdfview
|
||||||
|
sha256: a9055bf920c7095bf08c2781db431ba23577aa5da5a056a7152dc89a18fbec6f
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.2"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -733,6 +741,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.1"
|
||||||
|
photo_view:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: photo_view
|
||||||
|
sha256: "1fc3d970a91295fbd1364296575f854c9863f225505c28c46e0a03e48960c75e"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ dependencies:
|
|||||||
image_picker: ^1.2.1 # 用于从相册选择图片
|
image_picker: ^1.2.1 # 用于从相册选择图片
|
||||||
image: ^4.5.4
|
image: ^4.5.4
|
||||||
zxing_lib: ^1.1.4
|
zxing_lib: ^1.1.4
|
||||||
|
flutter_pdfview: 1.3.2 #显示pdf
|
||||||
|
photo_view: ^0.15.0 #操作图片
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user