diff --git a/lib/common/services/database/entity/supermarket_spu_entity.dart b/lib/common/services/database/entity/supermarket_spu_entity.dart index 4669e87..1716190 100644 --- a/lib/common/services/database/entity/supermarket_spu_entity.dart +++ b/lib/common/services/database/entity/supermarket_spu_entity.dart @@ -1,3 +1,4 @@ +import 'package:equatable/equatable.dart'; import 'package:floor/floor.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:pj_supermarket_cloud/common/index.dart'; @@ -6,7 +7,7 @@ part 'supermarket_spu_entity.g.dart'; @JsonSerializable() @Entity(tableName: "supermarket_spu") -class SupermarketSpuEntity { +class SupermarketSpuEntity extends Equatable { /// 自增ID @PrimaryKey(autoGenerate: true) int? id; @@ -95,4 +96,22 @@ class SupermarketSpuEntity { _$SupermarketSpuEntityFromJson(json); Map toJson() => _$SupermarketSpuEntityToJson(this); + + @override + List get props => [ + serviceUniqueCode, + classifyServiceUniqueCode, + productCode, + productName, + productNature, + barCode, + packageNum, + unit, + spec, + picUrl, + stock, + purchasePrice, + retailPrice, + promotionPrice, + ]; } diff --git a/lib/common/utils/manage/barcode_scan_manage.dart b/lib/common/utils/manage/barcode_scan_manage.dart index 751c0c8..f0f5277 100644 --- a/lib/common/utils/manage/barcode_scan_manage.dart +++ b/lib/common/utils/manage/barcode_scan_manage.dart @@ -17,12 +17,16 @@ class BarcodeScanManage { static BarcodeScanManage get instance => _instance; bool _isListening = false; - String _buffer = ''; + StringBuffer _buffer = StringBuffer(); Timer? _timer; - final StreamController _scanController = - StreamController.broadcast(); - Stream get scanStream => _scanController.stream; + // 二维码扫码结果 + final _qrcodeScanController = StreamController.broadcast(); + Stream get qrcodeScanStream => _qrcodeScanController.stream; + + // 扫码枪扫码结果 + final _gunScanController = StreamController.broadcast(); + Stream get gunScanStream => _gunScanController.stream; // 开始监听USB读头输入 void startListening() { @@ -30,44 +34,56 @@ class BarcodeScanManage { _isListening = true; // 确保没有焦点的控件 FocusManager.instance.primaryFocus?.unfocus(); - RawKeyboard.instance.addListener(_handleKeyEvent); + HardwareKeyboard.instance.addHandler(_handleKeyEvent); logger.i("|$_tag|开始监听USB读头输入"); } // 停止监听 void stopListening() { if (!_isListening) return; - RawKeyboard.instance.removeListener(_handleKeyEvent); + HardwareKeyboard.instance.removeHandler(_handleKeyEvent); _timer?.cancel(); _isListening = false; logger.i("|$_tag|停止监听USB读头输入"); } // 处理键盘事件 - void _handleKeyEvent(RawKeyEvent event) { - if (event is! RawKeyDownEvent) return; - // 阻止键盘弹出 - FocusManager.instance.primaryFocus?.unfocus(); - // 重置定时器 - _timer?.cancel(); - // 处理回车键,表示扫码结束 - if (event.logicalKey.keyId == 0x10000000d) { - if (_buffer.isNotEmpty) { - logger.i("|$_tag|扫码结果: $_buffer"); - _scanController.add(_buffer); - _buffer = ''; + bool _handleKeyEvent(KeyEvent event) { + if (event is! KeyDownEvent) return false; + try { + FocusManager.instance.primaryFocus?.unfocus(); + _timer?.cancel(); + // 处理回车键,表示扫码结束 + if (event.logicalKey.keyId == 0x10000000d) { + if (_buffer.isNotEmpty) { + final result = _buffer.toString(); + logger.i("|$_tag|扫码结果: $result"); + if (result.length >= 100) { + _qrcodeScanController.add(result); + } else { + _gunScanController.add(result); + } + _buffer.clear(); + } + return true; } - return; - } - - // 获取字符 - final character = _getCharacter(event); - if (character != null) { - _buffer += character; - // 设置超时定时器,如果超过一定时间没有新输入,则清空缓冲区 - _timer = Timer(Duration(milliseconds: 100), () { - _buffer = ''; - }); + final character = _getCharacter(event); + if (character != null) { + _buffer.write(character); + // 设置超时定时器,如果超过一定时间没有新输入,则清空缓冲区 + _timer = Timer(Duration(milliseconds: 100), () { + if (_buffer.isNotEmpty) { + logger.d("|$_tag|扫码超时,清空缓冲区"); + _buffer.clear(); + } + }); + } + return true; + } catch (e, stack) { + logger.e("|$_tag|处理键盘事件错误error|$e|stack|$stack|"); + _buffer.clear(); + _timer?.cancel(); + return false; } } @@ -82,31 +98,28 @@ class BarcodeScanManage { } // 从键盘事件中获取字符 - String? _getCharacter(RawKeyEvent event) { + String? _getCharacter(KeyEvent event) { // 处理数字键 if (event.logicalKey.keyId >= LogicalKeyboardKey.digit0.keyId && event.logicalKey.keyId <= LogicalKeyboardKey.digit9.keyId) { return String.fromCharCode( event.logicalKey.keyId - LogicalKeyboardKey.digit0.keyId + 48); } - - // 处理小写字母键 +// 处理小写字母键 if (event.logicalKey.keyId >= LogicalKeyboardKey.keyA.keyId && event.logicalKey.keyId <= LogicalKeyboardKey.keyZ.keyId) { - // 检查是否按下了Shift键,决定返回大写还是小写字母 - final isShiftPressed = event.isShiftPressed; + final isShiftPressed = HardwareKeyboard.instance.isShiftPressed; final charCode = event.logicalKey.keyId - LogicalKeyboardKey.keyA.keyId + (isShiftPressed ? 65 : 97); return String.fromCharCode(charCode); } - // 处理其他常见字符 switch (event.logicalKey) { case LogicalKeyboardKey.minus: - return event.isShiftPressed ? '_' : '-'; + return HardwareKeyboard.instance.isShiftPressed ? '_' : '-'; case LogicalKeyboardKey.equal: - return event.isShiftPressed ? '+' : '='; + return HardwareKeyboard.instance.isShiftPressed ? '+' : '='; case LogicalKeyboardKey.bracketLeft: return '['; case LogicalKeyboardKey.bracketRight: @@ -122,7 +135,7 @@ class BarcodeScanManage { case LogicalKeyboardKey.period: return '.'; case LogicalKeyboardKey.slash: - return event.isShiftPressed ? '?' : '/'; + return HardwareKeyboard.instance.isShiftPressed ? '?' : '/'; case LogicalKeyboardKey.add: return '+'; case LogicalKeyboardKey.underscore: @@ -177,6 +190,15 @@ class BarcodeScanManage { // 释放资源 void dispose() { stopListening(); - _scanController.close(); + _qrcodeScanController.close(); + _gunScanController.close(); + } + + void disposeQrCodeScan() { + _qrcodeScanController.close(); + } + + void disposeGunScan() { + _gunScanController.close(); } } diff --git a/lib/common/utils/sync/sync_product_tool.dart b/lib/common/utils/sync/sync_product_tool.dart index fb73f04..6513e66 100644 --- a/lib/common/utils/sync/sync_product_tool.dart +++ b/lib/common/utils/sync/sync_product_tool.dart @@ -163,21 +163,34 @@ abstract class SyncProductTool { await _batchUpdateCaneenProduct(list: canteenList); } else if (sysType == SysType.SUPERMARKET) { // 超市 + final app = Get.find(); + final dataBase = app.dataBase; + final supermarketSpuDao = dataBase.getSupermarketSpuDao; + final supermarketSkuDao = dataBase.getSupermarketSkuDao; + final supermarketCodeDao = dataBase.getSupermarketCodeDao; int currentPage = 1; + Set serviceCodes = {}; while (true) { - final pa = { - "current": currentPage, - "size": 50, - }; - final supermarket = await ProductApi.supermarketSpu( - pa, + final pa = {"current": currentPage, "size": 50}; + final supermarket = await ProductApi.supermarketSpu(pa); + if (supermarket == null) break; + if (ObjectUtil.isEmptyList(supermarket.records)) break; + final ids = await _batchUpdateSupermarketProduct( + list: supermarket.records!, ); - if (supermarket == null || - ObjectUtil.isEmptyList(supermarket.records)) { - break; - } currentPage += 1; - await _batchUpdateSupermarketProduct(list: supermarket.records!); + serviceCodes.addAll(ids); + } + // 处理删除 + final productList = await supermarketSpuDao.getAll( + "-1", + ); + final dbCodes = productList.map((e) => e.serviceUniqueCode).toSet(); + final deleteCodes = dbCodes.difference(serviceCodes).toList(); + if (ObjectUtil.isNotEmpty(deleteCodes)) { + await supermarketSpuDao.delByIds(deleteCodes); + await supermarketSkuDao.delBySpuIds(deleteCodes); + await supermarketCodeDao.delBySpuIds(deleteCodes); } } // 更新同步时间 @@ -299,7 +312,7 @@ abstract class SyncProductTool { // 同步食堂商品 spu start // ************************************************************************** - static Future _batchUpdateSupermarketProduct({ + static Future> _batchUpdateSupermarketProduct({ required List list, }) async { final app = Get.find(); @@ -314,13 +327,12 @@ abstract class SyncProductTool { isUpdate: false, ); await _batchAddOrUpdateSupermarketSpuAndCode(list); - return; + return list.map((e) => e.id.toString()).toSet().toList(); } final apiServiceCodes = list.map((e) => e.id.toString()).toSet(); final dbServiceCodes = productList.map((e) => e.serviceUniqueCode).toSet(); final addCodes = apiServiceCodes.difference(dbServiceCodes).toList(); - final deleteCodes = dbServiceCodes.difference(apiServiceCodes).toList(); final updateCodes = apiServiceCodes.intersection(dbServiceCodes).toList(); if (ObjectUtil.isNotEmpty(addCodes)) { await _batchAddOrUpdateSupermarketProduct( @@ -328,9 +340,6 @@ abstract class SyncProductTool { isUpdate: false, ); } - if (ObjectUtil.isNotEmpty(deleteCodes)) { - await supermarketSpuDao.delByIds(deleteCodes); - } if (ObjectUtil.isNotEmpty(updateCodes)) { await _batchAddOrUpdateSupermarketProduct( list: _filterCaneenSupermarketByCodes(list, updateCodes), @@ -338,6 +347,7 @@ abstract class SyncProductTool { ); } await _batchAddOrUpdateSupermarketSpuAndCode(list); + return apiServiceCodes.toList(); } static Future _batchAddOrUpdateSupermarketProduct({ @@ -354,13 +364,13 @@ abstract class SyncProductTool { serviceUniqueCode: v.id ?? "-1", classifyServiceUniqueCode: v.categoryId ?? "-1", productCode: v.productCode ?? "-1", - productName: v.productCode ?? "", + productName: v.productName ?? "", productNature: ProductNature.values.fromCode(v.productNature) ?? ProductNature.DEFAULT, barCode: "", packageNum: 0, - unit: v.productCode ?? "", - spec: v.productCode ?? "", + unit: v.unit ?? "", + spec: v.spec ?? "", picUrl: v.picUrl ?? "", stock: v.stock ?? 0, salesCount: diff --git a/lib/common/values/constants.dart b/lib/common/values/constants.dart index 5803ac0..9b24c62 100644 --- a/lib/common/values/constants.dart +++ b/lib/common/values/constants.dart @@ -10,9 +10,9 @@ class Constants { static const pwdEncKey = 'pigxpigxpigxpigx'; /// 服务 url - static const apiUrl = 'https://stgl.pj.gov.cn/api'; + // static const apiUrl = 'https://stgl.pj.gov.cn/api'; - // static const apiUrl = 'http://192.168.101.64:9999'; + static const apiUrl = 'http://192.168.101.64:9999'; /// 静态资源 url static const ossUrl = apiUrl; diff --git a/lib/common/widgets/order/order_supermarket_common.dart b/lib/common/widgets/order/order_supermarket_common.dart index ec810db..d902954 100644 --- a/lib/common/widgets/order/order_supermarket_common.dart +++ b/lib/common/widgets/order/order_supermarket_common.dart @@ -7,9 +7,10 @@ import 'package:pj_supermarket_cloud/common/index.dart'; Widget orderSupermarketSpuCard( BuildContext context, { required SupermarketSpuEntity supermarketSpu, + bool visibleProductImg = false, }) { final theme = FTheme.of(context); - final textStyle = theme.typography.sm.copyWith( + final textStyle = theme.typography.xs.copyWith( color: theme.colorScheme.primary, ); return FCard( @@ -19,17 +20,21 @@ Widget orderSupermarketSpuCard( ), ), image: appImg(supermarketSpu.picUrl, width: double.infinity, height: 100.h) - .center(), + .center() + .offstage(offstage: !visibleProductImg), subtitle: [ - Text(supermarketSpu.productName, style: theme.typography.xl), + Text( + supermarketSpu.productName, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: theme.typography.base, + ), SizedBox(height: 5.h), FBadge( style: FBadgeStyle.secondary, - label: Text( - supermarketSpu.productNature.title, - style: textStyle, - ), + label: Text(supermarketSpu.productNature.title, style: textStyle), ), + SizedBox(height: 5.h), [ Text("零售价:", style: textStyle), appPrice(context, supermarketSpu.retailPrice), @@ -42,16 +47,19 @@ Widget orderSupermarketSpuCard( Text("货号:", style: textStyle), Text(supermarketSpu.productCode, style: textStyle), ].toRow(mainAxisAlignment: MainAxisAlignment.center), - [ - Text("辅助条码:", style: textStyle), - Text(supermarketSpu.barCode ?? "", style: textStyle), - ].toRow(mainAxisAlignment: MainAxisAlignment.center), [ Text("包装单位:", style: textStyle), Text("${supermarketSpu.packageNum}", style: textStyle), Text("/"), Text(supermarketSpu.unit, style: textStyle), ].toRow(mainAxisAlignment: MainAxisAlignment.center), + [ + Text("条码:", style: textStyle), + Text( + supermarketSpu.barCode ?? "", + style: textStyle, + ), + ].toRow(mainAxisAlignment: MainAxisAlignment.center), ] .toColumn( crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/common/widgets/order/select_pay_type_dialog.dart b/lib/common/widgets/order/select_pay_type_dialog.dart index f6b123b..681f5e7 100644 --- a/lib/common/widgets/order/select_pay_type_dialog.dart +++ b/lib/common/widgets/order/select_pay_type_dialog.dart @@ -105,7 +105,9 @@ class SelectPayTypeDialogController extends GetxController { globalQrCode = null; _faceCallbackLock = false; // 设备 - BarcodeScanManage.instance.dispose(); + if (appSetting.sysType == SysType.CANTEEN) { + BarcodeScanManage.instance.dispose(); + } app.serialCardManage.close(); // 取消人脸监听 @@ -171,8 +173,11 @@ class SelectPayTypeDialogController extends GetxController { return; } TtsManage.speak("请出示二维码"); + isRecognition.value = true; + onDiscernUser?.call(payUser.value); + // 先关闭,超市有扫码枪 BarcodeScanManage.instance.startListening(); - BarcodeScanManage.instance.scanStream.listen((event) async { + BarcodeScanManage.instance.qrcodeScanStream.listen((event) async { final serviceCode = await BarcodeScanManage.instance.getCodeInfo(event); if (serviceCode == null) return; if (serviceCode.exceedTime != -1) { @@ -181,26 +186,26 @@ class SelectPayTypeDialogController extends GetxController { ).millisecondsSinceEpoch; int nowTime = DateTime.now().millisecondsSinceEpoch; if (exceedTime <= nowTime) { - Toast.error(msg: "识别错误,二维码已过期"); + TtsManage.speak("二维码已过期"); return; } } - globalQrCode = serviceCode; - if (!_validateQrCodeType(orderPayType.value!)) { + if (!_validateQrCodeType(serviceCode.payType!)) { logger.e("|$_tag|二维码识别,无效的二维码json:|${jsonEncode(serviceCode)}|"); - Toast.error(msg: "识别错误,无效的二维码"); + TtsManage.speak("无效的二维码"); return; } + globalQrCode = serviceCode; + orderPayType.value = serviceCode.payType; logger.i("|$_tag|二维码识别,识别到的二维码json:|${jsonEncode(serviceCode)}|"); final isNetwork = AppNetworkTool.isAutoNetwork(); - final appSetting = await AppSettingTool.getSysSetting(); if (isNetwork) { final pa = { "id": serviceCode.userId, "type": appSetting.sysType.name, }; final data = await HardApi.getUserInfoById(pa); - if (data != null) throw Error(); + if (data == null) throw Exception("用户不存在"); payUser.value = data; if (payUser.value != null) { await SyncUserTool.storageUser(user: payUser.value!); @@ -215,13 +220,9 @@ class SelectPayTypeDialogController extends GetxController { } } isRecognition.value = false; - BarcodeScanManage.instance.dispose(); - Toast.success(msg: "识别二维码成功"); - TtsManage.speak("识别二维码成功"); }); - isRecognition.value = true; - onDiscernUser?.call(payUser.value); } catch (e) { + TtsManage.speak("识别二维码错误"); logger.e("|$_tag|二维码识别,失败error:|$e|"); } } diff --git a/lib/pages/setting/widgets/setting_baseInfo.dart b/lib/pages/setting/widgets/setting_baseInfo.dart index 6eab60c..cd40439 100644 --- a/lib/pages/setting/widgets/setting_baseInfo.dart +++ b/lib/pages/setting/widgets/setting_baseInfo.dart @@ -17,11 +17,11 @@ Future _bindServiceDialog(BuildContext context, SysSetting sys) async { } if (AppSysTool.isDev()) { // 正式环境-超市 - uniqueCodeC.text = "1894303392833716226"; + // uniqueCodeC.text = "1894303392833716226"; // 正式环境-测试 // uniqueCodeC.text = "1896157295523049473"; // 测试环境-测试设备 - // uniqueCodeC.text = "1877592554489143298"; + uniqueCodeC.text = "1877592554489143298"; } await showAdaptiveDialog( context: context, diff --git a/lib/pages/setting/widgets/setting_product.dart b/lib/pages/setting/widgets/setting_product.dart index 933c204..2382c42 100644 --- a/lib/pages/setting/widgets/setting_product.dart +++ b/lib/pages/setting/widgets/setting_product.dart @@ -274,7 +274,7 @@ Widget _buildSupermarketList(BuildContext context) { crossAxisCount: 6, mainAxisSpacing: 10.w, crossAxisSpacing: 10.w, - childAspectRatio: 0.7, + childAspectRatio: 0.9, ), itemCount: productC.supermarketSpuList.length, itemBuilder: (BuildContext context, int index) { diff --git a/lib/pages/supermarket_order/controller.dart b/lib/pages/supermarket_order/controller.dart index fa3e35d..74757ee 100644 --- a/lib/pages/supermarket_order/controller.dart +++ b/lib/pages/supermarket_order/controller.dart @@ -297,7 +297,7 @@ class SupermarketOrderController extends GetxController with OrderMixin { Future _listenBarCodeCallback() async { BarcodeScanManage.instance.startListening(); - BarcodeScanManage.instance.scanStream.listen((event) async { + BarcodeScanManage.instance.gunScanStream.listen((event) async { if (ObjectUtil.isEmpty(event)) return; if (event.length >= 30) return; // 根据条码查询商品 diff --git a/lib/pages/supermarket_order/view.dart b/lib/pages/supermarket_order/view.dart index 90c4b85..3078687 100644 --- a/lib/pages/supermarket_order/view.dart +++ b/lib/pages/supermarket_order/view.dart @@ -141,7 +141,7 @@ class SupermarketOrderPage extends GetView { double maxHeight = constraints.maxHeight; double maxWidth = constraints.maxWidth; double itemWidth = (maxWidth - spacing * 4) / 5; - double itemHeight = (maxHeight - (runSpacing + 30) * 2) / 2; + double itemHeight = (maxHeight - (runSpacing + 130) * 2) / 2; return Obx( () => ListView.builder( padding: EdgeInsets.zero, diff --git a/lib/pages/take_food/controller.dart b/lib/pages/take_food/controller.dart index 7500405..9f292e5 100644 --- a/lib/pages/take_food/controller.dart +++ b/lib/pages/take_food/controller.dart @@ -96,7 +96,7 @@ class TakeFoodController extends GetxController { try { // 监听二维码测试 BarcodeScanManage.instance.startListening(); - BarcodeScanManage.instance.scanStream.listen((event) async { + BarcodeScanManage.instance.qrcodeScanStream.listen((event) async { final serviceCode = await BarcodeScanManage.instance.getCodeInfo(event); if (serviceCode == null) return; if (serviceCode.payType != OrderPayType.RESERVE_QR_CODE) {