feat:饭卡激活

This commit is contained in:
dzc committed 2025-03-16 21:35:15 +08:00
1 parent 72443f7abf
commit 9a98ac8e1c
20 files changed
+665 -154

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@ import 'package:pj_supermarket_cloud/common/index.dart';
abstract class ProductApi {
/// 菜品分类
static Future<List<ServiceClassify>?> cookbookClassify(
static Future<List<ServiceClassify>?> canteenClassify(
dynamic pa,
) async {
final res = await HttpManage().dio.get(
+2 -2
View File
@@ -24,7 +24,7 @@ abstract class WalletApi {
/// 开卡成功
static Future<bool> openCardSuccess(dynamic pa) async {
final res = await HttpManage().dio.get<ServiceBase>(
final res = await HttpManage().dio.get(
"/wallet/wallet-active/ad/open-card-success",
queryParameters: pa,
);
@@ -34,7 +34,7 @@ abstract class WalletApi {
/// 重新激活
static Future<bool> refreshAvatar(dynamic pa) async {
final res = await HttpManage().dio.get<ServiceBase>(
final res = await HttpManage().dio.get(
"/wallet/wallet-active/ad/refresh-avatar",
queryParameters: pa,
);
@@ -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<OrderStatus> visibleList() {
return OrderStatus.values.where((e) => e.visible).toList();
}
}
// 打印状态
-12
View File
@@ -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) {
+1 -7
View File
@@ -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<void> 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<ServiceClassify>? 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);
}
+16
View File
@@ -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());
}
+19 -14
View File
@@ -8,7 +8,7 @@ Future<bool> appMessageDialog(BuildContext context, String msg) async {
final res = await showAdaptiveDialog<bool>(
context: context,
builder: (context) => FDialog(
direction: Axis.horizontal,
direction: Axis.vertical,
title: <Widget>[
FIcon(
FAssets.icons.circleAlert,
@@ -17,25 +17,30 @@ Future<bool> 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(
<Widget>[
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(),
],
),
);
+112
View File
@@ -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 <Widget>[
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 <Widget>[
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<void> cardDialog({
required String title,
required Rx<bool> loading,
required VoidCallback doSubmit,
required Rx<ServiceOpenCard?> 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: <Widget>[
Obx(
() => Visibility(
visible: detail.value == null,
replacement: <Widget>[
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),
],
);
},
);
}
+3
View File
@@ -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';
@@ -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';
+5 -7
View File
@@ -17,13 +17,11 @@ class CanteenProductPage extends GetView<CanteenProductController> {
final context = Get.context!;
final theme = FTheme.of(context);
return <Widget>[
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,
+66 -50
View File
@@ -19,8 +19,7 @@ class CardActiveController extends GetxController {
RxList<ServiceOpenCard> tableList = <ServiceOpenCard>[].obs;
// 读卡器
RxString readCardNumber = "未读取".obs;
Rx<ServiceOpenCard?> detail = Rx(null);
RxBool readLoading = false.obs;
@@ -54,20 +53,28 @@ class CardActiveController extends GetxController {
}
Future<void> 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<void> _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 = "未读取";
}
}
+70 -29
View File
@@ -11,10 +11,50 @@ import 'index.dart';
class CardActivePage extends GetView<CardActiveController> {
const CardActivePage({super.key});
// 主视图
Widget _buildView(BuildContext context) {
// 条件
Widget _buildHeader(double height) {
final context = Get.context!;
final theme = FTheme.of(context);
return <Widget>[
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 <Widget>[
_condition(context),
_buildTable(context).expanded(),
Obx(() => appPage(
context,
@@ -26,11 +66,6 @@ class CardActivePage extends GetView<CardActiveController> {
].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<CardActiveController> {
).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<CardActiveController> {
@override
Widget build(BuildContext context) {
final theme = FTheme.of(context);
final height = context.height;
final height = 90;
return GetBuilder<CardActiveController>(
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,
);
},
+1 -1
View File
@@ -137,7 +137,7 @@ class OrderHistoryController extends GetxController {
Future<void> getTotalList() async {
final total = await _getTotal();
List<OrderHistoryTableItem> tabs = OrderStatus.values
List<OrderHistoryTableItem> tabs = OrderStatus.visibleList()
.map((v) => OrderHistoryTableItem(status: v))
.toList();
for (int i = 0; i < tabs.length; i++) {
+1 -1
View File
@@ -26,7 +26,7 @@ class SettingPage extends GetView<SettingController> {
),
),
Obx(() => _buildDetail(context, controller.cuurentMenuType.value))
.marginOnly(left: 20.w)
.marginSymmetric(horizontal: 10.w)
.expanded(),
].toRow().height(double.infinity);
}
@@ -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);
});
});
@@ -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();
+31 -2
View File
@@ -47,11 +47,27 @@ class ProductManageController extends GetxController {
Future<void> initData() async {
await getProductList();
if (ObjectUtil.isEmptyList(productList)) {
await SyncProductTool.updateDatabase();
getProductList();
}
}
// 更新商品和分类
Future<void> updateProductAndClassify() async {
EasyLoading.show(
status: '更新中...',
maskType: EasyLoadingMaskType.black,
);
try {
await SyncProductTool.updateDatabase();
await getProductList();
} finally {
Future.delayed(const Duration(seconds: 1), () {
EasyLoading.dismiss();
});
}
}
// 获取商品列表
Future<void> 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<AppControl>();
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();
}
+151 -1
View File
@@ -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<AppControl>();
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<void> _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<void> doPay() async {
final validShopCar = _validateShopCar();
if (!validShopCar) return;
// 创建本地订单
await createSqlOrder();
selectPayTypeDialog();
}
// |下单|点击取消下单
Future<void> doCancelPay({bool cancelShop = true}) async {
if (cancelShop) await _clearShopCard();
sendSubScreen();
}
// 分页
Future<void> doPage({int? page}) async {
if (page != null) currentPage.value = page;
}
void _listenPayTypeSelectCallback() {
final payTypeSelect = Get.find<SelectPayTypeDialogController>();
// |支付方式|点击|开始识别|
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<void> _computedAmountMealPrice() async {
final price = await getAmountMealPrice(
price: totalPrice.value,
);
if (ObjectUtil.isNotEmpty(price)) {
amountMealPrice.value = int.parse(price!.toString());
sendSubScreen();
}
}
Future<void> _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;
}
}
+170 -10
View File
@@ -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<SupermarketOrderController> {
const SupermarketOrderPage({super.key});
// **************************************************************************
// 头部 start
// **************************************************************************
// 头部
Widget _buildHeader(double headerHeight) {
final context = Get.context!;
final theme = FTheme.of(context);
return <Widget>[
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<SupermarketOrderController> {
].toRow();
}
// **************************************************************************
// 头部 end
// **************************************************************************
// **************************************************************************
// 商品 start
// **************************************************************************
Widget _buildProductItem(
BuildContext context, {
required List<ProductEntity> productList,
required double productSpacing,
required Size productSize,
String? title = "",
}) {
return <Widget>[].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 <Widget>[
_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 <Widget>[
<Widget>[
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(),
<Widget>[
Obx(
() => orderAllPrice(
Get.context!,
totalPrice: controller.totalPrice.value,
amountMealPrice: controller.amountMealPrice.value,
),
),
SizedBox(height: 20.h),
<Widget>[
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 <Widget>[
_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