import 'dart:io'; import 'package:dio/dio.dart' as dio; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:getx_scaffold/getx_scaffold.dart'; import 'package:image_picker/image_picker.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/storage_service.dart'; import 'package:permission_handler/permission_handler.dart'; class QrCodeController extends GetxController with BaseControllerMixin { @override String get builderId => 'qrcode'; final RxBool hasCameraPermission = false.obs; @override void onInit() { super.onInit(); _checkPermission(); } /// 检查权限 void _checkPermission() async { var status = await Permission.camera.status; hasCameraPermission.value = status.isGranted; } /// 1. 拍照并识别车牌流程 void takePhotoAndRecognize() 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; // 1.1 上传文件 String? imageUrl = await uploadFile(photo.path); if (imageUrl != null) { // 1.2 获取车牌号 String? plateNumber = await getPlateNumber(imageUrl); if (plateNumber != null) { // 1.3 弹窗确认 manualInputBind(plateNumber, 0); } } } /// 手动输入车牌绑定 void manualInputBind(String plateNumber, int source) { final TextEditingController controller = TextEditingController( text: plateNumber.toUpperCase() ?? '', ); DialogX.to.showConfirmDialog( title: source == 0 ? "识别结果" : '手动输入车牌', content: SizedBox( height: 40.h, child: TextField( textAlign: TextAlign.start, controller: controller, autofocus: plateNumber.isEmpty, textCapitalization: TextCapitalization.characters, decoration: const InputDecoration( contentPadding: EdgeInsets.only(left: 5), hintText: '请输入完整的车牌号', border: OutlineInputBorder(), ), ), ), confirmText: '确认绑定', cancelText: source == 0 ? "重新拍摄" : '取消', onCancel: () { if (source == 0) { takePhotoAndRecognize(); } }, onConfirm: () { final plate = controller.text.trim().toUpperCase(); if (plate.isNotEmpty) { bindVehicleByPlate(plate); } }, ); } /// 上传图片 Future uploadFile(String filePath) async { showLoading("正在上传图片..."); try { dio.FormData formData = dio.FormData.fromMap({ 'file': await dio.MultipartFile.fromFile(filePath, filename: 'ocr_plate.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; } /// OCR 识别 Future getPlateNumber(String imageUrl) async { showLoading("正在识别车牌..."); try { var response = await HttpService.to.get( "appointment/ocr/getPlateNumber", params: {'imageUrl': imageUrl}, ); 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 bindVehicleByPlate(String plateNumber) async { showLoading("正在绑定车辆..."); try { var responseData = await HttpService.to.post( "appointment/truck/bindOcrTruck", data: {"plateNumber": plateNumber, "phone": StorageService.to.phone}, ); var result = BaseModel.fromJson(responseData?.data); if (result.code == 0 && result.data != null) { await StorageService.to.saveVehicleInfo(VehicleInfo.fromJson(result.data)); dismissLoading(); showSuccessToast("绑定成功"); Get.back(result: true); } else { showErrorToast(result.error); } } catch (e) { showErrorToast("绑定失败,请检查网络"); } finally { dismissLoading(); } } }