From 9a98ac8e1c4f2608b2c3fbdcaf7514831d68ff43 Mon Sep 17 00:00:00 2001 From: dzc <2388224167@qq.com> Date: Sun, 16 Mar 2025 21:35:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E9=A5=AD=E5=8D=A1=E6=BF=80=E6=B4=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/api/product_api.dart | 2 +- lib/common/api/wallet_api.dart | 4 +- .../models/database/database_enum_models.dart | 22 ++- lib/common/utils/sync/sync_face_tool.dart | 12 -- lib/common/utils/sync/sync_product_tool.dart | 8 +- lib/common/widgets/app/app_back.dart | 16 ++ .../widgets/app/app_message_dialog.dart | 33 ++-- lib/common/widgets/card/card_common.dart | 112 +++++++++++ lib/common/widgets/index.dart | 3 + lib/common/widgets/order/order_common.dart | 2 - lib/pages/canteen_product/view.dart | 12 +- lib/pages/card_active/controller.dart | 116 ++++++----- lib/pages/card_active/view.dart | 99 +++++++--- lib/pages/order_history/controller.dart | 2 +- lib/pages/setting/view.dart | 2 +- lib/pages/setting/widgets/setting_backup.dart | 7 - lib/pages/setting/widgets/setting_face.dart | 2 + .../setting/widgets/setting_product.dart | 33 +++- lib/pages/supermarket_order/controller.dart | 152 ++++++++++++++- lib/pages/supermarket_order/view.dart | 180 +++++++++++++++++- 20 files changed, 665 insertions(+), 154 deletions(-) create mode 100644 lib/common/widgets/app/app_back.dart create mode 100644 lib/common/widgets/card/card_common.dart diff --git a/lib/common/api/product_api.dart b/lib/common/api/product_api.dart index 0c61901..cc0c813 100644 --- a/lib/common/api/product_api.dart +++ b/lib/common/api/product_api.dart @@ -2,7 +2,7 @@ import 'package:pj_supermarket_cloud/common/index.dart'; abstract class ProductApi { /// 菜品分类 - static Future?> cookbookClassify( + static Future?> canteenClassify( dynamic pa, ) async { final res = await HttpManage().dio.get( diff --git a/lib/common/api/wallet_api.dart b/lib/common/api/wallet_api.dart index 0e11125..f3c4a63 100644 --- a/lib/common/api/wallet_api.dart +++ b/lib/common/api/wallet_api.dart @@ -24,7 +24,7 @@ abstract class WalletApi { /// 开卡成功 static Future openCardSuccess(dynamic pa) async { - final res = await HttpManage().dio.get( + final res = await HttpManage().dio.get( "/wallet/wallet-active/ad/open-card-success", queryParameters: pa, ); @@ -34,7 +34,7 @@ abstract class WalletApi { /// 重新激活 static Future refreshAvatar(dynamic pa) async { - final res = await HttpManage().dio.get( + final res = await HttpManage().dio.get( "/wallet/wallet-active/ad/refresh-avatar", queryParameters: pa, ); diff --git a/lib/common/models/database/database_enum_models.dart b/lib/common/models/database/database_enum_models.dart index c60d53e..b00140e 100644 --- a/lib/common/models/database/database_enum_models.dart +++ b/lib/common/models/database/database_enum_models.dart @@ -56,22 +56,28 @@ enum OrderPayType implements BaseEnum { /// 订单状态 enum OrderStatus implements BaseEnum { // 查询使用 - TOTAL("-1", "全部"), + TOTAL("-1", "全部", true), - CREATE("1", "未付款"), - OFFLINE_CREATE("2", "离线创建订单(未支付)"), - PAY("5", "已付款"), - OFFLINE_PAY("6", "本地支付"), - REFUND("10", "退款"), - CANCEL("20", "已核销"), + CREATE("1", "未付款", true), + OFFLINE_CREATE("2", "离线创建订单(未支付)", false), + PAY("5", "已付款", true), + OFFLINE_PAY("6", "本地支付", true), + REFUND("10", "退款", true), + CANCEL("20", "已核销", false), ; @override final String code; @override final String title; + @override + final bool visible; - const OrderStatus(this.code, this.title); + const OrderStatus(this.code, this.title, this.visible); + + static List visibleList() { + return OrderStatus.values.where((e) => e.visible).toList(); + } } // 打印状态 diff --git a/lib/common/utils/sync/sync_face_tool.dart b/lib/common/utils/sync/sync_face_tool.dart index b213f3e..6e0cd48 100644 --- a/lib/common/utils/sync/sync_face_tool.dart +++ b/lib/common/utils/sync/sync_face_tool.dart @@ -122,12 +122,6 @@ class SyncFaceTool { add.path = filePath; await faceDao.edit(add); } - // 重新生成人脸模型 - await AppFaceTool.removeFace(sampleId: add.sampleId); - await AppFaceTool.faceRegister( - userId: add.userId.toString(), - forceRegister: true, - ); } continue; } @@ -162,12 +156,6 @@ class SyncFaceTool { edit.path = filePath; } await faceDao.edit(edit); - // 重新生成人脸模型 - await AppFaceTool.removeFace(sampleId: edit.sampleId); - await AppFaceTool.faceRegister( - userId: edit.userId.toString(), - forceRegister: true, - ); } if (data.length >= size) return true; } catch (e, stack) { diff --git a/lib/common/utils/sync/sync_product_tool.dart b/lib/common/utils/sync/sync_product_tool.dart index 0d0b635..40a1573 100644 --- a/lib/common/utils/sync/sync_product_tool.dart +++ b/lib/common/utils/sync/sync_product_tool.dart @@ -1,5 +1,4 @@ import 'package:common_utils/common_utils.dart'; -import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; import 'package:pj_supermarket_cloud/common/index.dart'; @@ -10,13 +9,8 @@ abstract class SyncProductTool { static Future updateDatabase() async { final isNetwork = AppNetworkTool.isAutoNetwork(); if (!isNetwork) return; - EasyLoading.show( - status: '初始化资源...', - maskType: EasyLoadingMaskType.black, - ); await syncClassify(); await syncProduct(); - EasyLoading.dismiss(); } /// 同步商品分类 @@ -26,7 +20,7 @@ abstract class SyncProductTool { try { List? apiList; if (sysType == SysType.CANTEEN) { - apiList = await ProductApi.cookbookClassify(null); + apiList = await ProductApi.canteenClassify(null); } else if (sysType == SysType.SUPERMARKET) { apiList = await ProductApi.supermarketClassify(null); } diff --git a/lib/common/widgets/app/app_back.dart b/lib/common/widgets/app/app_back.dart new file mode 100644 index 0000000..5f3d8bc --- /dev/null +++ b/lib/common/widgets/app/app_back.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +import 'package:ducafe_ui_core/ducafe_ui_core.dart'; +import 'package:forui/forui.dart'; + +Widget appBack( + BuildContext context, { + required VoidCallback onTap, + double height = 90, +}) { + final theme = FTheme.of(context); + return FIcon( + FAssets.icons.chevronLeft, + size: theme.typography.xl4.fontSize, + ).alignLeft().width(height).onTap(() => onTap()); +} diff --git a/lib/common/widgets/app/app_message_dialog.dart b/lib/common/widgets/app/app_message_dialog.dart index 3d1307d..6823cd5 100644 --- a/lib/common/widgets/app/app_message_dialog.dart +++ b/lib/common/widgets/app/app_message_dialog.dart @@ -8,7 +8,7 @@ Future appMessageDialog(BuildContext context, String msg) async { final res = await showAdaptiveDialog( context: context, builder: (context) => FDialog( - direction: Axis.horizontal, + direction: Axis.vertical, title: [ FIcon( FAssets.icons.circleAlert, @@ -17,25 +17,30 @@ Future appMessageDialog(BuildContext context, String msg) async { ).paddingOnly(top: 5.h), Text("警告", style: theme.typography.xl).paddingOnly(left: 10.w), ].toRow(crossAxisAlignment: CrossAxisAlignment.center), - body: Text(msg, style: theme.typography.base) - .width(context.width * 0.5) + body: Text(msg, style: theme.typography.xl) + .width(context.width * 0.6) + .padding(vertical: 50.w) .marginSymmetric( vertical: 10.h, ), actions: [ - FButton( + [ + FButton( + label: Text( + '确定', + style: theme.typography.base.copyWith( + color: theme.colorScheme.secondary, + ), + ), + onPress: () => Navigator.of(context).pop(true), + ).flexible(flex: 1), + SizedBox(width: 10.w), + FButton( style: FButtonStyle.outline, label: Text('取消', style: theme.typography.base), - onPress: () => Navigator.of(context).pop(false)), - FButton( - label: Text( - '确定', - style: theme.typography.base.copyWith( - color: theme.colorScheme.secondary, - ), - ), - onPress: () => Navigator.of(context).pop(true), - ), + onPress: () => Navigator.of(context).pop(false), + ).flexible(flex: 1), + ].toRow(), ], ), ); diff --git a/lib/common/widgets/card/card_common.dart b/lib/common/widgets/card/card_common.dart new file mode 100644 index 0000000..9d5549d --- /dev/null +++ b/lib/common/widgets/card/card_common.dart @@ -0,0 +1,112 @@ +import 'package:common_utils/common_utils.dart'; +import 'package:ducafe_ui_core/ducafe_ui_core.dart'; +import 'package:flutter/material.dart'; +import 'package:forui/forui.dart'; +import 'package:get/get.dart'; + +import 'package:pj_supermarket_cloud/common/index.dart'; + +Widget readCardEmpty( + BuildContext context, { + required bool loading, +}) { + final theme = FTheme.of(context); + if (loading) { + return [ + CircularProgressIndicator( + color: theme.colorScheme.primary, + ).width(100.w).height(100.w).center(), + SizedBox(height: 10.h), + Text( + "扫描中", + style: theme.typography.base.copyWith( + color: theme.colorScheme.primary, + ), + ), + ].toColumn(mainAxisAlignment: MainAxisAlignment.center); + } + return [ + FIcon( + FAssets.icons.idCard, + size: theme.typography.xl4.fontSize, + ), + SizedBox(height: 10.h), + Text( + "请让用户放置饭卡", + style: theme.typography.base.copyWith( + color: theme.colorScheme.primary, + ), + ), + ].toColumn(mainAxisAlignment: MainAxisAlignment.center); +} + +Future cardDialog({ + required String title, + required Rx loading, + required VoidCallback doSubmit, + required Rx detail, +}) async { + showDialog( + context: Get.context!, + barrierDismissible: false, + builder: (BuildContext context) { + final width = context.width; + final height = context.height; + final theme = FTheme.of(context); + final textStyle = theme.typography.xl; + final titleTheme = theme.tileGroupStyle.tileStyle.copyWith( + border: Border.all( + color: Colors.transparent, + ), + ); + return FDialog( + direction: Axis.vertical, + title: Text(title, style: theme.typography.lg), + body: [ + Obx( + () => Visibility( + visible: detail.value == null, + replacement: [ + FTile( + style: titleTheme, + prefixIcon: FIcon(FAssets.icons.user), + title: Text('姓名', style: textStyle), + details: Text( + "${detail.value?.username}", + style: textStyle, + ), + ), + FTile( + style: titleTheme, + prefixIcon: FIcon(FAssets.icons.badgeDollarSign), + title: Text('卡号', style: textStyle), + details: Text( + "${detail.value?.cardNumber}", + style: textStyle, + ), + ), + ].toColumn( + mainAxisAlignment: MainAxisAlignment.center, + ), + child: readCardEmpty( + context, + loading: loading.value, + ), + ), + ).width(width * 0.5).height(height * 0.5), + ].toColumn(), + actions: [ + FButton( + label: Text( + "确定", + style: theme.typography.xl.copyWith( + color: theme.colorScheme.secondary, + ), + ), + onPress: () => doSubmit(), + ).flexible(flex: 1), + ], + ); + }, + ); +} diff --git a/lib/common/widgets/index.dart b/lib/common/widgets/index.dart index 56f5b28..dcd03d8 100644 --- a/lib/common/widgets/index.dart +++ b/lib/common/widgets/index.dart @@ -2,6 +2,7 @@ library; export './toast/toast_widget.dart'; +export './app/app_back.dart'; export './app/app_check_pay_type_dialog.dart'; export './app/app_date_picker.dart'; export './app/app_divider_title.dart'; @@ -14,6 +15,8 @@ export './app/app_price.dart'; export './app/app_radio_hour_type_dialog.dart'; export './app/app_title.dart'; +export './card/card_common.dart'; + export './order/mixin/order_mixin.dart'; export './order/select_pay_type_dialog.dart'; export './order/steelyard_container.dart'; diff --git a/lib/common/widgets/order/order_common.dart b/lib/common/widgets/order/order_common.dart index 9ba84de..8a0482d 100644 --- a/lib/common/widgets/order/order_common.dart +++ b/lib/common/widgets/order/order_common.dart @@ -1,5 +1,3 @@ -import 'dart:ffi'; - import 'package:ducafe_ui_core/ducafe_ui_core.dart'; import 'package:flutter/material.dart'; import 'package:forui/forui.dart'; diff --git a/lib/pages/canteen_product/view.dart b/lib/pages/canteen_product/view.dart index 246fcc3..6751fef 100644 --- a/lib/pages/canteen_product/view.dart +++ b/lib/pages/canteen_product/view.dart @@ -17,13 +17,11 @@ class CanteenProductPage extends GetView { final context = Get.context!; final theme = FTheme.of(context); return [ - FIcon( - FAssets.icons.chevronLeft, - size: theme.typography.xl4.fontSize, - ) - .alignLeft() - .width(headerHeight) - .onTap(() => Get.offAndToNamed(RouteNames.panel)), + appBack( + context, + height: headerHeight, + onTap: () => Get.offAndToNamed(RouteNames.panel), + ), Obx( () => orderRowClassify( context, diff --git a/lib/pages/card_active/controller.dart b/lib/pages/card_active/controller.dart index 012ec08..18c3019 100644 --- a/lib/pages/card_active/controller.dart +++ b/lib/pages/card_active/controller.dart @@ -19,8 +19,7 @@ class CardActiveController extends GetxController { RxList tableList = [].obs; - // 读卡器 - RxString readCardNumber = "未读取".obs; + Rx detail = Rx(null); RxBool readLoading = false.obs; @@ -54,20 +53,28 @@ class CardActiveController extends GetxController { } Future getTableList() async { - final appSetting = await AppSettingTool.getSysSetting(); - final deptId = appSetting.merchantDeptId; - final pa = { - "current": currentPage.value, - "size": pageSize.value, - "deptId": "", - "walletTypeId": "", - }; - final data = await WalletApi.openCardList(pa); - if (data == null) return; - totalPage.value = data.total; - final records = data.records; - if (records != null) { - tableList.value = records; + EasyLoading.show( + status: '查询中...', + maskType: EasyLoadingMaskType.black, + ); + try { + final appSetting = await AppSettingTool.getSysSetting(); + final deptId = appSetting.merchantDeptId; + final pa = { + "current": currentPage.value, + "size": pageSize.value, + "deptId": "", + "walletTypeId": "", + }; + final data = await WalletApi.openCardList(pa); + if (data == null) return; + totalPage.value = data.total; + final records = data.records; + if (records != null) { + tableList.value = records; + } + } finally { + EasyLoading.dismiss(); } } @@ -88,28 +95,26 @@ class CardActiveController extends GetxController { return; } writeLoading.value = true; - // writeCardDialog( - // context: Get.context!, - // cardNumber: v.cardNumber!, - // doCancel: () { - // Get.back(); - // serialCardManage.readerClose(); - // serialCardManage.close(); - // _closeWriteStatus(); - // }, - // ); + detail.value = null; + final context = Get.context!; + cardDialog( + title: "写卡", + loading: readLoading, + detail: detail, + doSubmit: () { + Navigator.of(context).pop(); + _closeStatus(); + }, + ); serialCardManage.callback = (type, status, res) async { switch (status) { case SerialCardResStatus.success: - Toast.success(msg: "写入成功"); - serialCardManage.readerClose(); - serialCardManage.close(); if (ObjectUtil.isNotEmpty(v.id)) { await _getOpenCardSuccess(v.id!); - currentPage.value = 1; getTableList(); } - Navigator.of(Get.context!).pop(); + detail.value = v; + _closeStatus(); break; case SerialCardResStatus.warning: break; @@ -122,13 +127,15 @@ class CardActiveController extends GetxController { TtsManage.speak("请在合适区域放置饭卡"); serialCardManage.sendWrite(v.cardNumber!); } catch (e, stack) { + _closeStatus(); logger.d("写卡失败error:|$e|stack:|$stack|"); Toast.error(msg: "写入失败,请重试"); } } // 读卡 - void doSelectHorType() async { + void doReadCard() async { + final context = Get.context!; try { await serialCardManage.connect(); if (!serialCardManage.isConnect) { @@ -136,23 +143,26 @@ class CardActiveController extends GetxController { return; } readLoading.value = true; - // readCardDialog( - // context: Get.context!, - // doCancel: () { - // serialCardManage.readerClose(); - // serialCardManage.close(); - // _closeReadStatus(); - // Get.back(); - // }, - // ); - serialCardManage.callback = (type, status, res) { + detail.value = null; + cardDialog( + title: "读卡", + loading: readLoading, + detail: detail, + doSubmit: () { + Navigator.of(context).pop(); + _closeStatus(); + }, + ); + serialCardManage.callback = (type, status, res) async { switch (status) { case SerialCardResStatus.success: Toast.success(msg: "读取成功"); - readLoading.value = false; - readCardNumber.value = res; - serialCardManage.readerClose(); - serialCardManage.close(); + final pa = {"cardNumber": res}; + final data = await WalletApi.openCardList(pa); + if (data != null && data.records != null) { + detail.value = data.records![0]; + } + _closeStatus(); break; case SerialCardResStatus.warning: break; @@ -165,7 +175,7 @@ class CardActiveController extends GetxController { TtsManage.speak("请在合适区域放置饭卡"); serialCardManage.sendRead(); } catch (e, stack) { - _closeReadStatus(); + _closeStatus(); logger.d("读卡失败error:|$e|stack:|$stack|"); Toast.error(msg: "读取失败,请重试"); } @@ -173,7 +183,10 @@ class CardActiveController extends GetxController { Future _getOpenCardSuccess(String id) async { try { - EasyLoading.show(status: '激活饭卡中...'); + EasyLoading.show( + status: '激活饭卡中...', + maskType: EasyLoadingMaskType.black, + ); final pa = {"id": id}; final data = await WalletApi.openCardSuccess(pa); if (data == false) throw Error(); @@ -199,14 +212,17 @@ class CardActiveController extends GetxController { 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 _closeReadStatus() { + void _closeStatus() { + serialCardManage.readerClose(); + serialCardManage.close(); readLoading.value = false; - readCardNumber.value = "未读取"; } } diff --git a/lib/pages/card_active/view.dart b/lib/pages/card_active/view.dart index 917059c..3db7488 100644 --- a/lib/pages/card_active/view.dart +++ b/lib/pages/card_active/view.dart @@ -11,10 +11,50 @@ import 'index.dart'; class CardActivePage extends GetView { const CardActivePage({super.key}); - // 主视图 - Widget _buildView(BuildContext context) { + // 条件 + Widget _buildHeader(double height) { + final context = Get.context!; + final theme = FTheme.of(context); + return [ + appBack( + context, + height: height, + onTap: () => Get.offAndToNamed(RouteNames.panel), + ), + Spacer(), + FButton( + prefix: FIcon( + FAssets.icons.idCard, + size: theme.typography.base.fontSize, + ), + label: Text( + '读卡', + style: theme.typography.base, + ), + style: FButtonStyle.secondary, + onPress: () => controller.doReadCard(), + ), + SizedBox(width: 20.w), + FButton( + prefix: FIcon( + FAssets.icons.search, + size: theme.typography.base.fontSize, + ), + label: Text( + '搜索', + style: theme.typography.base.copyWith( + color: theme.colorScheme.secondary, + ), + ), + onPress: () => controller.doPage(), + ), + ].toRow(); + } + + // 主视图 + Widget _buildView() { + final context = Get.context!; return [ - _condition(context), _buildTable(context).expanded(), Obx(() => appPage( context, @@ -26,11 +66,6 @@ class CardActivePage extends GetView { ].toColumn(); } - // 条件 - Widget _condition(BuildContext context) { - return const Center(); - } - // 主视图 Widget _buildTable(BuildContext context) { final theme = FTheme.of(context); @@ -133,12 +168,28 @@ class CardActivePage extends GetView { ).center(), ), DataCell( - Text( - WalletType.values - .fromCode(v.walletTypeUniqueCode) - ?.title ?? - "未知", - textAlign: TextAlign.center, + Visibility( + visible: v.isActiveHardware == false, + replacement: FButton( + style: FButtonStyle.destructive, + prefix: FIcon(FAssets.icons.refreshCcw), + label: Text( + '重新激活', + style: theme.typography.base.copyWith( + color: theme.colorScheme.secondary, + ), + ), + onPress: () => controller.doRefreshAvatar(v), + ), + child: FButton( + style: FButtonStyle.secondary, + prefix: FIcon(FAssets.icons.folderOpenDot), + label: Text( + '激活', + style: theme.typography.base, + ), + onPress: () => controller.doWriteCard(v), + ), ).center(), ), ], @@ -151,26 +202,16 @@ class CardActivePage extends GetView { @override Widget build(BuildContext context) { - final theme = FTheme.of(context); - final height = context.height; + final height = 90; return GetBuilder( init: CardActiveController(), id: "card_active", builder: (_) { return FScaffold( - header: FHeader.nested( - title: Text("开卡管理"), - prefixActions: [ - FHeaderAction.back(onPress: Get.back), - ], - style: theme.headerStyle.nestedStyle.copyWith( - titleTextStyle: theme.typography.lg, - actionStyle: theme.headerStyle.nestedStyle.actionStyle.copyWith( - size: theme.typography.xl2.fontSize, - ), - ), - ).height(height * 0.1), - content: _buildView(context), + header: _buildHeader(height.h) + .height(height.h) + .padding(vertical: 30.h, horizontal: 10.w), + content: _buildView(), resizeToAvoidBottomInset: false, ); }, diff --git a/lib/pages/order_history/controller.dart b/lib/pages/order_history/controller.dart index 13b40db..24fdc67 100644 --- a/lib/pages/order_history/controller.dart +++ b/lib/pages/order_history/controller.dart @@ -137,7 +137,7 @@ class OrderHistoryController extends GetxController { Future getTotalList() async { final total = await _getTotal(); - List tabs = OrderStatus.values + List tabs = OrderStatus.visibleList() .map((v) => OrderHistoryTableItem(status: v)) .toList(); for (int i = 0; i < tabs.length; i++) { diff --git a/lib/pages/setting/view.dart b/lib/pages/setting/view.dart index d1c6409..b90b5a8 100644 --- a/lib/pages/setting/view.dart +++ b/lib/pages/setting/view.dart @@ -26,7 +26,7 @@ class SettingPage extends GetView { ), ), Obx(() => _buildDetail(context, controller.cuurentMenuType.value)) - .marginOnly(left: 20.w) + .marginSymmetric(horizontal: 10.w) .expanded(), ].toRow().height(double.infinity); } diff --git a/lib/pages/setting/widgets/setting_backup.dart b/lib/pages/setting/widgets/setting_backup.dart index 4557cab..9492253 100644 --- a/lib/pages/setting/widgets/setting_backup.dart +++ b/lib/pages/setting/widgets/setting_backup.dart @@ -33,13 +33,6 @@ Widget settingBackup(BuildContext context) { title: appPrefixTitle(context, "log备份"), onPress: () => controller.backupsLog(), ), - appDividerTitle(context, "恢复"), - FTile( - prefixIcon: appPrefixIcon(context, FAssets.icons.user), - suffixIcon: appSuffixIcon(context, FAssets.icons.folderSync), - title: appPrefixTitle(context, "sql恢复"), - onPress: () => controller.recoverSql(), - ), ].toColumnSpace(space: 20.h); }); }); diff --git a/lib/pages/setting/widgets/setting_face.dart b/lib/pages/setting/widgets/setting_face.dart index dd34f67..bee631d 100644 --- a/lib/pages/setting/widgets/setting_face.dart +++ b/lib/pages/setting/widgets/setting_face.dart @@ -104,6 +104,8 @@ class FaceManageController extends GetxController { } finally { currentPage.value = 1; getFaceList(); + // 重新生成人脸模型 + await AppFaceTool.removeFace(sampleId: "-1"); await AppFaceTool.faceRegister(forceRegister: true); Toast.success(msg: "同步成功"); EasyLoading.dismiss(); diff --git a/lib/pages/setting/widgets/setting_product.dart b/lib/pages/setting/widgets/setting_product.dart index 4c716cc..e8179ec 100644 --- a/lib/pages/setting/widgets/setting_product.dart +++ b/lib/pages/setting/widgets/setting_product.dart @@ -47,11 +47,27 @@ class ProductManageController extends GetxController { Future initData() async { await getProductList(); if (ObjectUtil.isEmptyList(productList)) { - await SyncProductTool.updateDatabase(); getProductList(); } } + // 更新商品和分类 + Future updateProductAndClassify() async { + EasyLoading.show( + status: '更新中...', + maskType: EasyLoadingMaskType.black, + ); + try { + await SyncProductTool.updateDatabase(); + await getProductList(); + } finally { + Future.delayed(const Duration(seconds: 1), () { + EasyLoading.dismiss(); + }); + } + } + + // 获取商品列表 Future getProductList({bool isLoading = false}) async { if (isLoading) { EasyLoading.show( @@ -60,7 +76,6 @@ class ProductManageController extends GetxController { ); } try { - await SyncProductTool.updateDatabase(); final app = Get.find(); final sysSetting = await AppSettingTool.getSysSetting(); final productDao = app.dataBase.getProductDao; @@ -134,6 +149,10 @@ Widget buildSearch(BuildContext context) { ).width(300.w), ].toRow().expanded(), FButton( + prefix: FIcon( + FAssets.icons.search, + size: theme.typography.base.fontSize, + ), label: Text( '搜索', style: theme.typography.base.copyWith( @@ -142,6 +161,16 @@ Widget buildSearch(BuildContext context) { ), onPress: () => productC.doPage(), ), + SizedBox(width: 20.w), + FButton( + label: Text( + '更新商品和分类', + style: theme.typography.base.copyWith( + color: theme.colorScheme.secondary, + ), + ), + onPress: () => productC.updateProductAndClassify(), + ), ].toRow(); } diff --git a/lib/pages/supermarket_order/controller.dart b/lib/pages/supermarket_order/controller.dart index 23720d1..4d08d8e 100644 --- a/lib/pages/supermarket_order/controller.dart +++ b/lib/pages/supermarket_order/controller.dart @@ -1,3 +1,6 @@ +import 'package:common_utils/common_utils.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_subscreen_plugin/flutter_subscreen_plugin.dart'; import 'package:get/get.dart'; import 'package:pj_supermarket_cloud/common/index.dart'; @@ -5,6 +8,16 @@ import 'package:pj_supermarket_cloud/common/index.dart'; class SupermarketOrderController extends GetxController with OrderMixin { SupermarketOrderController(); + static const _tag = "食堂购买页面"; + + final appC = Get.find(); + + late SysSetting appSetting; + + RxInt currentPage = RxInt(1); + RxInt totalPage = RxInt(0); + RxInt pageSize = RxInt(24); + _initData() { update(["supermarket_order"]); } @@ -12,8 +25,9 @@ class SupermarketOrderController extends GetxController with OrderMixin { void onTap() {} @override - void onInit() { + void onInit() async { super.onInit(); + await _initState(); } @override @@ -25,5 +39,141 @@ class SupermarketOrderController extends GetxController with OrderMixin { @override void onClose() { super.onClose(); + SubScreenPlugin.sendMsgToViceScreen(SubScreenAction.jumpSubBack.name); + // 读卡器 + appC.serialCardManage.readerClose(); + appC.serialCardManage.close(); + } + + Future _initState() async { + EasyLoading.show( + status: '初始化资源...', + maskType: EasyLoadingMaskType.black, + ); + appSetting = await AppSettingTool.getSysSetting(); + // 打开副屏 + SubScreenPlugin.sendMsgToViceScreen(SubScreenAction.jumpSubPage.name); + // 组件初始化 + sysType = SysType.SUPERMARKET; + _listenPayTypeSelectCallback(); + // 获取分类和商品 + await getClassify(); + await getProduct(); + // 清空购物车 + await _clearShopCard(); + EasyLoading.dismiss(); + } + + // |下单|点击购买 + Future doPay() async { + final validShopCar = _validateShopCar(); + if (!validShopCar) return; + // 创建本地订单 + await createSqlOrder(); + selectPayTypeDialog(); + } + + // |下单|点击取消下单 + Future doCancelPay({bool cancelShop = true}) async { + if (cancelShop) await _clearShopCard(); + sendSubScreen(); + } + + // 分页 + Future doPage({int? page}) async { + if (page != null) currentPage.value = page; + } + + void _listenPayTypeSelectCallback() { + final payTypeSelect = Get.find(); + // |支付方式|点击|开始识别| + payTypeSelect.onRecognition = () async {}; + // 识别出用户的回调 + payTypeSelect.onDiscernUser = (payUser) { + if (payUser == null) return; + this.payUser.value = payUser; + sendSubScreen(); + }; + // |支付方式|点击|确定| + payTypeSelect.onSubmit = ( + user, + orderType, + orderPayType, + orderPayDisplayType, + qrCode, + ) async { + final log = + "|$_tag|回调,识别到用户:|${user.toJson()}|orderType:|$orderType|;orderPayType:|$orderPayType|;orderPayDisplayType|$orderPayDisplayType|;"; + logger.i(log); + this.orderType = orderType; + this.orderPayType.value = orderPayType; + this.orderPayDisplayType.value = orderPayDisplayType; + this.globalQrCode = qrCode; + payUser.value = user; + final validPayUser = _validatePayUser(); + if (!validPayUser) return; + final validShopCar = _validateShopCar(); + if (!validShopCar) return; + EasyLoading.show( + status: '支付中...', + maskType: EasyLoadingMaskType.black, + ); + // 最后计算一次额外的价格 + await _computedAmountMealPrice(); + await updateSqlOrder(); + await createApiOrder(); + _clearShopCard(); + putOffSendSubScreen(); + }; + // |支付方式|点击|取消| + payTypeSelect.onCancel = () {}; + } + + // 计算额外的价格 + Future _computedAmountMealPrice() async { + final price = await getAmountMealPrice( + price: totalPrice.value, + ); + if (ObjectUtil.isNotEmpty(price)) { + amountMealPrice.value = int.parse(price!.toString()); + sendSubScreen(); + } + } + + Future _clearShopCard() async { + shopCardList.clear(); + payUser.value = null; + totalPrice.value = 0; + amountMealPrice.value = 0; + } + + bool _validatePayUser() { + bool flag = true; + if (ObjectUtil.isEmpty(payUser.value?.walletBalance)) { + TtsManage.speak("用户钱包不存在"); + Toast.error(msg: "校验购物车失败,用户钱包不存在"); + return false; + } + if (payUser.value!.walletBalance! < totalPrice.value) { + TtsManage.speak("用户余额不足"); + Toast.error(msg: "校验购物车失败,用户余额不足"); + return false; + } + return flag; + } + + bool _validateShopCar() { + bool flag = false; + for (final v in shopCardList) { + if (v.productNumber > 0) { + flag = true; + break; + } + } + if (!flag) { + Toast.error(msg: "校验购物车失败,未选择商品"); + return false; + } + return flag; } } diff --git a/lib/pages/supermarket_order/view.dart b/lib/pages/supermarket_order/view.dart index 414ec98..94d0928 100644 --- a/lib/pages/supermarket_order/view.dart +++ b/lib/pages/supermarket_order/view.dart @@ -1,3 +1,4 @@ +import 'package:common_utils/common_utils.dart'; import 'package:ducafe_ui_core/ducafe_ui_core.dart'; import 'package:flutter/material.dart'; import 'package:forui/forui.dart'; @@ -10,18 +11,20 @@ import 'index.dart'; class SupermarketOrderPage extends GetView { const SupermarketOrderPage({super.key}); + // ************************************************************************** + // 头部 start + // ************************************************************************** + // 头部 Widget _buildHeader(double headerHeight) { final context = Get.context!; final theme = FTheme.of(context); return [ - FIcon( - FAssets.icons.chevronLeft, - size: theme.typography.xl4.fontSize, - ) - .alignLeft() - .width(headerHeight) - .onTap(() => Get.offAndToNamed(RouteNames.panel)), + appBack( + context, + height: headerHeight, + onTap: () => Get.offAndToNamed(RouteNames.panel), + ), Obx( () => orderRowClassify( context, @@ -33,11 +36,168 @@ class SupermarketOrderPage extends GetView { ].toRow(); } + // ************************************************************************** + // 头部 end + // ************************************************************************** + + // ************************************************************************** + // 商品 start + // ************************************************************************** + + Widget _buildProductItem( + BuildContext context, { + required List productList, + required double productSpacing, + required Size productSize, + String? title = "", + }) { + return [].toColumn(); + } + + Widget _buildProduct() { + return LayoutBuilder( + builder: (BuildContext _, BoxConstraints constraints) { + const spacing = 10; + const runSpacing = 10; + double maxHeight = constraints.maxHeight; + double maxWidth = constraints.maxWidth; + double itemWidth = (maxWidth - spacing * 5) / 6; + double itemHeight = (maxHeight - (runSpacing + 30) * 3) / 3; + return ListView.builder( + padding: EdgeInsets.zero, + itemCount: controller.productClassifyList.length, + itemBuilder: (context, index) { + if (ObjectUtil.isEmpty(controller.productClassifyList) || + ObjectUtil.isEmpty(controller.productList)) { + return const SizedBox.shrink(); + } + final current = controller.productClassifyList[index]; + final currentStr = controller.classifyList + .firstWhereOrNull((v) => v.serviceUniqueCode == current); + final currentList = controller.productList + .where((i) => i.classifyServiceUniqueCode == current) + .toList(); + if (ObjectUtil.isEmpty(currentList)) { + return const SizedBox.shrink(); + } + return _buildProductItem( + context, + productList: currentList, + productSpacing: spacing.toDouble(), + productSize: Size(itemWidth, itemHeight), + title: currentStr?.name, + ); + }, + ); + }, + ); + } + + Widget _buildProductArea() { + final context = Get.context!; + return [ + _buildProduct().expanded(), + Obx( + () => appPage( + context, + pageNo: controller.currentPage.value, + pageSize: controller.pageSize.value, + total: controller.totalPage.value, + onPageChanged: (pageNo) => controller.doPage(page: pageNo), + ), + ), + ].toColumn(); + } + + // ************************************************************************** + // 商品 end + // ************************************************************************** + + // ************************************************************************** + // 用户 end + // ************************************************************************** + + Widget _buildUser() { + final theme = FTheme.of(Get.context!); + return [ + [ + Obx(() { + final payUser = controller.payUser.value; + return appUserInfo( + Get.context!, + username: payUser?.username, + balance: payUser?.walletBalance, + avatar: payUser?.userAvatar, + ); + }).width(400.w).center(), + SizedBox(height: 10.h), + FDivider( + style: theme.dividerStyles.verticalStyle.copyWith( + color: theme.colorScheme.primary, + padding: EdgeInsets.zero, + ), + ), + Text( + "已选商品", + style: theme.typography.xl2, + ).alignLeft(), + ].toColumn(), + Spacer(), + [ + Obx( + () => orderAllPrice( + Get.context!, + totalPrice: controller.totalPrice.value, + amountMealPrice: controller.amountMealPrice.value, + ), + ), + SizedBox(height: 20.h), + [ + FButton( + label: Text( + "支付", + style: theme.typography.xl.copyWith( + color: theme.colorScheme.secondary, + ), + ), + onPress: () => controller.doPay(), + ).flexible(flex: 1), + SizedBox(width: 20.w), + FButton( + style: FButtonStyle.destructive, + label: Text( + "取消", + style: theme.typography.xl.copyWith( + color: theme.colorScheme.secondary, + ), + ), + onPress: () => controller.doCancelPay(), + ).flexible(flex: 1), + ].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween), + ].toColumn(), + ].toColumn().padding(vertical: 10.h, horizontal: 10.w); + } + + // ************************************************************************** + // 用户 end + // ************************************************************************** + // 主视图 Widget _buildView(BuildContext context) { - return const Center( - child: Text("SupermarketOrderPage"), - ); + final context = Get.context!; + final width = context.width; + final theme = FTheme.of(context); + return [ + _buildProductArea().expanded(), + SizedBox(width: 15.w), + _buildUser().width(width * 0.25).decorated( + border: Border.all( + color: theme.colorScheme.primary, + width: 1, + ), + borderRadius: theme.style.borderRadius, + ), + ].toRow().marginOnly(bottom: 20.h); } @override