This commit is contained in:
dzc committed 2025-03-09 20:35:03 +08:00
1 parent 295b4058be
commit 08bf352984
21 files changed
+1109 -234

No files matched your search

+7 -11
View File
@@ -70,29 +70,25 @@ abstract class HardApi {
}
/// 获取订单详情
static Future<ServiceBase> getOrder(dynamic formData) async {
final res = await HttpManage().dio.get<ServiceBase>(
static Future<ServiceHardOrder?> 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<ServiceListBase<ServiceCar>> getCarByOrderId(
static Future<List<ServiceCar>?> 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<ServiceCar>((e) => ServiceCar.fromJson(e)).toList();
res.data?.data = typeData;
return res.data!;
if (res.data == null) return null;
return res.data?.map<ServiceCar>((e) => ServiceCar.fromJson(e)).toList();
}
/// 核销订单
+3 -7
View File
@@ -44,20 +44,16 @@ abstract class UserApi {
}
/// 获取人脸
static Future<ServiceListBase<ServiceAsyncFace>> getFaceList(
static Future<List<ServiceAsyncFace>?> 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<ServiceAsyncFace>((e) => ServiceAsyncFace.fromJson(e))
.toList();
res.data?.data = typeData;
return res.data;
}
}
+4 -2
View File
@@ -11,13 +11,15 @@ abstract class WalletApi {
}
/// 开卡列表
static Future<ServiceBase?> openCardList(dynamic pa) async {
static Future<ServiceOpenCardPage?> 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);
}
/// 开卡成功
+13 -12
View File
@@ -21,20 +21,21 @@ class ServiceBase<T> {
}
@JsonSerializable()
class ServiceListBase<T> {
String? msg;
int? code;
@JsonKey(includeFromJson: false, includeToJson: false)
List<T>? 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<String, dynamic> json) =>
_$ServiceListBaseFromJson(json);
factory ServicePageBase.fromJson(Map<String, dynamic> json) =>
_$ServicePageBaseFromJson(json);
Map<String, dynamic> toJson() => _$ServiceListBaseToJson(this);
Map<String, dynamic> toJson() => _$ServicePageBaseToJson(this);
}
+11 -7
View File
@@ -20,14 +20,18 @@ Map<String, dynamic> _$ServiceBaseToJson<T>(ServiceBase<T> instance) =>
'data': instance.data,
};
ServiceListBase<T> _$ServiceListBaseFromJson<T>(Map<String, dynamic> json) =>
ServiceListBase<T>(
msg: json['msg'] as String?,
code: (json['code'] as num?)?.toInt(),
ServicePageBase _$ServicePageBaseFromJson(Map<String, dynamic> 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<String, dynamic> _$ServiceListBaseToJson<T>(ServiceListBase<T> instance) =>
Map<String, dynamic> _$ServicePageBaseToJson(ServicePageBase instance) =>
<String, dynamic>{
'msg': instance.msg,
'code': instance.code,
'current': instance.current,
'pages': instance.pages,
'size': instance.size,
'total': instance.total,
};
+107
View File
@@ -201,3 +201,110 @@ class ServiceQRCode {
Map<String, dynamic> 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<String, dynamic> json) =>
_$ServiceHardOrderFromJson(json);
Map<String, dynamic> 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<String, dynamic> json) =>
_$ServiceOpenCardFromJson(json);
Map<String, dynamic> toJson() => _$ServiceOpenCardToJson(this);
}
@JsonSerializable()
class ServiceOpenCardPage extends ServicePageBase {
List<ServiceOpenCard>? records;
ServiceOpenCardPage({
this.records,
});
factory ServiceOpenCardPage.fromJson(Map<String, dynamic> json) =>
_$ServiceOpenCardPageFromJson(
json,
);
Map<String, dynamic> toJson() => _$ServiceOpenCardPageToJson(this);
}
@@ -118,3 +118,75 @@ Map<String, dynamic> _$ServiceQRCodeToJson(ServiceQRCode instance) =>
'exceedTime': instance.exceedTime,
'extra': instance.extra,
};
ServiceHardOrder _$ServiceHardOrderFromJson(Map<String, dynamic> json) =>
ServiceHardOrder(
username: json['username'] as String?,
userMobile: json['userMobile'] as String?,
userAvatar: json['userAvatar'] as String?,
);
Map<String, dynamic> _$ServiceHardOrderToJson(ServiceHardOrder instance) =>
<String, dynamic>{
'username': instance.username,
'userMobile': instance.userMobile,
'userAvatar': instance.userAvatar,
};
ServiceOpenCard _$ServiceOpenCardFromJson(Map<String, dynamic> 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<String, dynamic> _$ServiceOpenCardToJson(ServiceOpenCard instance) =>
<String, dynamic>{
'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<String, dynamic> json) =>
ServiceOpenCardPage(
records: (json['records'] as List<dynamic>?)
?.map((e) => ServiceOpenCard.fromJson(e as Map<String, dynamic>))
.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<String, dynamic> _$ServiceOpenCardPageToJson(
ServiceOpenCardPage instance) =>
<String, dynamic>{
'current': instance.current,
'pages': instance.pages,
'size': instance.size,
'total': instance.total,
'records': instance.records,
};
@@ -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);
}
@@ -68,6 +68,7 @@ const _$OrderTypeEnumMap = {
};
const _$OrderStatusEnumMap = {
OrderStatus.all: 'all',
OrderStatus.nonePay: 'nonePay',
OrderStatus.pay: 'pay',
OrderStatus.offlinePay: 'offlinePay',
+2 -3
View File
@@ -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<ServiceAsyncFace>;
final data = await UserApi.getFaceList(pa);
if (data == null) throw Error();
if (ObjectUtil.isEmptyList(data)) {
if (ObjectUtil.isEmpty(userId)) return false;
// 删除人脸图片
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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 = "未知",
+25 -22
View File
@@ -19,28 +19,31 @@ class CanteenProductPage extends GetView<CanteenProductController> {
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<CanteenProductController> {
<Widget>[
Obx(() {
final payUser = controller.payUser.value;
return userInfo(
return appUserInfo(
context,
username: payUser?.username,
balance: payUser?.walletBalance,
+218 -11
View File
@@ -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<AppControl>();
SerialCardManage get serialCardManage => appC.serialCardManage;
// 语音锁
bool _speechingLock = false;
// table
RxInt currentPage = 1.obs;
RxInt totalPage = 0.obs;
RxInt pageSize = 10.obs;
RxList<ServiceOpenCard> tableList = <ServiceOpenCard>[].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<void> doPage({int? page}) async {
if (page != null) currentPage.value = page;
getTableList();
}
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;
}
}
// 写卡
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<void> _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<void> doRefreshAvatar(ServiceOpenCard v) async {
if (v.isActiveHardware == false) {
Toast.error(msg: "该卡为激活状态,不能重新激活");
return;
}
try {
final isSure = await appMessageDialog(Get.context!, "是否重新激活");
if (!isSure) return;
final pa = {"id": v.id};
final data = await WalletApi.refreshAvatar(pa);
if (data == false) return;
getTableList();
} catch (e, stack) {
logger.d("重新激活失败error:|$e|stack:|$stack|");
}
}
Future<void> _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;
}
}
+154 -8
View File
@@ -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<CardActiveController> {
const CardActivePage({super.key});
// 主视图
Widget _buildView() {
return const Center(
child: Text("CardActivePage"),
);
Widget _buildView(BuildContext context) {
return <Widget>[
_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<CardActiveController>(
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),
);
},
);
+127 -124
View File
@@ -20,12 +20,14 @@ class OrderHistoryPage extends GetView<OrderHistoryController> {
_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<OrderHistoryController> {
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(),
),
);
},
);
+14 -1
View File
@@ -104,12 +104,25 @@ class SettingPage extends GetView<SettingController> {
@override
Widget build(BuildContext context) {
final theme = FTheme.of(context);
final height = context.height;
return GetBuilder<SettingController>(
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,
+1 -1
View File
@@ -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),
);
}
+17 -5
View File
@@ -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<SupermarketOrderController> {
const SupermarketOrderPage({super.key});
// 主视图
Widget _buildView() {
Widget _buildView(BuildContext context) {
return const Center(
child: Text("SupermarketOrderPage"),
);
@@ -16,15 +17,26 @@ class SupermarketOrderPage extends GetView<SupermarketOrderController> {
@override
Widget build(BuildContext context) {
final theme = FTheme.of(context);
final height = context.height;
return GetBuilder<SupermarketOrderController>(
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),
);
},
);
+181 -11
View File
@@ -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<AppControl>();
Rxn<ServiceUser> payUser = Rxn<ServiceUser>(ServiceUser());
RxList<ServiceCar> carList = <ServiceCar>[].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<void> 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<void> 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<void> startQrcode() async {
try {
final isOpen = await appC.scanCodeManage.open();
if (!isOpen) return;
appC.scanCodeManage.listen<String>((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<void> 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<void> _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;
}
}
}
+137 -7
View File
@@ -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<TakeFoodController> {
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 <Widget>[
_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 <Widget>[
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),
<Widget>[
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<TakeFoodController>(
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),
);
},
);