车辆信息 ui
This commit is contained in:
@@ -12,12 +12,12 @@ class AttachmentViewerPage extends GetView<AttachmentViewerController> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Get.put(AttachmentViewerController());
|
Get.put(AttachmentViewerController());
|
||||||
final fileName = controller.url.split('/').last;
|
// final fileName = controller.url.split('/').last;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
fileName,
|
"证件详情",
|
||||||
style: const TextStyle(fontSize: 16),
|
style: const TextStyle(fontSize: 16),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -78,18 +78,11 @@ class CertificateViewerController extends GetxController with BaseControllerMixi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Get.to(
|
Get.to(() => const AttachmentViewerPage(), arguments: {'url': url});
|
||||||
() => const AttachmentViewerPage(),
|
|
||||||
arguments: {
|
|
||||||
'url': url,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 检查 URL 是否为 PDF (此方法保持不变)
|
/// 检查 URL 是否为 PDF (此方法保持不变)
|
||||||
bool isPdf(String url) {
|
bool isPdf(String url) {
|
||||||
return url.toLowerCase().endsWith('.pdf');
|
return url.toLowerCase().endsWith('.pdf');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ 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/pages/c_page/car_info/attachment_viewer_page.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:path_provider/path_provider.dart';
|
||||||
import 'certificate_viewer_page.dart';
|
import 'certificate_viewer_page.dart';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
class CarInfoController extends GetxController with BaseControllerMixin {
|
class CarInfoController extends GetxController with BaseControllerMixin {
|
||||||
@override
|
@override
|
||||||
@@ -117,6 +119,15 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
parseAttachments(data['hydrogenationAttachment']),
|
parseAttachments(data['hydrogenationAttachment']),
|
||||||
);
|
);
|
||||||
registerAttachments.assignAll(parseAttachments(data['registerAttachment']));
|
registerAttachments.assignAll(parseAttachments(data['registerAttachment']));
|
||||||
|
|
||||||
|
// 初始化时开始加载所有PDF
|
||||||
|
attachments = [
|
||||||
|
...drivingAttachments,
|
||||||
|
...operationAttachments,
|
||||||
|
...hydrogenationAttachments,
|
||||||
|
...registerAttachments,
|
||||||
|
];
|
||||||
|
loadAllPdfs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateUi();
|
updateUi();
|
||||||
@@ -138,4 +149,69 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
arguments: {'title': title, 'attachments': attachments},
|
arguments: {'title': title, 'attachments': 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> attachments = [];
|
||||||
|
|
||||||
|
// --- 新增: 状态管理 ---
|
||||||
|
/// 用于存储网络PDF的本地路径,key是网络url,value是本地路径
|
||||||
|
final RxMap<String, String> localPdfPaths = <String, String>{}.obs;
|
||||||
|
|
||||||
|
/// 用于跟踪每个附件的加载状态,key是网络url
|
||||||
|
final RxMap<String, bool> isLoading = <String, bool>{}.obs;
|
||||||
|
|
||||||
|
/// 遍历所有附件,如果是PDF则进行下载
|
||||||
|
void loadAllPdfs() {
|
||||||
|
for (var url in attachments) {
|
||||||
|
if (isPdf(url)) {
|
||||||
|
_downloadPdf(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 下载单个PDF文件
|
||||||
|
Future<void> _downloadPdf(String url) async {
|
||||||
|
if (url.isEmpty) return;
|
||||||
|
|
||||||
|
// 开始加载
|
||||||
|
isLoading[url] = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final dio = Dio();
|
||||||
|
final Directory tempDir = await getTemporaryDirectory();
|
||||||
|
final String savePath = '${tempDir.path}/${url.split('/').last}';
|
||||||
|
|
||||||
|
// 检查文件是否已存在,避免重复下载
|
||||||
|
if (await File(savePath).exists()) {
|
||||||
|
localPdfPaths[url] = savePath;
|
||||||
|
isLoading[url] = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await dio.download(url, savePath);
|
||||||
|
|
||||||
|
// 下载成功后,更新本地路径
|
||||||
|
localPdfPaths[url] = savePath;
|
||||||
|
} catch (e) {
|
||||||
|
print('PDF download error for $url: $e');
|
||||||
|
// 出错时也可以更新状态,以便UI显示错误提示
|
||||||
|
} finally {
|
||||||
|
// 结束加载
|
||||||
|
isLoading[url] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_pdfview/flutter_pdfview.dart';
|
||||||
import 'package:get/get.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/login_util.dart';
|
import 'package:ln_jq_app/common/login_util.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
|
import 'package:photo_view/photo_view.dart';
|
||||||
|
|
||||||
import '../../../common/styles/theme.dart';
|
import '../../../common/styles/theme.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
@@ -18,21 +20,20 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
id: 'car_info',
|
id: 'car_info',
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Color.fromRGBO(240, 244, 247, 0.4),
|
backgroundColor: const Color.fromRGBO(240, 244, 247, 0.4),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_buildUserInfoCard(),
|
_buildUserInfoCard(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: EdgeInsets.only(left: 20.w,right: 20.w),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 16),
|
||||||
_buildCarInfoCard(),
|
_buildCarInfoCard(),
|
||||||
const SizedBox(height: 5),
|
_buildCertificatesCard(context),
|
||||||
_buildCertificatesCard(),
|
const SizedBox(height: 12),
|
||||||
const SizedBox(height: 5),
|
|
||||||
_buildSafetyReminderCard(),
|
_buildSafetyReminderCard(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -60,7 +61,6 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 16, top: 40),
|
padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 16, top: 40),
|
||||||
// 增加了顶部 padding 适配状态栏
|
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Stack(
|
Stack(
|
||||||
@@ -102,15 +102,14 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
vertical: 2,
|
vertical: 2,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color.fromRGBO(236, 255, 234, 1), // 极浅绿色背景
|
color: const Color.fromRGBO(236, 255, 234, 1),
|
||||||
border: Border.all(color: const Color(0xFFB7E19F)), // 边框
|
border: Border.all(color: const Color(0xFFB7E19F)),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: const Row(
|
child: const Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.eco, size: 12, color: Color(0xFF52C41A)),
|
Icon(Icons.eco, size: 12, color: Color(0xFF52C41A)),
|
||||||
// 叶子图标
|
|
||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
"绿色先锋",
|
"绿色先锋",
|
||||||
@@ -173,7 +172,6 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统计项
|
|
||||||
Widget _buildModernStatItem(String title, String subtitle, String value, String unit) {
|
Widget _buildModernStatItem(String title, String subtitle, String value, String unit) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -216,7 +214,6 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 构建车辆信息卡片
|
|
||||||
Widget _buildCarInfoCard() {
|
Widget _buildCarInfoCard() {
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
@@ -227,13 +224,13 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_buildDetailRow('车牌号', controller.plateNumber, isPlate: true),
|
_buildDetailRow('车牌号', controller.plateNumber, isPlate: true),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 11),
|
||||||
_buildDetailRow('车架号', controller.vin),
|
_buildDetailRow('车架号', controller.vin),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 11),
|
||||||
_buildDetailRow('车辆型号', controller.modelName),
|
_buildDetailRow('车辆型号', controller.modelName),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 11),
|
||||||
_buildDetailRow('车辆品牌', controller.brandName),
|
_buildDetailRow('车辆品牌', controller.brandName),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 10),
|
||||||
_buildH2LevelProgress(),
|
_buildH2LevelProgress(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -241,7 +238,6 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 详情行:左右分布
|
|
||||||
Widget _buildDetailRow(String label, String value, {bool isPlate = false}) {
|
Widget _buildDetailRow(String label, String value, {bool isPlate = false}) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@@ -257,17 +253,21 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
margin: const EdgeInsets.only(right: 10),
|
margin: const EdgeInsets.only(right: 10),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(color: Color.fromRGBO(71, 174, 208, 1)), // 浅绿色边框
|
border: Border.all(color: const Color.fromRGBO(71, 174, 208, 1)),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
color: Color.fromRGBO(235, 250, 255, 1), // 极浅绿色背景
|
color: const Color.fromRGBO(235, 250, 255, 1),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.sync, size: 12, color: Color.fromRGBO(71, 174, 208, 1)),
|
const Icon(
|
||||||
SizedBox(width: 4),
|
Icons.sync,
|
||||||
|
size: 12,
|
||||||
|
color: Color.fromRGBO(71, 174, 208, 1),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
StorageService.to.hasVehicleInfo ? "换车牌" : "扫码绑定",
|
StorageService.to.hasVehicleInfo ? "换车牌" : "扫码绑定",
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
color: Color.fromRGBO(71, 174, 208, 1),
|
color: Color.fromRGBO(71, 174, 208, 1),
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -291,17 +291,16 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// H2 Level 进度条模块
|
|
||||||
Widget _buildH2LevelProgress() {
|
Widget _buildH2LevelProgress() {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
child: const LinearProgressIndicator(
|
child: const LinearProgressIndicator(
|
||||||
value: 0.75, // 示例值
|
value: 0.75,
|
||||||
minHeight: 8,
|
minHeight: 8,
|
||||||
backgroundColor: Color(0xFFF0F2F5),
|
backgroundColor: Color(0xFFF0F2F5),
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFF52C41A)),
|
valueColor: AlwaysStoppedAnimation<Color>(Color.fromRGBO(16, 185, 129, 1)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
@@ -313,7 +312,7 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
"75%",
|
"75%",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: Color(0xFF52C41A),
|
color: Color.fromRGBO(16, 185, 129, 1),
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -323,76 +322,195 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 3. 构建车辆证件卡片
|
/// 3. 构建车辆证件卡片 (重构为 TabView 样式)
|
||||||
Widget _buildCertificatesCard() {
|
Widget _buildCertificatesCard(BuildContext context) {
|
||||||
return Card(
|
return DefaultTabController(
|
||||||
elevation: 2,
|
length: 4,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_buildCertificateRow(
|
TabBar(
|
||||||
icon: Icons.credit_card_rounded,
|
isScrollable: false,
|
||||||
title: '行驶证',
|
indicatorColor: Color.fromRGBO(16, 185, 129, 1),
|
||||||
attachments: controller.drivingAttachments,
|
labelColor: Color.fromRGBO(16, 185, 129, 1),
|
||||||
|
unselectedLabelColor: Colors.grey,
|
||||||
|
labelStyle: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||||
|
indicatorSize: TabBarIndicatorSize.label,
|
||||||
|
tabs: const [
|
||||||
|
Tab(text: '行驶证'),
|
||||||
|
Tab(text: '营运证'),
|
||||||
|
Tab(text: '加氢资格证'),
|
||||||
|
Tab(text: '登记证'),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const Divider(),
|
const SizedBox(height: 9),
|
||||||
_buildCertificateRow(
|
SizedBox(
|
||||||
icon: Icons.article_rounded,
|
height: 343.h, // 给定一个高度,或者使用别的方式布局
|
||||||
title: '运营证',
|
child: TabBarView(
|
||||||
attachments: controller.operationAttachments,
|
children: [
|
||||||
|
_buildCertificateContent('行驶证', controller.drivingAttachments),
|
||||||
|
_buildCertificateContent('营运证', controller.operationAttachments),
|
||||||
|
_buildCertificateContent('加氢资格证', controller.hydrogenationAttachments),
|
||||||
|
_buildCertificateContent('登记证', controller.registerAttachments),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const Divider(),
|
|
||||||
_buildCertificateRow(
|
|
||||||
icon: Icons.propane_tank_rounded,
|
|
||||||
title: '加氢证',
|
|
||||||
attachments: controller.hydrogenationAttachments,
|
|
||||||
),
|
),
|
||||||
const Divider(),
|
],
|
||||||
_buildCertificateRow(
|
),
|
||||||
icon: Icons.app_registration_rounded,
|
);
|
||||||
title: '登记证',
|
}
|
||||||
attachments: controller.registerAttachments,
|
|
||||||
|
/// 构建单个证件的展示内容
|
||||||
|
Widget _buildCertificateContent(String title, RxList<String> attachments) {
|
||||||
|
return Obx(() {
|
||||||
|
if (attachments.isEmpty) {
|
||||||
|
return const Center(child: Text('暂无相关证件信息'));
|
||||||
|
}
|
||||||
|
return Card(
|
||||||
|
elevation: 0,
|
||||||
|
color: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
_buildCertDetailItem('所有人', '上海羚牛氢运物联网科技有限公司', isFull: true),
|
||||||
|
_buildCertDetailItem('车辆识别代号', controller.vin),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
_buildCertDetailItem(
|
||||||
|
'有效期至',
|
||||||
|
'2028-08-14',
|
||||||
|
valueColor: const Color(0xFF52C41A),
|
||||||
|
),
|
||||||
|
_buildCertDetailItem('使用性质', '货运'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
// 附件预览部分
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
itemCount: attachments.length,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final url = attachments[index];
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
controller.openAttachment(url);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
height: 184.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
border: Border.all(color: Color.fromRGBO(226, 232, 240, 1)),
|
||||||
|
color: Color.fromRGBO(248, 250, 252, 1)
|
||||||
|
),
|
||||||
|
child: Center(child: _buildAttachmentPreview(url)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 证件展示
|
Widget _buildCertDetailItem(
|
||||||
Widget _buildCertificateRow({
|
String label,
|
||||||
required IconData icon,
|
String value, {
|
||||||
required String title,
|
Color? valueColor,
|
||||||
required List<String> attachments,
|
bool isFull = false,
|
||||||
}) {
|
}) {
|
||||||
return ListTile(
|
return Container(
|
||||||
contentPadding: EdgeInsets.zero,
|
width: isFull ? null : 140,
|
||||||
leading: CircleAvatar(
|
child: Column(
|
||||||
radius: 24,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
backgroundColor: Colors.blue.withOpacity(0.1),
|
children: [
|
||||||
child: Icon(icon, color: Colors.blue, size: 28),
|
Text(label, style: const TextStyle(fontSize: 12, color: Colors.grey)),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: valueColor ?? Colors.black87,
|
||||||
),
|
),
|
||||||
title: Text(
|
maxLines: 2,
|
||||||
title,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
|
||||||
),
|
),
|
||||||
subtitle: Obx(
|
],
|
||||||
() => Text(
|
|
||||||
'共 ${attachments.length} 个附件',
|
|
||||||
style: const TextStyle(color: Colors.grey, fontSize: 12),
|
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 附件预览组件 (智能判断 PDF 或图片)
|
||||||
|
Widget _buildAttachmentPreview(String url) {
|
||||||
|
return AspectRatio(
|
||||||
|
aspectRatio: 4 / 3,
|
||||||
|
child: controller.isPdf(url)
|
||||||
|
? Obx(() {
|
||||||
|
final bool loading = controller.isLoading[url] ?? true;
|
||||||
|
final String? localPath = controller.localPdfPaths[url];
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return _buildLoadingIndicator();
|
||||||
|
} else if (localPath != null && localPath.isNotEmpty) {
|
||||||
|
return IgnorePointer(
|
||||||
|
ignoring: true, // 设置为 true 来忽略所有指针事件
|
||||||
|
child: PDFView(
|
||||||
|
backgroundColor: Color.fromRGBO(248, 250, 252, 1),
|
||||||
|
fitEachPage: true,
|
||||||
|
filePath: localPath,
|
||||||
|
fitPolicy: FitPolicy.WIDTH,
|
||||||
|
// 适配宽度
|
||||||
|
enableSwipe: false,
|
||||||
|
swipeHorizontal: false,
|
||||||
|
autoSpacing: false,
|
||||||
|
pageFling: false,
|
||||||
|
preventLinkNavigation: true, // 顺便禁用PDF内部链接的跳转
|
||||||
),
|
),
|
||||||
trailing: Container(
|
);
|
||||||
padding: const EdgeInsets.all(8),
|
} else {
|
||||||
decoration: BoxDecoration(
|
// PDF加载失败
|
||||||
color: Colors.grey[200],
|
return _buildErrorIndicator();
|
||||||
borderRadius: BorderRadius.circular(8),
|
}
|
||||||
|
})
|
||||||
|
: Image.network(
|
||||||
|
url,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
// 图片加载时显示loading
|
||||||
|
loadingBuilder: (context, child, loadingProgress) {
|
||||||
|
if (loadingProgress == null) return child;
|
||||||
|
return _buildLoadingIndicator();
|
||||||
|
},
|
||||||
|
// 图片加载失败时显示错误
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return _buildErrorIndicator();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
child: const Icon(Icons.find_in_page_outlined, color: AppTheme.themeColor),
|
);
|
||||||
),
|
}
|
||||||
onTap: () => controller.navigateToCertificateViewer(title, attachments),
|
|
||||||
|
Widget _buildLoadingIndicator() {
|
||||||
|
return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildErrorIndicator() {
|
||||||
|
return const SizedBox(
|
||||||
|
height: 200,
|
||||||
|
child: Center(child: Icon(Icons.error_outline, color: Colors.red, size: 48)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,7 +542,11 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
const Text(
|
const Text(
|
||||||
"请确保车辆证件齐全有效,定期检查车辆状态和证件有效期,以确保运输作业合规安全。",
|
"请确保车辆证件齐全有效,定期检查车辆状态和证件有效期,以确保运输作业合规安全。",
|
||||||
style: TextStyle(fontSize: 13, color: Color.fromRGBO(1, 113, 55, 0.8), height: 1.5),
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Color.fromRGBO(1, 113, 55, 0.8),
|
||||||
|
height: 1.5,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
const Text(
|
const Text(
|
||||||
|
|||||||
Reference in New Issue
Block a user