374 lines
11 KiB
Dart
374 lines
11 KiB
Dart
import 'package:common_utils/common_utils.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
|
import 'package:get/get.dart';
|
|
|
|
import 'package:pj_supermarket_cloud/common/index.dart';
|
|
|
|
class CardActiveController extends GetxController {
|
|
final appC = Get.find<AppControl>();
|
|
|
|
SerialCardManage get serialCardManage => appC.serialCardManage;
|
|
|
|
// table
|
|
RxInt currentPage = 1.obs;
|
|
|
|
RxInt totalPage = 0.obs;
|
|
|
|
RxInt pageSize = 10.obs;
|
|
|
|
RxList<ServiceOpenCard> tableList = <ServiceOpenCard>[].obs;
|
|
|
|
RxBool isLoading = false.obs;
|
|
|
|
RxString errorMessage = ''.obs;
|
|
|
|
Rx<ServiceOpenCard?> detail = Rx(null);
|
|
|
|
RxBool readLoading = false.obs;
|
|
|
|
final Rx<SerialMifareScanProgress?> scanProgress = Rx(null);
|
|
|
|
// 写卡
|
|
RxBool writeLoading = false.obs;
|
|
|
|
// 卡号
|
|
final carNumberC = TextEditingController();
|
|
|
|
_initData() {
|
|
update(["card_active"]);
|
|
}
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
getTableList();
|
|
}
|
|
|
|
@override
|
|
void onReady() {
|
|
super.onReady();
|
|
_initData();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
super.onClose();
|
|
carNumberC.dispose();
|
|
}
|
|
|
|
Future<void> doPage({int? page}) async {
|
|
if (page != null) currentPage.value = page;
|
|
// 收起键盘
|
|
FocusManager.instance.primaryFocus?.unfocus();
|
|
getTableList();
|
|
}
|
|
|
|
Future<void> doReset() async {
|
|
currentPage.value = 1;
|
|
carNumberC.clear();
|
|
// 收起键盘
|
|
FocusManager.instance.primaryFocus?.unfocus();
|
|
await getTableList();
|
|
}
|
|
|
|
Future<void> getTableList() async {
|
|
isLoading.value = true;
|
|
errorMessage.value = '';
|
|
EasyLoading.show(
|
|
status: '查询中...',
|
|
maskType: EasyLoadingMaskType.black,
|
|
);
|
|
try {
|
|
final pa = {
|
|
"current": currentPage.value,
|
|
"size": pageSize.value,
|
|
"deptId": "",
|
|
"walletTypeId": "",
|
|
"cardNumber": carNumberC.text,
|
|
};
|
|
final data = await WalletApi.openCardList(pa);
|
|
if (data == null) {
|
|
tableList.clear();
|
|
totalPage.value = 0;
|
|
return;
|
|
}
|
|
totalPage.value = data.total;
|
|
final records = data.records;
|
|
if (records != null) {
|
|
tableList.value = records;
|
|
} else {
|
|
tableList.clear();
|
|
}
|
|
} catch (e, stack) {
|
|
errorMessage.value = '饭卡数据加载失败,请检查网络后重试';
|
|
logger.e('饭卡列表查询失败error:|$e|stack:|$stack|');
|
|
} finally {
|
|
isLoading.value = false;
|
|
EasyLoading.dismiss();
|
|
}
|
|
}
|
|
|
|
// 写卡
|
|
void doWriteCard(ServiceOpenCard v) async {
|
|
if (v.isActiveHardware == true) {
|
|
Toast.error(msg: "该卡已经激活,无需重复激活");
|
|
return;
|
|
}
|
|
if (ObjectUtil.isEmpty(v.cardNumber)) {
|
|
Toast.error(msg: "没有设置卡号");
|
|
return;
|
|
}
|
|
try {
|
|
await serialCardManage.connect();
|
|
if (!serialCardManage.isConnect) {
|
|
Toast.error(msg: "连接读卡器失败");
|
|
return;
|
|
}
|
|
writeLoading.value = true;
|
|
detail.value = null;
|
|
cardDialog(
|
|
title: "写卡",
|
|
loading: writeLoading,
|
|
detail: detail,
|
|
doSubmit: _closeStatus,
|
|
);
|
|
serialCardManage.callback = (type, status, res) async {
|
|
if (type != SerialCardCmdEnum.write) return;
|
|
switch (status) {
|
|
case SerialCardResStatus.success:
|
|
if (ObjectUtil.isNotEmpty(v.id)) {
|
|
await _getOpenCardSuccess(v.id!);
|
|
getTableList();
|
|
}
|
|
detail.value = v;
|
|
_closeStatus();
|
|
break;
|
|
case SerialCardResStatus.warning:
|
|
break;
|
|
case SerialCardResStatus.error:
|
|
final message = res is String && res.trim().isNotEmpty
|
|
? res.trim()
|
|
: "写卡失败,请重试";
|
|
TtsManage.speak("写卡失败");
|
|
Toast.error(msg: message);
|
|
_closeStatus();
|
|
break;
|
|
}
|
|
};
|
|
TtsManage.speak("请在合适区域放置饭卡");
|
|
serialCardManage.sendWrite(v.cardNumber!);
|
|
} catch (e, stack) {
|
|
_closeStatus();
|
|
logger.d("写卡失败error:|$e|stack:|$stack|");
|
|
Toast.error(msg: "写入失败,请重试");
|
|
}
|
|
}
|
|
|
|
// 读卡
|
|
void doReadCard() async {
|
|
try {
|
|
final config = await AppSettingTool.getSerialCardConfig();
|
|
await serialCardManage.connect();
|
|
if (!serialCardManage.isConnect) {
|
|
Toast.error(msg: "连接读卡器失败");
|
|
return;
|
|
}
|
|
readLoading.value = true;
|
|
detail.value = null;
|
|
scanProgress.value = null;
|
|
cardDialog(
|
|
title: "读卡",
|
|
loading: readLoading,
|
|
detail: detail,
|
|
scanProgress: scanProgress,
|
|
onScanMifareBlocks:
|
|
config.showMifareScanButton ? doScanMifareBlocks : null,
|
|
doSubmit: _closeStatus,
|
|
);
|
|
serialCardManage.callback = (type, status, res) async {
|
|
if (type != SerialCardCmdEnum.read) return;
|
|
switch (status) {
|
|
case SerialCardResStatus.success:
|
|
await _handleReadCardSuccess(res);
|
|
break;
|
|
case SerialCardResStatus.warning:
|
|
break;
|
|
case SerialCardResStatus.error:
|
|
final message = res is String && res.trim().isNotEmpty
|
|
? res.trim()
|
|
: "读卡失败,请重试";
|
|
TtsManage.speak("读卡失败");
|
|
Toast.error(msg: message);
|
|
_closeStatus();
|
|
break;
|
|
}
|
|
};
|
|
TtsManage.speak("请在合适区域放置饭卡");
|
|
serialCardManage.sendRead();
|
|
} catch (e, stack) {
|
|
_closeStatus();
|
|
logger.d("读卡失败error:|$e|stack:|$stack|");
|
|
Toast.error(msg: "读取失败,请重试");
|
|
}
|
|
}
|
|
|
|
/// 从当前读卡弹窗切换为 M1 数据块诊断扫描,不改变正常读卡与支付流程。
|
|
Future<void> doScanMifareBlocks() async {
|
|
final currentProgress = scanProgress.value;
|
|
if (currentProgress != null && !currentProgress.completed) return;
|
|
try {
|
|
if (readLoading.value) {
|
|
// 用户主动切换扫描模式时先终止普通读卡,避免两个串口状态机竞争同一设备。
|
|
readLoading.value = false;
|
|
_closeSerialCardOperation();
|
|
}
|
|
detail.value = null;
|
|
// 点击后先建立扫描状态,连接串口期间按钮也会立即禁用,避免重复启动。
|
|
scanProgress.value = SerialMifareScanProgress.initial(
|
|
totalBlocks: SerialReadCardTool.mifareSafeDataBlockAddresses().length,
|
|
);
|
|
await serialCardManage.connect();
|
|
if (!serialCardManage.isConnect) {
|
|
scanProgress.value = null;
|
|
Toast.error(msg: "连接读卡器失败");
|
|
return;
|
|
}
|
|
serialCardManage.callback = (type, status, result) {
|
|
if (type != SerialCardCmdEnum.scan) return;
|
|
switch (status) {
|
|
case SerialCardResStatus.success:
|
|
if (result is SerialMifareScanProgress) {
|
|
scanProgress.value = result;
|
|
if (result.completed) {
|
|
final stopReason = result.stopReason;
|
|
if (stopReason == null) {
|
|
Toast.success(msg: "M1 数据块扫描完成");
|
|
} else {
|
|
Toast.error(msg: "扫描已提前结束:$stopReason");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SerialCardResStatus.warning:
|
|
break;
|
|
case SerialCardResStatus.error:
|
|
final message = result is String && result.trim().isNotEmpty
|
|
? result.trim()
|
|
: "M1 数据块扫描失败";
|
|
Toast.error(msg: message);
|
|
scanProgress.value = null;
|
|
_closeSerialCardOperation();
|
|
break;
|
|
}
|
|
};
|
|
TtsManage.speak("请保持饭卡不动,正在扫描数据块");
|
|
serialCardManage.scanMifareBlocks();
|
|
} catch (e, stack) {
|
|
scanProgress.value = null;
|
|
_closeSerialCardOperation();
|
|
logger.e("扫描 M1 数据块失败error:|$e|stack:|$stack|");
|
|
Toast.error(msg: "M1 数据块扫描失败");
|
|
}
|
|
}
|
|
|
|
/// 串口读卡成功后先更新弹窗,再查询绑定用户。
|
|
///
|
|
/// 读卡与网络查询是两个阶段,不能让接口等待、空列表或异常使界面一直停留在“扫描中”。
|
|
Future<void> _handleReadCardSuccess(dynamic result) async {
|
|
final cardNumber =
|
|
(result?.toString() ?? '').replaceAll(RegExp(r'[^a-zA-Z0-9]'), '');
|
|
if (cardNumber.isEmpty) {
|
|
TtsManage.speak("未读取到有效卡号");
|
|
Toast.error(msg: "未读取到有效卡号,请重新刷卡");
|
|
_closeStatus();
|
|
return;
|
|
}
|
|
|
|
// 先把串口结果写入当前弹窗的 Rx,网络查询期间不再错误显示为仍在读卡。
|
|
readLoading.value = false;
|
|
detail.value = ServiceOpenCard(
|
|
username: "用户信息查询中",
|
|
cardNumber: cardNumber,
|
|
);
|
|
TtsManage.speak("读取成功");
|
|
|
|
try {
|
|
final data = await WalletApi.openCardList({"cardNumber": cardNumber});
|
|
final records = data?.records;
|
|
if (records == null || records.isEmpty) {
|
|
detail.value = ServiceOpenCard(
|
|
username: "未绑定用户",
|
|
cardNumber: cardNumber,
|
|
);
|
|
TtsManage.speak("此饭卡未绑定用户");
|
|
Toast.error(msg: "此饭卡未绑定用户");
|
|
return;
|
|
}
|
|
detail.value = records.first;
|
|
} catch (e, stack) {
|
|
detail.value = ServiceOpenCard(
|
|
username: "用户信息查询失败",
|
|
cardNumber: cardNumber,
|
|
);
|
|
logger.e("读卡成功但查询绑定用户失败error:|$e|stack:|$stack|");
|
|
Toast.error(msg: "卡号读取成功,用户信息查询失败");
|
|
} finally {
|
|
// 用户可能已从普通读卡切换到块扫描,旧网络查询不得关闭新的串口操作。
|
|
if (serialCardManage.currentCmd != SerialCardCmdEnum.scan) {
|
|
_closeStatus();
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _getOpenCardSuccess(String id) async {
|
|
try {
|
|
EasyLoading.show(
|
|
status: '激活饭卡中...',
|
|
maskType: EasyLoadingMaskType.black,
|
|
);
|
|
final pa = {"id": id};
|
|
final data = await WalletApi.openCardSuccess(pa);
|
|
if (data == false) throw Error();
|
|
EasyLoading.dismiss();
|
|
TtsManage.speak("激活成功");
|
|
Toast.success(msg: "激活成功");
|
|
} catch (e, stack) {
|
|
logger.e("激活饭卡请求失败error:|$e|stack:|$stack|");
|
|
EasyLoading.dismiss();
|
|
Toast.error(msg: "激活失败");
|
|
}
|
|
}
|
|
|
|
// 重新激活
|
|
Future<void> doRefreshAvatar(ServiceOpenCard v) async {
|
|
if (v.isActiveHardware == false) {
|
|
Toast.error(msg: "该卡为激活状态,不能重新激活");
|
|
return;
|
|
}
|
|
try {
|
|
final isSure = await appMessageDialog(Get.context!, "是否重新激活");
|
|
if (!isSure) return;
|
|
final pa = {"id": v.id};
|
|
final data = await WalletApi.refreshAvatar(pa);
|
|
if (data == false) return;
|
|
TtsManage.speak("重新激活成功");
|
|
Toast.success(msg: "重新激活成功");
|
|
getTableList();
|
|
} catch (e, stack) {
|
|
logger.d("重新激活失败error:|$e|stack:|$stack|");
|
|
}
|
|
}
|
|
|
|
void _closeStatus() {
|
|
_closeSerialCardOperation();
|
|
readLoading.value = false;
|
|
writeLoading.value = false;
|
|
}
|
|
|
|
void _closeSerialCardOperation() {
|
|
serialCardManage.callback = null;
|
|
serialCardManage.close();
|
|
}
|
|
}
|