司机-可上传证件
This commit is contained in:
BIN
ln_jq_app/assets/images/ic_attention@2x.png
Normal file
BIN
ln_jq_app/assets/images/ic_attention@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
ln_jq_app/assets/images/ic_upload@2x.png
Normal file
BIN
ln_jq_app/assets/images/ic_upload@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,5 +1,8 @@
|
|||||||
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:getx_scaffold/getx_scaffold.dart' as dio;
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:image_picker_platform_interface/src/types/image_source.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/c_page/car_info/attachment_viewer_page.dart';
|
||||||
@@ -89,6 +92,114 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//上传证件照
|
||||||
|
void pickImage(String title, ImageSource source) async {
|
||||||
|
if (source == ImageSource.camera) {
|
||||||
|
takePhotoAndRecognize(title);
|
||||||
|
} else if (source == ImageSource.gallery) {
|
||||||
|
//相册选择逻辑
|
||||||
|
var status = await Permission.photos.request();
|
||||||
|
if (!status.isGranted) {
|
||||||
|
if (status.isPermanentlyDenied) openAppSettings();
|
||||||
|
showErrorToast("需要相册权限才能拍照上传");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final XFile? image = await ImagePicker().pickImage(
|
||||||
|
source: ImageSource.gallery,
|
||||||
|
imageQuality: 80,
|
||||||
|
);
|
||||||
|
if (image != null) {
|
||||||
|
_uploadAndSaveCertificate(title, image.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void takePhotoAndRecognize(String title) async {
|
||||||
|
var status = await Permission.camera.request();
|
||||||
|
if (!status.isGranted) {
|
||||||
|
if (status.isPermanentlyDenied) openAppSettings();
|
||||||
|
showErrorToast("需要相机权限才能拍照上传");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final XFile? photo = await ImagePicker().pickImage(
|
||||||
|
source: ImageSource.camera,
|
||||||
|
imageQuality: 80, // 压缩图片质量以加快上传
|
||||||
|
);
|
||||||
|
if (photo == null) return;
|
||||||
|
|
||||||
|
_uploadAndSaveCertificate(title, photo.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 提取共用的上传与关联证件逻辑
|
||||||
|
void _uploadAndSaveCertificate(String title, String filePath) async {
|
||||||
|
// 上传文件
|
||||||
|
String? imageUrl = await uploadFile(filePath);
|
||||||
|
if (imageUrl == null) return;
|
||||||
|
|
||||||
|
// 根据标题映射业务类型
|
||||||
|
Logger.d("message-$title-$imageUrl");
|
||||||
|
String type = "";
|
||||||
|
switch (title) {
|
||||||
|
case "行驶证":
|
||||||
|
type = "9";
|
||||||
|
break;
|
||||||
|
case "营运证":
|
||||||
|
type = "10";
|
||||||
|
break;
|
||||||
|
case "加氢证":
|
||||||
|
type = "12";
|
||||||
|
break;
|
||||||
|
case "登记证":
|
||||||
|
type = "13";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用后台接口关联证件
|
||||||
|
var response = await HttpService.to.post(
|
||||||
|
"appointment/truck/uploadCertificatePic",
|
||||||
|
data: {
|
||||||
|
"plateNumber": plateNumber,
|
||||||
|
"imageUrl": imageUrl,
|
||||||
|
"type": type,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response != null) {
|
||||||
|
final result = BaseModel.fromJson(response.data);
|
||||||
|
if (result.code == 0) {
|
||||||
|
showSuccessToast("上传成功");
|
||||||
|
getUserBindCarInfo(); // 重新拉取数据更新UI
|
||||||
|
} else {
|
||||||
|
showErrorToast(result.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 上传图片
|
||||||
|
Future<String?> uploadFile(String filePath) async {
|
||||||
|
showLoading("正在上传...");
|
||||||
|
try {
|
||||||
|
dio.FormData formData = dio.FormData.fromMap({
|
||||||
|
'file': await dio.MultipartFile.fromFile(filePath, filename: 'ocr_identity.jpg'),
|
||||||
|
});
|
||||||
|
|
||||||
|
var response = await HttpService.to.post("appointment/ocr/upload", data: formData);
|
||||||
|
if (response != null) {
|
||||||
|
final result = BaseModel.fromJson(response.data);
|
||||||
|
if (result.code == 0) return result.data.toString();
|
||||||
|
showErrorToast(result.error);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showErrorToast("图片上传失败");
|
||||||
|
} finally {
|
||||||
|
dismissLoading();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
void getUserBindCarInfo() async {
|
void getUserBindCarInfo() async {
|
||||||
if (StorageService.to.hasVehicleInfo) {
|
if (StorageService.to.hasVehicleInfo) {
|
||||||
VehicleInfo? bean = StorageService.to.vehicleInfo;
|
VehicleInfo? bean = StorageService.to.vehicleInfo;
|
||||||
@@ -102,7 +213,7 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
// 获取证件信息
|
// 获取证件信息
|
||||||
final response = await HttpService.to.get(
|
final response = await HttpService.to.get(
|
||||||
'appointment/vehicle/getPicInfoByVin?vin=$vin',
|
'appointment/vehicle/getPicInfoByVin?vin=$vin&plateNumber=$plateNumber',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response != null && response.data != null) {
|
if (response != null && response.data != null) {
|
||||||
@@ -134,10 +245,10 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
...registerAttachments,
|
...registerAttachments,
|
||||||
];
|
];
|
||||||
|
|
||||||
color = data['color'].toString();
|
color = data['color'].toString();
|
||||||
hydrogenCapacity = data['hydrogenCapacity'].toString();
|
hydrogenCapacity = data['hydrogenCapacity'].toString();
|
||||||
rentFromCompany = data['rentFromCompany'].toString();
|
rentFromCompany = data['rentFromCompany'].toString();
|
||||||
address = data['address'].toString();
|
address = data['address'].toString();
|
||||||
|
|
||||||
loadAllPdfs();
|
loadAllPdfs();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_pdfview/flutter_pdfview.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:image_picker/image_picker.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';
|
||||||
@@ -368,7 +369,7 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
children: [
|
children: [
|
||||||
_buildCertificateContent('行驶证', controller.drivingAttachments),
|
_buildCertificateContent('行驶证', controller.drivingAttachments),
|
||||||
_buildCertificateContent('营运证', controller.operationAttachments),
|
_buildCertificateContent('营运证', controller.operationAttachments),
|
||||||
_buildCertificateContent('加氢资格证', controller.hydrogenationAttachments),
|
_buildCertificateContent('加氢证', controller.hydrogenationAttachments),
|
||||||
_buildCertificateContent('登记证', controller.registerAttachments),
|
_buildCertificateContent('登记证', controller.registerAttachments),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -388,7 +389,7 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(16.0),
|
padding: EdgeInsets.all(16.0),
|
||||||
child: attachments.isEmpty
|
child: attachments.isEmpty
|
||||||
? const Center(child: Text('暂无相关证件信息'))
|
? _buildEmptyCertificateState(title)
|
||||||
: Column(
|
: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@@ -440,6 +441,158 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildEmptyCertificateState(String title) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
'assets/images/ic_attention@2x.png', // 请替换为您的实际图片路径
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'您未上传“$title”',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color.fromRGBO(51, 51, 51, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'上传后可提前通知加氢站报备\n大幅减少加氢站等待时间',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12.sp,
|
||||||
|
color: Color.fromRGBO(156, 163, 175, 1),
|
||||||
|
height: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
SizedBox(
|
||||||
|
width: 200,
|
||||||
|
height: 44,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
_showUploadDialog(title);
|
||||||
|
},
|
||||||
|
icon: const Text(
|
||||||
|
'立即上传',
|
||||||
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
label: Image.asset(
|
||||||
|
'assets/images/ic_upload@2x.png',
|
||||||
|
height: 20.h,
|
||||||
|
width: 20.w,
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color(0xFF017137),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
shape: StadiumBorder(),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showUploadDialog(String title) {
|
||||||
|
Get.dialog(
|
||||||
|
Dialog(
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'上传$title',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
'请确保拍摄证件清晰可见,关键文字无反光遮挡,这将有助于快速通过审核',
|
||||||
|
style: TextStyle(fontSize: 13, color: Colors.grey[400], height: 1.5),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildUploadOption(
|
||||||
|
icon: Icons.camera_alt_outlined,
|
||||||
|
label: '拍照上传',
|
||||||
|
onTap: () {
|
||||||
|
controller.pickImage(title, ImageSource.camera);
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: _buildUploadOption(
|
||||||
|
icon: Icons.image_outlined,
|
||||||
|
label: '相册上传',
|
||||||
|
onTap: () {
|
||||||
|
controller.pickImage(title, ImageSource.gallery);
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建弹窗内的选择按钮
|
||||||
|
Widget _buildUploadOption({
|
||||||
|
required IconData icon,
|
||||||
|
required String label,
|
||||||
|
required VoidCallback onTap,
|
||||||
|
}) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 24),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFF2F9F7), // 浅绿色背景
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Color(0xFF017137), // 深绿色圆圈
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(icon, color: Colors.white, size: 28),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Color(0xFF333333),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildCertDetailItem(
|
Widget _buildCertDetailItem(
|
||||||
String label,
|
String label,
|
||||||
String value, {
|
String value, {
|
||||||
|
|||||||
Reference in New Issue
Block a user