Files
ln-ios/ln_jq_app/lib/pages/qr_code/controller.dart
2026-01-30 16:02:35 +08:00

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();
}
}
}