diff --git a/lib/common/api/hard_api.dart b/lib/common/api/hard_api.dart index 7e32374..7a28cf7 100644 --- a/lib/common/api/hard_api.dart +++ b/lib/common/api/hard_api.dart @@ -70,29 +70,25 @@ abstract class HardApi { } /// 获取订单详情 - static Future getOrder(dynamic formData) async { - final res = await HttpManage().dio.get( + static Future getOrder(dynamic formData) async { + final res = await HttpManage().dio.get( "/hard/hard-api/ad/get-order", queryParameters: formData, ); - return res.data as ServiceBase; + if (res.data == null) return null; + return ServiceHardOrder.fromJson(res.data); } /// 获取订单购物车 - static Future> getCarByOrderId( + static Future?> getCarByOrderId( dynamic formData, ) async { final res = await HttpManage().dio.get( "/hard/hard-api/ad/get-by-order-id", queryParameters: formData, ); - if (res.data?.data == null) { - return res.data!; - } - final typeData = - res.data?.data?.map((e) => ServiceCar.fromJson(e)).toList(); - res.data?.data = typeData; - return res.data!; + if (res.data == null) return null; + return res.data?.map((e) => ServiceCar.fromJson(e)).toList(); } /// 核销订单 diff --git a/lib/common/api/user_api.dart b/lib/common/api/user_api.dart index 2b2af8d..3c3e20c 100644 --- a/lib/common/api/user_api.dart +++ b/lib/common/api/user_api.dart @@ -44,20 +44,16 @@ abstract class UserApi { } /// 获取人脸 - static Future> getFaceList( + static Future?> getFaceList( dynamic formData, ) async { final res = await HttpManage().dio.get( "/admin/user-face/ad/find-face", queryParameters: formData, ); - if (res.data?.data == null) { - return res.data; - } - final typeData = res.data?.data + if (res.data == null) return null; + return res.data ?.map((e) => ServiceAsyncFace.fromJson(e)) .toList(); - res.data?.data = typeData; - return res.data; } } diff --git a/lib/common/api/wallet_api.dart b/lib/common/api/wallet_api.dart index 3c56499..0e11125 100644 --- a/lib/common/api/wallet_api.dart +++ b/lib/common/api/wallet_api.dart @@ -11,13 +11,15 @@ abstract class WalletApi { } /// 开卡列表 - static Future openCardList(dynamic pa) async { + static Future openCardList( + dynamic pa, + ) async { final res = await HttpManage().dio.get( "/wallet/wallet-active/ad/open-card-list", queryParameters: pa, ); if (res.data == null) return null; - return res.data as ServiceBase; + return ServiceOpenCardPage.fromJson(res.data); } /// 开卡成功 diff --git a/lib/common/models/api/base_api_models.dart b/lib/common/models/api/base_api_models.dart index 373ca54..fcdab14 100644 --- a/lib/common/models/api/base_api_models.dart +++ b/lib/common/models/api/base_api_models.dart @@ -21,20 +21,21 @@ class ServiceBase { } @JsonSerializable() -class ServiceListBase { - String? msg; - int? code; - @JsonKey(includeFromJson: false, includeToJson: false) - List? data; +class ServicePageBase { + int current; + int pages; + int size; + int total; - ServiceListBase({ - this.msg, - this.code, - this.data, + ServicePageBase({ + this.current = 1, + this.pages = 1, + this.size = 10, + this.total = 0, }); - factory ServiceListBase.fromJson(Map json) => - _$ServiceListBaseFromJson(json); + factory ServicePageBase.fromJson(Map json) => + _$ServicePageBaseFromJson(json); - Map toJson() => _$ServiceListBaseToJson(this); + Map toJson() => _$ServicePageBaseToJson(this); } diff --git a/lib/common/models/api/base_api_models.g.dart b/lib/common/models/api/base_api_models.g.dart index 7625156..bcc04e7 100644 --- a/lib/common/models/api/base_api_models.g.dart +++ b/lib/common/models/api/base_api_models.g.dart @@ -20,14 +20,18 @@ Map _$ServiceBaseToJson(ServiceBase instance) => 'data': instance.data, }; -ServiceListBase _$ServiceListBaseFromJson(Map json) => - ServiceListBase( - msg: json['msg'] as String?, - code: (json['code'] as num?)?.toInt(), +ServicePageBase _$ServicePageBaseFromJson(Map json) => + ServicePageBase( + current: (json['current'] as num?)?.toInt() ?? 1, + pages: (json['pages'] as num?)?.toInt() ?? 1, + size: (json['size'] as num?)?.toInt() ?? 10, + total: (json['total'] as num?)?.toInt() ?? 0, ); -Map _$ServiceListBaseToJson(ServiceListBase instance) => +Map _$ServicePageBaseToJson(ServicePageBase instance) => { - 'msg': instance.msg, - 'code': instance.code, + 'current': instance.current, + 'pages': instance.pages, + 'size': instance.size, + 'total': instance.total, }; diff --git a/lib/common/models/api/hard_api_models.dart b/lib/common/models/api/hard_api_models.dart index a1bcab4..4ad059a 100644 --- a/lib/common/models/api/hard_api_models.dart +++ b/lib/common/models/api/hard_api_models.dart @@ -201,3 +201,110 @@ class ServiceQRCode { Map toJson() => _$ServiceQRCodeToJson(this); } + +@JsonSerializable() +class ServiceHardOrder { + /// 用户名 + final String? username; + + /// 用户手机号 + final String? userMobile; + + /// 用户头像 + final String? userAvatar; + + ServiceHardOrder({ + this.username, + this.userMobile, + this.userAvatar, + }); + + factory ServiceHardOrder.fromJson(Map json) => + _$ServiceHardOrderFromJson(json); + + Map toJson() => _$ServiceHardOrderToJson(this); +} + +@JsonSerializable() +class ServiceOpenCard { + /// 用户名 + String? username; + + /// 用户手机号 + String? mobile; + + /// 用户头像 + String? avatar; + + /// 饭卡状态 + String? cardStatus; + + /// 编号 + String? id; + + /// 部门名 + String? deptId; + + /// 部门名 + String? deptName; + + /// 用户编号 + String? userId; + + /// 饭卡类型Id + String? walletTypeId; + + /// 饭卡类型 + String? walletTypeUniqueCode; + + /// 饭卡图标 + String? walletTypeIcon; + + /// 用户卡号 + String? cardNumber; + + /// 钱包编号 + String? walletId; + + /// 是否激活 + @JsonKey(fromJson: JsonTool.stringToBoolean, toJson: JsonTool.booleanToString) + bool? isActiveHardware; + + ServiceOpenCard({ + this.username, + this.mobile, + this.avatar, + this.cardStatus, + this.id, + this.deptId, + this.deptName, + this.userId, + this.walletTypeId, + this.walletTypeUniqueCode, + this.walletTypeIcon, + this.cardNumber, + this.walletId, + this.isActiveHardware, + }); + + factory ServiceOpenCard.fromJson(Map json) => + _$ServiceOpenCardFromJson(json); + + Map toJson() => _$ServiceOpenCardToJson(this); +} + +@JsonSerializable() +class ServiceOpenCardPage extends ServicePageBase { + List? records; + + ServiceOpenCardPage({ + this.records, + }); + + factory ServiceOpenCardPage.fromJson(Map json) => + _$ServiceOpenCardPageFromJson( + json, + ); + + Map toJson() => _$ServiceOpenCardPageToJson(this); +} diff --git a/lib/common/models/api/hard_api_models.g.dart b/lib/common/models/api/hard_api_models.g.dart index 66b28b2..00e9c7e 100644 --- a/lib/common/models/api/hard_api_models.g.dart +++ b/lib/common/models/api/hard_api_models.g.dart @@ -118,3 +118,75 @@ Map _$ServiceQRCodeToJson(ServiceQRCode instance) => 'exceedTime': instance.exceedTime, 'extra': instance.extra, }; + +ServiceHardOrder _$ServiceHardOrderFromJson(Map json) => + ServiceHardOrder( + username: json['username'] as String?, + userMobile: json['userMobile'] as String?, + userAvatar: json['userAvatar'] as String?, + ); + +Map _$ServiceHardOrderToJson(ServiceHardOrder instance) => + { + 'username': instance.username, + 'userMobile': instance.userMobile, + 'userAvatar': instance.userAvatar, + }; + +ServiceOpenCard _$ServiceOpenCardFromJson(Map json) => + ServiceOpenCard( + username: json['username'] as String?, + mobile: json['mobile'] as String?, + avatar: json['avatar'] as String?, + cardStatus: json['cardStatus'] as String?, + id: json['id'] as String?, + deptId: json['deptId'] as String?, + deptName: json['deptName'] as String?, + userId: json['userId'] as String?, + walletTypeId: json['walletTypeId'] as String?, + walletTypeUniqueCode: json['walletTypeUniqueCode'] as String?, + walletTypeIcon: json['walletTypeIcon'] as String?, + cardNumber: json['cardNumber'] as String?, + walletId: json['walletId'] as String?, + isActiveHardware: + JsonTool.stringToBoolean(json['isActiveHardware'] as String), + ); + +Map _$ServiceOpenCardToJson(ServiceOpenCard instance) => + { + 'username': instance.username, + 'mobile': instance.mobile, + 'avatar': instance.avatar, + 'cardStatus': instance.cardStatus, + 'id': instance.id, + 'deptId': instance.deptId, + 'deptName': instance.deptName, + 'userId': instance.userId, + 'walletTypeId': instance.walletTypeId, + 'walletTypeUniqueCode': instance.walletTypeUniqueCode, + 'walletTypeIcon': instance.walletTypeIcon, + 'cardNumber': instance.cardNumber, + 'walletId': instance.walletId, + 'isActiveHardware': JsonTool.booleanToString(instance.isActiveHardware), + }; + +ServiceOpenCardPage _$ServiceOpenCardPageFromJson(Map json) => + ServiceOpenCardPage( + records: (json['records'] as List?) + ?.map((e) => ServiceOpenCard.fromJson(e as Map)) + .toList(), + ) + ..current = (json['current'] as num).toInt() + ..pages = (json['pages'] as num).toInt() + ..size = (json['size'] as num).toInt() + ..total = (json['total'] as num).toInt(); + +Map _$ServiceOpenCardPageToJson( + ServiceOpenCardPage instance) => + { + 'current': instance.current, + 'pages': instance.pages, + 'size': instance.size, + 'total': instance.total, + 'records': instance.records, + }; diff --git a/lib/common/models/database/database_enum_models.dart b/lib/common/models/database/database_enum_models.dart index eaccc3b..25f7d6d 100644 --- a/lib/common/models/database/database_enum_models.dart +++ b/lib/common/models/database/database_enum_models.dart @@ -100,3 +100,16 @@ enum ProductHourType implements BaseEnum { const ProductHourType(this.code, this.title); } + +enum WalletType implements BaseEnum { + eat("1", "饭卡钱包"), + pay("2", "虚拟钱包"), + ; + + @override + final String code; + @override + final String title; + + const WalletType(this.code, this.title); +} diff --git a/lib/common/services/database/entity/order_entity.g.dart b/lib/common/services/database/entity/order_entity.g.dart index 234dc0d..644fc17 100644 --- a/lib/common/services/database/entity/order_entity.g.dart +++ b/lib/common/services/database/entity/order_entity.g.dart @@ -68,6 +68,7 @@ const _$OrderTypeEnumMap = { }; const _$OrderStatusEnumMap = { + OrderStatus.all: 'all', OrderStatus.nonePay: 'nonePay', OrderStatus.pay: 'pay', OrderStatus.offlinePay: 'offlinePay', diff --git a/lib/common/utils/sync/sync_face_tool.dart b/lib/common/utils/sync/sync_face_tool.dart index 5901c24..049cc24 100644 --- a/lib/common/utils/sync/sync_face_tool.dart +++ b/lib/common/utils/sync/sync_face_tool.dart @@ -26,9 +26,8 @@ class SyncFaceTool { if (isGlobalFace != null && isGlobalFace) pa["merchantDeptId"] = null; logger.i("|$_tag|同步人脸api参数:|$pa|"); try { - final res = await UserApi.getFaceList(pa); - if (res.code != 0 || res.data == null) throw Error(); - final data = res.data as List; + final data = await UserApi.getFaceList(pa); + if (data == null) throw Error(); if (ObjectUtil.isEmptyList(data)) { if (ObjectUtil.isEmpty(userId)) return false; // 删除人脸图片 diff --git a/lib/common/widgets/order/order_common.dart b/lib/common/widgets/order/order_common.dart index 64d63e0..bfc5342 100644 --- a/lib/common/widgets/order/order_common.dart +++ b/lib/common/widgets/order/order_common.dart @@ -81,7 +81,7 @@ Widget orderAllPrice( Widget orderSelectPanel( BuildContext context, { required void Function(int, int) onChange, - String label = "馒头馒头馒头馒头馒头", + String label = "", int total = 1, num price = 0, int index = 0, diff --git a/lib/common/widgets/user/user_info.dart b/lib/common/widgets/user/user_info.dart index 2f42552..f8ba664 100644 --- a/lib/common/widgets/user/user_info.dart +++ b/lib/common/widgets/user/user_info.dart @@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart'; import 'package:forui/forui.dart'; import 'package:pj_supermarket_cloud/common/index.dart'; -Widget userInfo( +Widget appUserInfo( BuildContext context, { dynamic username = "未知", dynamic balance = "未知", diff --git a/lib/pages/canteen_product/view.dart b/lib/pages/canteen_product/view.dart index 5730e69..5e4b119 100644 --- a/lib/pages/canteen_product/view.dart +++ b/lib/pages/canteen_product/view.dart @@ -19,28 +19,31 @@ class CanteenProductPage extends GetView { size: theme.typography.xl4.fontSize, ).alignLeft().width(headerHeight).onTap(() => Get.back()), Obx(() => controller.classifyList - .map((v) { - final currentIndex = controller.currentTabIndex.value; - final target = controller.classifyList[currentIndex]; - final active = target.id == v.id; - return FButton( - style: active ? FButtonStyle.primary : FButtonStyle.secondary, - label: Text( - v.name, - textAlign: TextAlign.center, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: theme.typography.xl.copyWith( - color: active - ? theme.colorScheme.secondary - : theme.colorScheme.primary, + .map( + (v) { + final currentIndex = controller.currentTabIndex.value; + final target = controller.classifyList[currentIndex]; + final active = target.id == v.id; + return FButton( + style: + active ? FButtonStyle.primary : FButtonStyle.secondary, + label: Text( + v.name, + textAlign: TextAlign.center, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: theme.typography.xl.copyWith( + color: active + ? theme.colorScheme.secondary + : theme.colorScheme.primary, + ), + ).center().width(120.w), + onPress: () => controller.doTab( + controller.classifyList.indexWhere((i) => i.id == v.id), ), - ).center().width(120.w), - onPress: () => controller.doTab( - controller.classifyList.indexWhere((i) => i.id == v.id), - ), - ); - }) + ); + }, + ) .toList() .toRowSpace(space: 20.w)) .scrollable(scrollDirection: Axis.horizontal) @@ -169,7 +172,7 @@ class CanteenProductPage extends GetView { [ Obx(() { final payUser = controller.payUser.value; - return userInfo( + return appUserInfo( context, username: payUser?.username, balance: payUser?.walletBalance, diff --git a/lib/pages/card_active/controller.dart b/lib/pages/card_active/controller.dart index 4f5a310..377a526 100644 --- a/lib/pages/card_active/controller.dart +++ b/lib/pages/card_active/controller.dart @@ -1,18 +1,45 @@ +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 { - CardActiveController(); + final appC = Get.find(); + + SerialCardManage get serialCardManage => appC.serialCardManage; + + // 语音锁 + bool _speechingLock = false; + + // table + RxInt currentPage = 1.obs; + + RxInt totalPage = 0.obs; + + RxInt pageSize = 10.obs; + + RxList tableList = [].obs; + + // 读卡器 + RxString readCardNumber = "未读取".obs; + + RxBool readLoading = false.obs; + + // 写卡 + RxBool writeLoading = false.obs; _initData() { update(["card_active"]); } - void onTap() {} - - // @override - // void onInit() { - // super.onInit(); - // } + @override + void onInit() { + super.onInit(); + _closeStatus(); + getTableList(); + } @override void onReady() { @@ -20,8 +47,188 @@ class CardActiveController extends GetxController { _initData(); } - // @override - // void onClose() { - // super.onClose(); - // } + @override + void onClose() { + super.onClose(); + _closeStatus(); + } + + Future doPage({int? page}) async { + if (page != null) currentPage.value = page; + getTableList(); + } + + 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; + } + } + + // 写卡 + 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; + // writeCardDialog( + // context: Get.context!, + // cardNumber: v.cardNumber!, + // doCancel: () { + // Get.back(); + // serialCardManage.readerClose(); + // serialCardManage.close(); + // _closeWriteStatus(); + // }, + // ); + 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(); + break; + case SerialCardResStatus.warning: + break; + case SerialCardResStatus.error: + _speeching("请在合适区域放置饭卡"); + Toast.error(msg: "请在合适区域放置饭卡"); + break; + } + }; + _speeching("请在合适区域放置饭卡"); + serialCardManage.sendWrite(v.cardNumber!); + } catch (e, stack) { + logger.d("写卡失败error:|$e|stack:|$stack|"); + Toast.error(msg: "写入失败,请重试"); + } + } + + // 读卡 + void doSelectHorType() async { + try { + await serialCardManage.connect(); + if (!serialCardManage.isConnect) { + Toast.error(msg: "连接读卡器失败"); + return; + } + readLoading.value = true; + // readCardDialog( + // context: Get.context!, + // doCancel: () { + // serialCardManage.readerClose(); + // serialCardManage.close(); + // _closeReadStatus(); + // Get.back(); + // }, + // ); + serialCardManage.callback = (type, status, res) { + switch (status) { + case SerialCardResStatus.success: + Toast.success(msg: "读取成功"); + readLoading.value = false; + readCardNumber.value = res; + serialCardManage.readerClose(); + serialCardManage.close(); + break; + case SerialCardResStatus.warning: + break; + case SerialCardResStatus.error: + Toast.error(msg: "请在合适区域放置饭卡"); + _speeching("请在合适区域放置饭卡"); + break; + } + }; + _speeching("请在合适区域放置饭卡"); + serialCardManage.sendRead(); + } catch (e, stack) { + _closeReadStatus(); + logger.d("读卡失败error:|$e|stack:|$stack|"); + Toast.error(msg: "读取失败,请重试"); + } + } + + Future _getOpenCardSuccess(String id) async { + try { + EasyLoading.show(status: '激活饭卡中...'); + final pa = {"id": id}; + final data = await WalletApi.openCardSuccess(pa); + if (data == false) throw Error(); + EasyLoading.dismiss(); + _speeching("激活成功"); + Toast.success(msg: "激活成功"); + } catch (e, stack) { + logger.e("激活饭卡请求失败error:|$e|stack:|$stack|"); + EasyLoading.dismiss(); + Toast.error(msg: "激活失败"); + } + } + + // 重新激活 + Future 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; + getTableList(); + } catch (e, stack) { + logger.d("重新激活失败error:|$e|stack:|$stack|"); + } + } + + Future _speeching(String str) async { + if (_speechingLock) return; + try { + _speechingLock = true; + await Future.delayed(const Duration(seconds: 1)); + await LocalTtsPlugs.speeching(appId: Constants.ttsId, str: str); + await Future.delayed(const Duration(seconds: 2)); + } finally { + _speechingLock = false; + } + } + + void _closeReadStatus() { + readLoading.value = false; + readCardNumber.value = "未读取"; + } + + // 重置状态 + void _closeStatus() { + _speechingLock = false; + } } diff --git a/lib/pages/card_active/view.dart b/lib/pages/card_active/view.dart index e2b91f7..c2c9368 100644 --- a/lib/pages/card_active/view.dart +++ b/lib/pages/card_active/view.dart @@ -1,30 +1,176 @@ +import 'package:data_table_2/data_table_2.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'; + import 'index.dart'; class CardActivePage extends GetView { const CardActivePage({super.key}); // 主视图 - Widget _buildView() { - return const Center( - child: Text("CardActivePage"), - ); + Widget _buildView(BuildContext context) { + return [ + _condition(context), + _buildTable(context).expanded(), + Obx(() => appPage( + context, + pageNo: controller.currentPage.value, + pageSize: controller.pageSize.value, + total: controller.totalPage.value, + onPageChanged: (page) => controller.doPage(page: page), + )) + ].toColumn(); + } + + // 条件 + Widget _condition(BuildContext context) { + return const Center(); + } + + // 主视图 + Widget _buildTable(BuildContext context) { + final theme = FTheme.of(context); + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + final width = constraints.maxWidth; + final height = constraints.maxHeight; + final hdHeight = (height - 50.h) / 10.5; + final hdWidth = (width - 50.h) / 4.5; + return Obx(() => DataTable2( + columnSpacing: 12.w, + horizontalMargin: 12.w, + minWidth: hdWidth, + showBottomBorder: true, + headingTextStyle: theme.typography.lg, + dataTextStyle: theme.typography.base, + dataRowHeight: hdHeight, + columns: [ + DataColumn2( + label: Text("编号", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + fixedWidth: 280.w, + ), + DataColumn2( + label: Text("开卡部门", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + ), + DataColumn2( + label: Text("绑定用户昵称", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + ), + DataColumn2( + label: Text("绑定用户手机号", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + ), + DataColumn2( + label: Text("饭卡类型", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + ), + DataColumn2( + label: Text("饭卡卡号", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + ), + DataColumn2( + label: Text("饭卡状态", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + ), + DataColumn2( + label: Text("操作", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + ), + ], + rows: controller.tableList + .map( + (v) => DataRow( + cells: [ + DataCell( + Text( + v.id ?? "未知", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + v.deptName ?? "未知", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + v.username ?? "", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + v.mobile ?? "", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + WalletType.values + .fromCode(v.walletTypeUniqueCode) + ?.title ?? + "未知", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + v.cardNumber ?? "", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + v.isActiveHardware == false ? "未激活" : "激活", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + WalletType.values + .fromCode(v.walletTypeUniqueCode) + ?.title ?? + "未知", + textAlign: TextAlign.center, + ).center(), + ), + ], + ), + ) + .toList(), + )); + }); } @override Widget build(BuildContext context) { + final theme = FTheme.of(context); + final height = context.height; return GetBuilder( init: CardActiveController(), id: "card_active", builder: (_) { return FScaffold( - header: FHeader(title: SizedBox.shrink()), - content: _buildView(), - contentPad: true, - resizeToAvoidBottomInset: true, + 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), ); }, ); diff --git a/lib/pages/order_history/view.dart b/lib/pages/order_history/view.dart index 4b31c96..e021529 100644 --- a/lib/pages/order_history/view.dart +++ b/lib/pages/order_history/view.dart @@ -20,12 +20,14 @@ class OrderHistoryPage extends GetView { _buildCondition(context, headerHeight), SizedBox(height: 20.h), _buildTable(context).center().expanded(), - appPage( - context, - pageNo: controller.currentPage.value, - pageSize: controller.pageSize.value, - total: controller.totalPage.value, - onPageChanged: (page) => controller.doPage(page: page), + Obx( + () => appPage( + context, + pageNo: controller.currentPage.value, + pageSize: controller.pageSize.value, + total: controller.totalPage.value, + onPageChanged: (page) => controller.doPage(page: page), + ), ) ].toColumn(); } @@ -69,128 +71,129 @@ class OrderHistoryPage extends GetView { Widget _buildTable(BuildContext context) { final theme = FTheme.of(context); - final titleTextStyle = theme.typography.lg; return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { final height = constraints.maxHeight; final hdHeight = (height - 50.h) / 10.5; - return DataTable2( - columnSpacing: 12.w, - horizontalMargin: 12.w, - minWidth: 600.w, - showBottomBorder: true, - headingTextStyle: theme.typography.lg, - dataTextStyle: theme.typography.base, - dataRowHeight: hdHeight, - columns: [ - DataColumn2( - label: Text("下单时间", textAlign: TextAlign.center), - headingRowAlignment: MainAxisAlignment.center, - fixedWidth: 260.w, - ), - DataColumn2( - label: Text('订餐类型'), - headingRowAlignment: MainAxisAlignment.center, - fixedWidth: 150.w, - ), - DataColumn2( - label: Text('支付类型'), - headingRowAlignment: MainAxisAlignment.center, - fixedWidth: 150.w, - ), - DataColumn2( - label: Text('订单人', textAlign: TextAlign.center), - headingRowAlignment: MainAxisAlignment.center, - fixedWidth: 150.w, - ), - DataColumn2( - label: Text('菜品名称'), - headingRowAlignment: MainAxisAlignment.center, - fixedWidth: 220.w, - ), - DataColumn2( - label: Text('数量'), - headingRowAlignment: MainAxisAlignment.center, - fixedWidth: 150.w, - ), - DataColumn2( - label: Text('菜品总价'), - headingRowAlignment: MainAxisAlignment.center, - fixedWidth: 150.w, - ), - DataColumn2( - label: Text('额外支付'), - headingRowAlignment: MainAxisAlignment.center, - fixedWidth: 150.w, - ), - DataColumn2( - label: Text('实际扣款'), - headingRowAlignment: MainAxisAlignment.center, - fixedWidth: 150.w, - ), - DataColumn2( - label: Text('操作'), - headingRowAlignment: MainAxisAlignment.center, - fixedWidth: 150.w, - ), - ], - rows: controller.orderList - .map( - (v) => DataRow(cells: [ - DataCell( - Text( - DateUtil.formatDate(v.createTime), - textAlign: TextAlign.center, - ).center(), - ), - DataCell( - Text( - v.type.title, - textAlign: TextAlign.center, - ).center(), - ), - DataCell( - Text( - v.payType.title, - textAlign: TextAlign.center, - ).center(), - ), - DataCell( - Text( - v.username ?? "未知用户", - textAlign: TextAlign.center, - ).center(), - ), - DataCell( - Text( - v.productDesc ?? "未知商品", - textAlign: TextAlign.center, - ).center(), - ), - DataCell( - Text( - "${v.count}", - textAlign: TextAlign.center, - ).center(), - ), - DataCell( - appPrice(context, v.price).center(), - ), - DataCell( - appPrice(context, v.extraPrice).center(), - ), - DataCell( - appPrice(context, v.payPrice).center(), - ), - DataCell( - Text( - v.payType.title, - textAlign: TextAlign.center, - ).center(), - ), - ]), - ) - .toList(), + return Obx( + () => DataTable2( + columnSpacing: 12.w, + horizontalMargin: 12.w, + minWidth: 600.w, + showBottomBorder: true, + headingTextStyle: theme.typography.lg, + dataTextStyle: theme.typography.base, + dataRowHeight: hdHeight, + columns: [ + DataColumn2( + label: Text("下单时间", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + fixedWidth: 260.w, + ), + DataColumn2( + label: Text('订餐类型'), + headingRowAlignment: MainAxisAlignment.center, + fixedWidth: 150.w, + ), + DataColumn2( + label: Text('支付类型'), + headingRowAlignment: MainAxisAlignment.center, + fixedWidth: 150.w, + ), + DataColumn2( + label: Text('订单人', textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + fixedWidth: 150.w, + ), + DataColumn2( + label: Text('菜品名称'), + headingRowAlignment: MainAxisAlignment.center, + fixedWidth: 220.w, + ), + DataColumn2( + label: Text('数量'), + headingRowAlignment: MainAxisAlignment.center, + fixedWidth: 150.w, + ), + DataColumn2( + label: Text('菜品总价'), + headingRowAlignment: MainAxisAlignment.center, + fixedWidth: 150.w, + ), + DataColumn2( + label: Text('额外支付'), + headingRowAlignment: MainAxisAlignment.center, + fixedWidth: 150.w, + ), + DataColumn2( + label: Text('实际扣款'), + headingRowAlignment: MainAxisAlignment.center, + fixedWidth: 150.w, + ), + DataColumn2( + label: Text('操作'), + headingRowAlignment: MainAxisAlignment.center, + fixedWidth: 150.w, + ), + ], + rows: controller.orderList + .map( + (v) => DataRow(cells: [ + DataCell( + Text( + DateUtil.formatDate(v.createTime), + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + v.type.title, + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + v.payType.title, + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + v.username ?? "未知用户", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + v.productDesc ?? "未知商品", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + "${v.count}", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + appPrice(context, v.price).center(), + ), + DataCell( + appPrice(context, v.extraPrice).center(), + ), + DataCell( + appPrice(context, v.payPrice).center(), + ), + DataCell( + Text( + v.payType.title, + textAlign: TextAlign.center, + ).center(), + ), + ]), + ) + .toList(), + ), ); }, ); diff --git a/lib/pages/setting/view.dart b/lib/pages/setting/view.dart index b5d56c4..ea07215 100644 --- a/lib/pages/setting/view.dart +++ b/lib/pages/setting/view.dart @@ -104,12 +104,25 @@ class SettingPage extends GetView { @override Widget build(BuildContext context) { + final theme = FTheme.of(context); + final height = context.height; return GetBuilder( init: SettingController(), id: "setting", builder: (_) { return FScaffold( - header: FHeader(title: SizedBox.shrink()), + 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(), contentPad: true, resizeToAvoidBottomInset: true, diff --git a/lib/pages/setting/widgets/setting_face.dart b/lib/pages/setting/widgets/setting_face.dart index b535c46..ba118e4 100644 --- a/lib/pages/setting/widgets/setting_face.dart +++ b/lib/pages/setting/widgets/setting_face.dart @@ -212,7 +212,7 @@ Widget buildFacePage(context) { context, pageNo: faceC.currentPage.value, pageSize: faceC.pageSize.value, - total: faceC.totalPage.value * faceC.pageSize.value, + total: faceC.totalPage.value, onPageChanged: (pageNo) => faceC.doPage(page: pageNo), ); } diff --git a/lib/pages/supermarket_order/view.dart b/lib/pages/supermarket_order/view.dart index ee51dcc..8d81e5b 100644 --- a/lib/pages/supermarket_order/view.dart +++ b/lib/pages/supermarket_order/view.dart @@ -1,3 +1,4 @@ +import 'package:ducafe_ui_core/ducafe_ui_core.dart'; import 'package:flutter/material.dart'; import 'package:forui/forui.dart'; import 'package:get/get.dart'; @@ -8,7 +9,7 @@ class SupermarketOrderPage extends GetView { const SupermarketOrderPage({super.key}); // 主视图 - Widget _buildView() { + Widget _buildView(BuildContext context) { return const Center( child: Text("SupermarketOrderPage"), ); @@ -16,15 +17,26 @@ class SupermarketOrderPage extends GetView { @override Widget build(BuildContext context) { + final theme = FTheme.of(context); + final height = context.height; return GetBuilder( init: SupermarketOrderController(), id: "supermarket_order", builder: (_) { return FScaffold( - header: FHeader(title: SizedBox.shrink()), - content: _buildView(), - contentPad: true, - resizeToAvoidBottomInset: true, + 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), ); }, ); diff --git a/lib/pages/take_food/controller.dart b/lib/pages/take_food/controller.dart index c928cfb..f2102f2 100644 --- a/lib/pages/take_food/controller.dart +++ b/lib/pages/take_food/controller.dart @@ -1,27 +1,197 @@ +import 'dart:convert'; + +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'; class TakeFoodController extends GetxController { - TakeFoodController(); + final appC = Get.find(); + + Rxn payUser = Rxn(ServiceUser()); + + RxList carList = [].obs; + + OrderPayType orderPayType = OrderPayType.reserveQrCode; + + OrderType orderType = OrderType.reserve; + + int totalPrice = 0; + + String? orderId; + + ServiceQRCode? globalQrCode; + + // 语音锁 + bool _speechingLock = false; _initData() { update(["take_food"]); } - void onTap() {} - - // @override - // void onInit() { - // super.onInit(); - // } + @override + void onInit() { + super.onInit(); + SubScreenPlugin.doubleScreenShow(); + SubScreenPlugin.sendMsgToViceScreen(SubScreenAction.jumpSubPage.name); + // 开启扫码功能 + startQrcode(); + } @override void onReady() { super.onReady(); + _closeStatus(); _initData(); } - // @override - // void onClose() { - // super.onClose(); - // } + @override + void onClose() { + super.onClose(); + // 二维码读头 + appC.serialCardManage.close(); + _closeStatus(); + } + + // 重置状态 + void _closeStatus() { + _speechingLock = false; + } + + doCancelPay() { + orderId = null; + globalQrCode = null; + payUser.value = ServiceUser(); + payUser.refresh(); + carList.value = []; + } + + doPay() async { + await orderQrCodeCancel(); + } + + /// 获取订单详情 + Future getOrderDetail() async { + if (ObjectUtil.isEmpty(orderId)) return; + final pa = {"id": orderId}; + try { + final data = await HardApi.getOrder(pa); + if (data == null) throw Error(); + if (ObjectUtil.isEmpty(data)) return; + payUser.value?.username = data.username; + payUser.value?.userId = globalQrCode?.userId; + payUser.value?.userAvatar = data.userAvatar; + payUser.refresh(); + } catch (e) { + logger.e("获取订单详情错误error:|$e|"); + } + } + + /// 获取购物车详情 + Future getCarList() async { + if (ObjectUtil.isEmpty(orderId)) return; + final pa = {"orderId": orderId}; + try { + final data = await HardApi.getCarByOrderId(pa); + if (data == null) throw Error(); + carList.value = data; + senSubScreen(); + } catch (e, stack) { + logger.e("获取购物车详情错误error:|$e|stack:|$stack|"); + } + } + + /// 开启扫码 + Future startQrcode() async { + try { + final isOpen = await appC.scanCodeManage.open(); + if (!isOpen) return; + appC.scanCodeManage.listen((str) async { + if (ObjectUtil.isNotEmpty(str)) { + str = EncryptUtil.decodeBase64(str); + final qrCode = ServiceQRCode.fromJson(json.decode(str)); + if (ObjectUtil.isEmpty(qrCode)) { + Toast.error(msg: "识别错误,无效的二维码"); + return; + } + globalQrCode = qrCode; + // 二维码支付渠道 + if (ObjectUtil.isNotEmpty(qrCode.payType)) { + final OrderPayType? type = OrderPayType.values.fromCode( + qrCode.payType!, + ); + if (ObjectUtil.isNotEmpty(type)) { + orderPayType = type!; + } + } + if (ObjectUtil.isNotEmpty(qrCode.orderType)) { + final OrderType? type = OrderType.values.fromCode( + qrCode.orderType!, + ); + if (ObjectUtil.isNotEmpty(type)) { + orderType = type!; + } + } + orderId = qrCode.id; + await getOrderDetail(); + await getCarList(); + Toast.success(msg: "识别二维码成功"); + } + }); + } catch (e) { + doCancelPay(); + logger.e("开启扫码错误error:|$e|"); + } + } + + /// 订单核销 + Future orderQrCodeCancel() async { + if (ObjectUtil.isEmpty(orderId)) { + Toast.error(msg: "请前往设置绑定设备"); + return; + } + final appSetting = await AppSettingTool.getSysSetting(); + final serviceId = appSetting.serviceId; + if (ObjectUtil.isEmpty(serviceId)) { + Toast.error(msg: "订单不存在"); + return; + } + final pa = { + "orderId": orderId, + "hardwareId": serviceId, + }; + try { + EasyLoading.show( + status: '核销中...', + maskType: EasyLoadingMaskType.black, + ); + final res = await HardApi.cancelQrCode(pa); + if (res.code != 0) throw Error(); + EasyLoading.dismiss(); + Toast.error(msg: "核销成功"); + _speeching("核销成功"); + doCancelPay(); + } catch (e, stack) { + logger.e("获取订单详情错误error:|$e|stack:|$stack|"); + EasyLoading.dismiss(); + } + } + + /// 发送给父屏订单信息 + void senSubScreen() { + if (ObjectUtil.isEmpty(carList)) return; + } + + Future _speeching(String str) async { + if (_speechingLock) return; + try { + _speechingLock = true; + await Future.delayed(const Duration(seconds: 1)); + await LocalTtsPlugs.speeching(appId: Constants.ttsId, str: str); + await Future.delayed(const Duration(seconds: 2)); + } finally { + _speechingLock = false; + } + } } diff --git a/lib/pages/take_food/view.dart b/lib/pages/take_food/view.dart index 25312a5..eca9c16 100644 --- a/lib/pages/take_food/view.dart +++ b/lib/pages/take_food/view.dart @@ -1,34 +1,164 @@ +import 'package:data_table_2/data_table_2.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'; + import 'index.dart'; class TakeFoodPage extends GetView { const TakeFoodPage({super.key}); // 主视图 - Widget _buildView() { - return const Center( - child: Text("TakeFoodPage"), + Widget _buildView(BuildContext context) { + final theme = FTheme.of(context); + final width = context.width; + return [ + _buildTable(context).expanded(), + SizedBox(width: 15.w), + _buildUserInfo(context).width(width * 0.25).decorated( + border: Border.all( + color: theme.colorScheme.primary, + width: 1, + ), + borderRadius: theme.style.borderRadius, + ), + ] + .toRow(crossAxisAlignment: CrossAxisAlignment.start) + .marginOnly(bottom: 20.h); + } + + // 表格 + Widget _buildTable(BuildContext context) { + final theme = FTheme.of(context); + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + final width = constraints.maxWidth; + final height = constraints.maxHeight; + final hdHeight = (height - 50.h) / 10.5; + final hdWidth = (width - 50.h) / 4.5; + return Obx( + () => DataTable2( + columnSpacing: 12.w, + horizontalMargin: 12.w, + minWidth: hdWidth, + showBottomBorder: true, + headingTextStyle: theme.typography.lg, + dataTextStyle: theme.typography.base, + dataRowHeight: hdHeight, + columns: [ + DataColumn2( + label: Text("菜品名称", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + ), + DataColumn2( + label: Text("数量", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + ), + DataColumn2( + label: Text("单价金额", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + ), + DataColumn2( + label: Text("总金额", textAlign: TextAlign.center), + headingRowAlignment: MainAxisAlignment.center, + ), + ], + rows: controller.carList + .map( + (v) => DataRow( + cells: [ + DataCell( + Text( + "${v.productName}", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + Text( + "${v.productCount}", + textAlign: TextAlign.center, + ).center(), + ), + DataCell( + appPrice(context, v.productPrice), + ), + DataCell( + appPrice(context, v.totalPrice), + ), + ], + ), + ) + .toList(), + ), + ); + }, ); } + // 用户 + Widget _buildUserInfo(BuildContext context) { + final theme = FTheme.of(context); + return [ + Obx(() { + final payUser = controller.payUser.value; + return appUserInfo( + context, + username: payUser?.username, + balance: payUser?.walletBalance, + avatar: payUser?.userAvatar, + ); + }).width(400.w).center(), + SizedBox(height: 200.h), + [ + FButton( + style: FButtonStyle.destructive, + label: Text( + "取消点餐", + style: theme.typography.xl.copyWith( + color: theme.colorScheme.secondary, + ), + ), + onPress: () => controller.doCancelPay(), + ).flexible(flex: 1), + SizedBox(width: 20.w), + FButton( + label: Text( + "确认点餐", + style: theme.typography.xl.copyWith( + color: theme.colorScheme.secondary, + ), + ), + onPress: () => controller.doPay(), + ).flexible(flex: 1), + ].toRow(crossAxisAlignment: CrossAxisAlignment.end).expanded(), + ].toColumn().padding(vertical: 10.h, horizontal: 10.w); + } + @override Widget build(BuildContext context) { final theme = FTheme.of(context); + final height = context.height; return GetBuilder( init: TakeFoodController(), id: "take_food", builder: (_) { return FScaffold( header: FHeader.nested( - title: Text("取餐", style: theme.typography.xl3), + title: Text("取餐"), prefixActions: [ - FHeaderAction.back(onPress: () {}), + FHeaderAction.back(onPress: Get.back), ], - ), - content: _buildView(), + 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), ); }, );