163 lines
4.7 KiB
Dart
163 lines
4.7 KiB
Dart
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<String?> 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<String?> 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) {
|
|
|
|
} finally {
|
|
dismissLoading();
|
|
}
|
|
}
|
|
}
|