From 09f0048b673c3d65f1a80065c41df6029234eff4 Mon Sep 17 00:00:00 2001 From: cuiyouliang <799699717@qq.com> Date: Sat, 13 Sep 2025 16:14:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=B8=E9=94=80=E8=81=94=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/api/shop_api.dart | 5 + lib/pages/chat/index.dart | 55 ++- lib/pages/chat/notify/system.dart | 63 ++- lib/pages/my/vloger.dart | 1 + lib/pages/order/detail.dart | 606 ++++++++++++++++---------- lib/pages/order/my_order.dart | 1 + lib/pages/order/seller_detail.dart | 341 ++++----------- lib/pages/video/module/attention.dart | 3 +- lib/pages/video/module/friend.dart | 3 +- lib/pages/video/module/recommend.dart | 3 +- lib/utils/scan_code_type.dart | 29 ++ pubspec.lock | 16 + pubspec.yaml | 2 + 13 files changed, 630 insertions(+), 498 deletions(-) create mode 100644 lib/utils/scan_code_type.dart diff --git a/lib/api/shop_api.dart b/lib/api/shop_api.dart index f3e8c3d..4d43e66 100644 --- a/lib/api/shop_api.dart +++ b/lib/api/shop_api.dart @@ -39,4 +39,9 @@ class ShopApi { // 商家营收分页 static const String revenueList= '/app/merchant/account/bill/page'; + // 根据核销码查询详情 + static const String getWriteOffDetail= '/app/merchant/order/scan'; + + // 确认核销 + static const String confirmWriteOff= '/app/merchant/order/verify'; } diff --git a/lib/pages/chat/index.dart b/lib/pages/chat/index.dart index 4198f1a..2190e23 100644 --- a/lib/pages/chat/index.dart +++ b/lib/pages/chat/index.dart @@ -12,6 +12,7 @@ import 'package:loopin/components/scan_util.dart'; import 'package:loopin/models/conversation_type.dart'; import 'package:loopin/models/conversation_view_model.dart'; import 'package:loopin/utils/index.dart'; +import 'package:loopin/utils/scan_code_type.dart'; // 导入外部枚举 import 'package:loopin/utils/parse_message_summary.dart'; import 'package:shirne_dialog/shirne_dialog.dart'; @@ -51,7 +52,55 @@ class ChatPageState extends State { await Future.delayed(Duration(seconds: 1)); setState(() {}); } + // 处理扫码结果 + void handleScanResult(String code) { + print('扫码结果11111111111111111111111:$code'); + // 使用外部枚举的方法检查扫码类型 + final scanType = ScanCodeType.fromCode(code); + if (scanType != null) { + final value = code.substring(scanType.prefix.length + 1); // 获取后缀值 + _processScanCode(scanType, value); + } else { + // 未知类型的码 + Get.snackbar('未知的二维码类型', '无法识别此二维码: $code'); + } + } + + // 处理不同类型的扫码结果 + void _processScanCode(ScanCodeType type, String value) { + switch (type) { + case ScanCodeType.verification: + _handleVerificationCode(value); + break; + case ScanCodeType.friend: + _handleFriendCode(value); + break; + case ScanCodeType.promotion: + _handlePromotionCode(value); + break; + } + } + + // 处理核销码 + void _handleVerificationCode (String value) async { + print('处理核销码: $value'); + // 带着核销码,跳转到商家的商品详情页面,引导商家去手动点击核销按钮 + Get.toNamed('/sellerOrder/detail', arguments: {'writeOffCodeId':value}); + } + + // 处理好友码 + void _handleFriendCode(String value) { + print('处理好友码: $value'); + // 模仿抖音,去个人页面手动点击关注 + Get.toNamed('/vloger', arguments: {'memberId':value}); + } + + // 处理推广码 + void _handlePromotionCode(String value) { + print('处理推广码: $value'); + Get.toNamed('/vloger', arguments: {'memberId':value}); + } // 长按菜单 void showContextMenu(BuildContext context, ConversationViewModel item) { bool isLeft = posDX > MediaQuery.of(context).size.width / 2 ? false : true; @@ -240,11 +289,7 @@ class ChatPageState extends State { break; case 'scan': logger.w('点击了扫一扫'); - ScanUtil.openScanner(onResult: (code) { - logger.w('扫码结果:$code'); - Get.snackbar('扫码成功', code); - // 执行业务逻辑 - }); + ScanUtil.openScanner(onResult: handleScanResult); break; } } diff --git a/lib/pages/chat/notify/system.dart b/lib/pages/chat/notify/system.dart index f8472ce..8f20525 100644 --- a/lib/pages/chat/notify/system.dart +++ b/lib/pages/chat/notify/system.dart @@ -4,16 +4,19 @@ library; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; +import 'package:loopin/service/http.dart'; import 'package:loopin/IM/controller/chat_controller.dart'; import 'package:loopin/IM/global_badge.dart'; import 'package:loopin/IM/im_service.dart'; import 'package:loopin/behavior/custom_scroll_behavior.dart'; +import 'package:loopin/api/shop_api.dart'; import 'package:loopin/components/network_or_asset_image.dart'; import 'package:loopin/components/scan_util.dart'; import 'package:loopin/models/conversation_type.dart'; import 'package:loopin/models/conversation_view_model.dart'; import 'package:loopin/styles/index.dart'; import 'package:loopin/utils/parse_message_summary.dart'; +import 'package:loopin/utils/scan_code_type.dart'; // 导入外部枚举 import 'package:shirne_dialog/shirne_dialog.dart'; // systemNotify, // 系统->通知 @@ -30,7 +33,7 @@ class System extends StatefulWidget { class SystemState extends State { late final ChatController controller; - + dynamic writeOffInfo; @override void initState() { super.initState(); @@ -121,6 +124,56 @@ class SystemState extends State { ); } + // 处理扫码结果 + void handleScanResult(String code) { + print('扫码结果:$code'); + + // 使用外部枚举的方法检查扫码类型 + final scanType = ScanCodeType.fromCode(code); + if (scanType != null) { + final value = code.substring(scanType.prefix.length + 1); // 获取后缀值 + _processScanCode(scanType, value); + } else { + // 未知类型的码 + Get.snackbar('未知的二维码类型', '无法识别此二维码: $code'); + } + } + + // 处理不同类型的扫码结果 + void _processScanCode(ScanCodeType type, String value) { + switch (type) { + case ScanCodeType.verification: + _handleVerificationCode(value); + break; + case ScanCodeType.friend: + _handleFriendCode(value); + break; + case ScanCodeType.promotion: + _handlePromotionCode(value); + break; + } + } + + // 处理核销码 + void _handleVerificationCode (String value) async { + print('处理核销码: $value'); + // 带着核销码,跳转到商家的商品详情页面,引导商家去手动点击核销按钮 + Get.toNamed('/sellerOrder/detail', arguments: {'writeOffCodeId':value}); + } + + // 处理好友码 + void _handleFriendCode(String value) { + print('处理好友码: $value'); + // 模仿抖音,去个人页面手动点击关注 + Get.toNamed('/vloger', arguments: {'memberId':value}); + } + + // 处理推广码 + void _handlePromotionCode(String value) { + print('处理推广码: $value'); + Get.toNamed('/vloger', arguments: {'memberId':value}); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -242,11 +295,7 @@ class SystemState extends State { break; case 'scan': print('点击了扫一扫'); - ScanUtil.openScanner(onResult: (code) { - print('扫码结果:$code'); - Get.snackbar('扫码成功', code); - // 在这里继续你的业务逻辑,比如跳转页面、请求接口等 - }); + ScanUtil.openScanner(onResult: handleScanResult); break; } } @@ -441,4 +490,4 @@ class SystemState extends State { ), ); } -} +} \ No newline at end of file diff --git a/lib/pages/my/vloger.dart b/lib/pages/my/vloger.dart index 21ed756..134ef35 100644 --- a/lib/pages/my/vloger.dart +++ b/lib/pages/my/vloger.dart @@ -78,6 +78,7 @@ class MyPageState extends State with SingleTickerProviderStateMixin { void initState() { super.initState(); args = Get.arguments ?? {}; + print('argsssssssssssssssssssssss${args}'); itemsParams = PageParams(); favoriteParams = PageParams(); selfInfo(); diff --git a/lib/pages/order/detail.dart b/lib/pages/order/detail.dart index 2352397..9736ad3 100644 --- a/lib/pages/order/detail.dart +++ b/lib/pages/order/detail.dart @@ -4,9 +4,11 @@ import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:loopin/service/http.dart'; import 'package:loopin/api/shop_api.dart'; +import 'package:loopin/components/my_toast.dart'; import 'package:shirne_dialog/shirne_dialog.dart'; import 'package:timer_count_down/timer_count_down.dart'; import 'package:loopin/utils/wxsdk.dart'; +import 'package:qr_flutter/qr_flutter.dart'; import '../../behavior/custom_scroll_behavior.dart'; import '../../utils/lifecycle_handler.dart'; @@ -18,16 +20,17 @@ class OrderDetail extends StatefulWidget { } class _OrderDetailState extends State with SingleTickerProviderStateMixin { - late String _orderId; + final String _orderId = Get.arguments['orderId'] ?? ''; dynamic orderGoodsInfo; int _initialSeconds = 30 * 60; // 存储初始秒数 bool _countdownFinished = false; // 新增标志位,用于跟踪倒计时是否结束 bool _isLoading = true; // 添加加载状态 + bool _showQrCodeDialog = false; + String _qrCodeUrl = ''; @override void initState() { super.initState(); - _orderId = Get.arguments['orderId'] ?? ''; getOrderDetail(orderId: _orderId); LifecycleHandler.onAppResumed = _onAppResumed; } @@ -61,7 +64,7 @@ class _OrderDetailState extends State with SingleTickerProviderStat _isLoading = true; }); final res = await Http.get('${ShopApi.goodsOrderDetail}/$orderId'); - debugPrint('订单详情-------------->${res['data']}'); + debugPrint(res['data'].toString(), wrapWidth: 600); setState(() { orderGoodsInfo = res['data']; _isLoading = false; @@ -84,6 +87,41 @@ class _OrderDetailState extends State with SingleTickerProviderStat } } + // 核销 + void _writeOffQrCode(verificationCodes) async { + if (verificationCodes != null && verificationCodes.isNotEmpty) { + // 过滤可用的核销码(status为0) + List newVerifyList = verificationCodes.where((item) => item['status'] == 0).toList(); + + if (newVerifyList.isNotEmpty) { + setState(() { + _showQrCodeDialog = true; + _qrCodeUrl = newVerifyList[0]['code'] ?? ''; + }); + } else { + MyToast().tip( + title: '暂无可用的核销码', + position: 'center', + type: 'error', + ); + } + } else { + MyToast().tip( + title: '暂无可用的核销码', + position: 'center', + type: 'error', + ); + } + } + + // 关闭二维码弹框 + void _closeQrCodeDialog() { + setState(() { + _showQrCodeDialog = false; + _qrCodeUrl = ''; + }); + } + // 显示支付结果弹框 void _showPaymentResultDialog() { showDialog( @@ -314,7 +352,7 @@ class _OrderDetailState extends State with SingleTickerProviderStat if (orderGoodsInfo == null) return SizedBox.shrink(); int orderStatus = orderGoodsInfo?['status'] ?? 0; - + List verificationCodes = orderGoodsInfo?['verificationCodes'] ?? []; switch (orderStatus) { case 0: // 待付款 return Row( @@ -353,12 +391,12 @@ class _OrderDetailState extends State with SingleTickerProviderStat children: [ const SizedBox(width: 10.0), ElevatedButton( - onPressed: () {}, + onPressed: () => _writeOffQrCode(verificationCodes), style: ButtonStyle( - backgroundColor: WidgetStateProperty.all(Color(0xFFFF5000)), + backgroundColor: WidgetStateProperty.all(Colors.green), foregroundColor: WidgetStateProperty.all(Colors.white), ), - child: const Text('待核销'), + child: const Text('核销码'), ), ], ); @@ -465,232 +503,118 @@ class _OrderDetailState extends State with SingleTickerProviderStat ); } - @override - Widget build(BuildContext context) { - final productInfo = _getFirstProductInfo(); - - return Scaffold( - backgroundColor: Colors.grey[50], - appBar: AppBar( - backgroundColor: Color(0xFFFF5000), - foregroundColor: Colors.white, - title: Text('订单详情'), - titleSpacing: 1.0, - ), - body: _isLoading - ? Center(child: CircularProgressIndicator()) - : orderGoodsInfo == null - ? emptyTip() - : ScrollConfiguration( - behavior: CustomScrollBehavior().copyWith(scrollbars: false), - child: ListView( - physics: BouncingScrollPhysics(), - padding: EdgeInsets.all(10.0), - children: [ - if (orderGoodsInfo?['status'] == 0) - Container( - padding: EdgeInsets.all(12.0), - margin: EdgeInsets.only(bottom: 10.0), + Widget _buildQrCodeDialog() { + return GestureDetector( + onTap: _closeQrCodeDialog, + child: Container( + color: Colors.black54, + child: Center( + child: GestureDetector( + onTap: () {}, // 阻止点击内容区域关闭 + child: Container( + width: MediaQuery.of(context).size.width * 0.8, + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 10, + offset: Offset(0, 4), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '核销二维码', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + SizedBox(height: 20), + _qrCodeUrl.isNotEmpty + ? Container( + padding: EdgeInsets.all(15), decoration: BoxDecoration( color: Colors.white, - borderRadius: BorderRadius.circular(15.0), - boxShadow: [ - BoxShadow( - color: Colors.black.withAlpha(10), - offset: Offset(0.0, 1.0), - blurRadius: 1.0, - spreadRadius: 0.0, - ), - ], + border: Border.all(color: Colors.grey[300]!), + borderRadius: BorderRadius.circular(8), ), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.info, - size: 16.0, - color: Colors.orange, - ), - SizedBox(width: 4), - Text( - getOrderStatusText(orderGoodsInfo?['status']), - style: TextStyle(color: Colors.orange), - ), - SizedBox(width: 4), - Text( - _countdownFinished ? '倒计时已结束' : '剩余 ', - style: TextStyle(color: Colors.grey), - ), - if (!_countdownFinished) - Countdown( - seconds: _initialSeconds, - build: (_, double time) { - int m = ((time % 3600) ~/ 60).toInt(); - int s = (time % 60).toInt(); - String formatted = "${m.toString().padLeft(2, '0')}:${s.toString().padLeft(2, '0')}"; - - return Text( - formatted, - style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold), - ); - }, - interval: Duration(seconds: 1), - onFinished: () { - print("倒计时结束"); - _cancelOrder(); - setState(() { - _countdownFinished = true; - }); - }, - ), - ], - ), - SizedBox(height: 4), - Text( - _countdownFinished - ? '订单已自动取消' - : '超过30分钟未支付,订单将自动取消', - style: TextStyle(color: Colors.grey, fontSize: 12.0), - ), - ], - ), - ), - // 商品信息 - Container( - margin: EdgeInsets.only(bottom: 10.0), - padding: EdgeInsets.all(10.0), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(15.0), - boxShadow: [ - BoxShadow( - color: Colors.black.withAlpha(10), - offset: Offset(0.0, 1.0), - blurRadius: 1.0, - spreadRadius: 0.0, - ), - ], - ), - child: Column( - children: [ - Row( - children: [ - Spacer(), - Text( - getOrderStatusText(orderGoodsInfo?['status']), - style: TextStyle( - color: getOrderStatusColor(orderGoodsInfo?['status']), - fontWeight: FontWeight.bold, - ), - ) - ], - ), - SizedBox(height: 10), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildProductImage(), - SizedBox(width: 10.0), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - productInfo['productName']?.toString() ?? '商品名称未知', - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle(fontSize: 14), - ), - SizedBox(height: 8), - Row( - children: [ - Text( - '¥${productInfo['salePrice']?.toString() ?? '0.00'}', - style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold), - ), - Spacer(), - Text( - 'x${productInfo['buyNum']?.toString() ?? '1'}', - style: TextStyle(color: Colors.grey), - ), - ], - ), - ], - ), - ) - ], - ), - ], - ), - ), - // 订单信息 - Container( - margin: EdgeInsets.only(bottom: 10.0), - padding: EdgeInsets.all(10.0), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(15.0), - boxShadow: [ - BoxShadow( - color: Colors.black.withAlpha(10), - offset: Offset(0.0, 1.0), - blurRadius: 1.0, - spreadRadius: 0.0, - ), - ], - ), - child: Column( - children: [ - Row( - children: [ - Text( - '订单信息', - style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold), - ), - Spacer(), - InkWell( + child: QrImageView( + data: _qrCodeUrl, + version: QrVersions.auto, + size: 180, + foregroundColor: Colors.black, + backgroundColor: Colors.white, + errorStateBuilder: (cxt, err) { + return Container( + width: 180, + height: 180, + color: Colors.grey[200], + child: Center( child: Icon( - Icons.copy, - color: Colors.grey, - size: 18.0, + Icons.error_outline, + color: Colors.grey[400], + size: 40, ), - onTap: () async { - await Clipboard.setData(ClipboardData(text: _orderId)); - MyDialog.toast('订单已复制到剪切板', icon: Icon(Icons.check_circle)); - }, - ) - ], + ), + ); + }, + ), + ) + : Container( + width: 180, + height: 180, + color: Colors.grey[200], + child: Center( + child: CircularProgressIndicator( + color: Color(0xff07c160), ), - SizedBox(height: 10), - Column( - children: [ - _buildOrderInfoRow('订单号', orderGoodsInfo?['orderId'] ?? ''), - _buildOrderInfoRow('下单时间', orderGoodsInfo?['createTime'] ?? ''), - _buildOrderInfoRow('购买数量', (productInfo['buyNum'] ?? 0).toString()), - _buildOrderInfoRow('订单金额', '¥${orderGoodsInfo?['totalAmount'] ?? '0.00'}'), - _buildOrderInfoRow('实付金额', '¥${orderGoodsInfo?['payAmount'] ?? '0.00'}'), - ], - ) - ], + ), ), - ), - ], + SizedBox(height: 15), + Text( + '订单号: $_orderId', + style: TextStyle( + fontSize: 14, + color: Colors.grey[600], + ), + textAlign: TextAlign.center, ), - ), - // 底部固定按钮 - bottomNavigationBar: orderGoodsInfo == null - ? null - : SafeArea( - minimum: const EdgeInsets.all(10), - child: Container( - height: 60.0, - color: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0), - child: buildBottomButtons(), + SizedBox(height: 10), + Text( + '核销码: $_qrCodeUrl', + style: TextStyle( + fontSize: 12, + color: Colors.grey[500], + fontFamily: 'monospace', + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 20), + ElevatedButton( + onPressed: _closeQrCodeDialog, + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xff07c160), + foregroundColor: Colors.white, + minimumSize: Size(120, 40), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + ), + child: Text('关闭'), + ), + ], ), ), + ), + ), + ), ); } @@ -709,4 +633,240 @@ class _OrderDetailState extends State with SingleTickerProviderStat ), ); } + + @override + Widget build(BuildContext context) { + final productInfo = _getFirstProductInfo(); + + return Scaffold( + backgroundColor: Colors.grey[50], + appBar: AppBar( + backgroundColor: Color(0xFFFF5000), + foregroundColor: Colors.white, + title: Text('订单详情'), + titleSpacing: 1.0, + ), + body: Stack( + children: [ + // 主要内容 + _isLoading + ? Center(child: CircularProgressIndicator()) + : orderGoodsInfo == null + ? emptyTip() + : ScrollConfiguration( + behavior: CustomScrollBehavior().copyWith(scrollbars: false), + child: ListView( + physics: BouncingScrollPhysics(), + padding: EdgeInsets.all(10.0), + children: [ + if (orderGoodsInfo?['status'] == 0) + Container( + padding: EdgeInsets.all(12.0), + margin: EdgeInsets.only(bottom: 10.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15.0), + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha(10), + offset: Offset(0.0, 1.0), + blurRadius: 1.0, + spreadRadius: 0.0, + ), + ], + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.info, + size: 16.0, + color: Colors.orange, + ), + SizedBox(width: 4), + Text( + getOrderStatusText(orderGoodsInfo?['status']), + style: TextStyle(color: Colors.orange), + ), + SizedBox(width: 4), + Text( + _countdownFinished ? '倒计时已结束' : '剩余 ', + style: TextStyle(color: Colors.grey), + ), + if (!_countdownFinished) + Countdown( + seconds: _initialSeconds, + build: (_, double time) { + int m = ((time % 3600) ~/ 60).toInt(); + int s = (time % 60).toInt(); + String formatted = "${m.toString().padLeft(2, '0')}:${s.toString().padLeft(2, '0')}"; + + return Text( + formatted, + style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold), + ); + }, + interval: Duration(seconds: 1), + onFinished: () { + print("倒计时结束"); + _cancelOrder(); + setState(() { + _countdownFinished = true; + }); + }, + ), + ], + ), + SizedBox(height: 4), + Text( + _countdownFinished + ? '订单已自动取消' + : '超过30分钟未支付,订单将自动取消', + style: TextStyle(color: Colors.grey, fontSize: 12.0), + ), + ], + ), + ), + // 商品信息 + Container( + margin: EdgeInsets.only(bottom: 10.0), + padding: EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15.0), + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha(10), + offset: Offset(0.0, 1.0), + blurRadius: 1.0, + spreadRadius: 0.0, + ), + ], + ), + child: Column( + children: [ + Row( + children: [ + Spacer(), + Text( + getOrderStatusText(orderGoodsInfo?['status']), + style: TextStyle( + color: getOrderStatusColor(orderGoodsInfo?['status']), + fontWeight: FontWeight.bold, + ), + ) + ], + ), + SizedBox(height: 10), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildProductImage(), + SizedBox(width: 10.0), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + productInfo['productName']?.toString() ?? '商品名称未知', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 14), + ), + SizedBox(height: 8), + Row( + children: [ + Text( + '¥${productInfo['salePrice']?.toString() ?? '0.00'}', + style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold), + ), + Spacer(), + Text( + 'x${productInfo['buyNum']?.toString() ?? '1'}', + style: TextStyle(color: Colors.grey), + ), + ], + ), + ], + ), + ) + ], + ), + ], + ), + ), + // 订单信息 + Container( + margin: EdgeInsets.only(bottom: 10.0), + padding: EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15.0), + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha(10), + offset: Offset(0.0, 1.0), + blurRadius: 1.0, + spreadRadius: 0.0, + ), + ], + ), + child: Column( + children: [ + Row( + children: [ + Text( + '订单信息', + style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold), + ), + Spacer(), + InkWell( + child: Icon( + Icons.copy, + color: Colors.grey, + size: 18.0, + ), + onTap: () async { + await Clipboard.setData(ClipboardData(text: _orderId)); + MyDialog.toast('订单已复制到剪切板', icon: Icon(Icons.check_circle)); + }, + ) + ], + ), + SizedBox(height: 10), + Column( + children: [ + _buildOrderInfoRow('订单号', orderGoodsInfo?['orderId'] ?? ''), + _buildOrderInfoRow('下单时间', orderGoodsInfo?['createTime'] ?? ''), + _buildOrderInfoRow('购买数量', (productInfo['buyNum'] ?? 0).toString()), + _buildOrderInfoRow('订单金额', '¥${orderGoodsInfo?['totalAmount'] ?? '0.00'}'), + _buildOrderInfoRow('实付金额', '¥${orderGoodsInfo?['payAmount'] ?? '0.00'}'), + ], + ) + ], + ), + ), + ], + ), + ), + + // 二维码对话框(条件渲染) + if (_showQrCodeDialog) _buildQrCodeDialog(), + ], + ), + bottomNavigationBar: orderGoodsInfo == null + ? null + : SafeArea( + minimum: const EdgeInsets.all(10), + child: Container( + height: 60.0, + color: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0), + child: buildBottomButtons(), + ), + ), + ); + } } \ No newline at end of file diff --git a/lib/pages/order/my_order.dart b/lib/pages/order/my_order.dart index cc44349..038bdc6 100644 --- a/lib/pages/order/my_order.dart +++ b/lib/pages/order/my_order.dart @@ -665,6 +665,7 @@ class _MyOrderState extends State with SingleTickerProviderStateMixin { ); }), ), + ); } diff --git a/lib/pages/order/seller_detail.dart b/lib/pages/order/seller_detail.dart index c7726ce..0dcdcb2 100644 --- a/lib/pages/order/seller_detail.dart +++ b/lib/pages/order/seller_detail.dart @@ -5,10 +5,8 @@ import 'package:get/get.dart'; import 'package:loopin/service/http.dart'; import 'package:loopin/api/shop_api.dart'; import 'package:shirne_dialog/shirne_dialog.dart'; -import 'package:timer_count_down/timer_count_down.dart'; -import 'package:loopin/utils/wxsdk.dart'; +import 'package:loopin/components/my_toast.dart'; import '../../behavior/custom_scroll_behavior.dart'; -import '../../utils/lifecycle_handler.dart'; class SellerOrderDetail extends StatefulWidget { const SellerOrderDetail({super.key}); @@ -18,49 +16,54 @@ class SellerOrderDetail extends StatefulWidget { } class _SellerOrderDetailState extends State with SingleTickerProviderStateMixin { - late String _orderId; + late String ? _orderId; + late String ?_writeOffCodeId; dynamic orderGoodsInfo; - int _initialSeconds = 30 * 60; // 存储初始秒数 - bool _countdownFinished = false; // 新增标志位,用于跟踪倒计时是否结束 bool _isLoading = true; // 添加加载状态 @override void initState() { super.initState(); - _orderId = Get.arguments['orderId'] ?? ''; - getOrderDetail(orderId: _orderId); - LifecycleHandler.onAppResumed = _onAppResumed; + _orderId = Get.arguments['orderId'] ?? ''; // 从商家订单列表进入此页面,此时带着订单id + _writeOffCodeId = Get.arguments['writeOffCodeId'] ?? ''; // 从扫码核销进入此页面,此时带着核销码 + if(_orderId != null){ + getOrderDetailByOrderId(_orderId); + }else if(_writeOffCodeId != null){ + getOrderDetailByWriteOffId(_writeOffCodeId); + } + } @override void dispose() { - LifecycleHandler.onAppResumed = null; super.dispose(); } - - void _onAppResumed() { - print('App回到前台,刷新订单状态,订单Id${_orderId}'); - getOrderDetail(orderId: _orderId); // 刷新订单详情数据 - _showPaymentResultDialog(); // 展示支付结果弹框 - } - - // 获取订单状态 - void getOrderRealStatus({required String orderId}) async { - try { - final res = await Http.get('${ShopApi.goodsOrderStatus}/$orderId'); - Get.toNamed('/myOrder'); - } catch (e) { - print('报错-------------->${e}'); - } - } - - // 获取订单详情信息,包含商品参数 - void getOrderDetail({required String orderId}) async { + // 获取订单详情信息,根据订单id + void getOrderDetailByOrderId(orderId) async { try { setState(() { _isLoading = true; }); final res = await Http.get('${ShopApi.goodsOrderDetail}/$orderId'); + debugPrint(res['data'].toString(), wrapWidth: 600); + setState(() { + orderGoodsInfo = res['data']; + _isLoading = false; + }); + } catch (e) { + setState(() { + _isLoading = false; + }); + MyDialog.toast('获取订单详情失败'); + } + } + // 获取订单详情信息,根据核销码 + void getOrderDetailByWriteOffId(code) async { + try { + setState(() { + _isLoading = true; + }); + final res = await Http.get('${ShopApi.getWriteOffDetail}?code=$code'); print('订单详情-------------->${res['data']}'); setState(() { orderGoodsInfo = res['data']; @@ -74,123 +77,44 @@ class _SellerOrderDetailState extends State with SingleTicker } } - // 取消订单 - void _cancelOrder() async { + // 确认核销订单 + void _confirmVerifyCode () async { + late String writeOffCode; + if(_orderId != null){ // 商家可以直接从订单详情获取核销码,并过滤后进行核销 + var verificationCodesList = orderGoodsInfo['verificationCodes']; + if (verificationCodesList != null && verificationCodesList.isNotEmpty) { + // 过滤可用的核销码(status为0) + List newVerifyList = verificationCodesList.where((item) => item['status'] == 0).toList(); + if (newVerifyList.isNotEmpty) { + writeOffCode = newVerifyList[0]['code'] ?? ''; + } else { + return MyToast().tip( + title: '暂无可用的核销码', + position: 'center', + type: 'error', + ); + } + } else { + return MyToast().tip( + title: '暂无可用的核销码', + position: 'center', + type: 'error', + ); + } + }else if(_writeOffCodeId != null){ + writeOffCode = _writeOffCodeId?? ''; + } try { - final res = await Http.post('${ShopApi.cancelGoodsOrder}/$_orderId'); - getOrderDetail(orderId: _orderId); // 刷新订单详情数据 - } catch (e) { - MyDialog.toast('取消订单失败'); + final res = await Http.get('${ShopApi.confirmWriteOff}?code=$writeOffCode'); + debugPrint(res['data'].toString(), wrapWidth: 600); + MyToast().tip( + title: '核销成功', + position: 'center', + type: 'error', + ); + } catch (e) { } } - - // 显示支付结果弹框 - void _showPaymentResultDialog() { - showDialog( - context: context, - barrierDismissible: false, - barrierColor: Colors.black54, - builder: (BuildContext context) { - return Dialog( - backgroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // 图标 - Container( - width: 60, - height: 60, - decoration: BoxDecoration( - color: Color(0xFFFFF8E6), - shape: BoxShape.circle, - ), - child: Icon( - Icons.payment, - size: 32, - color: Color(0xFFFFA500), - ), - ), - SizedBox(height: 16), - - // 标题 - Text( - '支付确认', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - SizedBox(height: 8), - - // 描述 - Text( - '请确认您的支付状态', - style: TextStyle( - fontSize: 14, - color: Colors.grey[600], - ), - textAlign: TextAlign.center, - ), - SizedBox(height: 24), - - // 按钮区域 - Row( - children: [ - // 支付遇到问题按钮 - Expanded( - child: OutlinedButton( - onPressed: () { - Navigator.of(context).pop(); - getOrderRealStatus(orderId: _orderId); // 主动再次拉取订单状态 - }, - style: OutlinedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: Colors.grey[700], - side: BorderSide(color: Colors.grey[300]!), - padding: EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: Text('支付遇到问题'), - ), - ), - SizedBox(width: 12), - - // 支付完成按钮 - Expanded( - child: ElevatedButton( - onPressed: () { - Navigator.of(context).pop(); - getOrderRealStatus(orderId: _orderId); // 同时主动拉取订单状态 - }, - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFFFF5000), - foregroundColor: Colors.white, - padding: EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: Text('支付完成'), - ), - ), - ], - ), - ], - ), - ), - ); - }, - ); - } - // 获取订单状态文本 String getOrderStatusText(int status) { switch (status) { @@ -383,32 +307,7 @@ class _SellerOrderDetailState extends State with SingleTicker case 0: // 待付款 return Row( mainAxisAlignment: MainAxisAlignment.end, - children: [ - ElevatedButton( - onPressed: () { - // 取消订单逻辑 - _cancelOrder(); - }, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all(Colors.white), - foregroundColor: WidgetStateProperty.all(Colors.black87), - side: WidgetStateProperty.all(BorderSide(color: Colors.grey[300]!)), - ), - child: const Text('取消订单'), - ), - const SizedBox(width: 10.0), - ElevatedButton( - onPressed: () { - // 打开微信小程序的某个页面地址,如pages/index/index - Wxsdk.openMiniApp(orderId: _orderId); - }, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all(Color(0xff07c160)), - foregroundColor: WidgetStateProperty.all(Colors.white), - ), - child: const Text('去支付'), - ), - ], + children: [], ); case 1: // 待核销 @@ -417,12 +316,14 @@ class _SellerOrderDetailState extends State with SingleTicker children: [ const SizedBox(width: 10.0), ElevatedButton( - onPressed: () {}, + onPressed: () { + _confirmVerifyCode(); + }, style: ButtonStyle( backgroundColor: WidgetStateProperty.all(Color(0xFFFF5000)), foregroundColor: WidgetStateProperty.all(Colors.white), ), - child: const Text('待核销'), + child: const Text('确认核销'), ), ], ); @@ -430,81 +331,31 @@ class _SellerOrderDetailState extends State with SingleTicker case 2: // 已完成 return Row( mainAxisAlignment: MainAxisAlignment.end, - children: [ - const SizedBox(width: 10.0), - ElevatedButton( - onPressed: () {}, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all(Colors.green), - foregroundColor: WidgetStateProperty.all(Colors.white), - ), - child: const Text('已完成'), - ), - ], + children: [], ); case 3: // 已关闭 return Row( mainAxisAlignment: MainAxisAlignment.end, - children: [ - const SizedBox(width: 10.0), - ElevatedButton( - onPressed: () {}, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all(Colors.grey), - foregroundColor: WidgetStateProperty.all(Colors.white), - ), - child: const Text('已关闭'), - ), - ], + children: [], ); case 4: // 退款中 return Row( mainAxisAlignment: MainAxisAlignment.end, - children: [ - const SizedBox(width: 10.0), - ElevatedButton( - onPressed: () {}, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all(Colors.orange), - foregroundColor: WidgetStateProperty.all(Colors.white), - ), - child: const Text('退款中'), - ), - ], + children: [], ); case 5: // 已退款 return Row( mainAxisAlignment: MainAxisAlignment.end, - children: [ - const SizedBox(width: 10.0), - ElevatedButton( - onPressed: () {}, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all(Colors.grey), - foregroundColor: WidgetStateProperty.all(Colors.white), - ), - child: const Text('已退款'), - ), - ], + children: [], ); case 6: // 已取消 return Row( mainAxisAlignment: MainAxisAlignment.end, - children: [ - const SizedBox(width: 10.0), - ElevatedButton( - onPressed: () {}, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all(Colors.grey), - foregroundColor: WidgetStateProperty.all(Colors.white), - ), - child: const Text('已取消'), - ), - ], + children: [], ); default: @@ -549,7 +400,7 @@ class _SellerOrderDetailState extends State with SingleTicker physics: BouncingScrollPhysics(), padding: EdgeInsets.all(10.0), children: [ - if (orderGoodsInfo?['status'] == 0) // 修正:应该是 status 而不是 orderStatus + if (orderGoodsInfo?['status'] == 0) Container( padding: EdgeInsets.all(12.0), margin: EdgeInsets.only(bottom: 10.0), @@ -581,41 +432,11 @@ class _SellerOrderDetailState extends State with SingleTicker style: TextStyle(color: Colors.orange), ), SizedBox(width: 4), - Text( - _countdownFinished ? '倒计时已结束' : '剩余 ', - style: TextStyle(color: Colors.grey), - ), - if (!_countdownFinished) - Countdown( - seconds: _initialSeconds, - build: (_, double time) { - int m = ((time % 3600) ~/ 60).toInt(); - int s = (time % 60).toInt(); - String formatted = "${m.toString().padLeft(2, '0')}:${s.toString().padLeft(2, '0')}"; - return Text( - formatted, - style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold), - ); - }, - interval: Duration(seconds: 1), - onFinished: () { - print("倒计时结束"); - _cancelOrder(); - setState(() { - _countdownFinished = true; - }); - }, - ), + ], ), SizedBox(height: 4), - Text( - _countdownFinished - ? '订单已自动取消' - : '超过30分钟未支付,订单将自动取消', - style: TextStyle(color: Colors.grey, fontSize: 12.0), - ), ], ), ), @@ -686,7 +507,7 @@ class _SellerOrderDetailState extends State with SingleTicker size: 18.0, ), onTap: () async { - await Clipboard.setData(ClipboardData(text: _orderId)); + await Clipboard.setData(ClipboardData(text: _orderId??'')); MyDialog.toast('订单已复制到剪切板', icon: Icon(Icons.check_circle)); }, ) @@ -717,7 +538,7 @@ class _SellerOrderDetailState extends State with SingleTicker height: 60.0, color: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0), - child: buildBottomButtons(), + // child: buildBottomButtons(), ), ), ); diff --git a/lib/pages/video/module/attention.dart b/lib/pages/video/module/attention.dart index 3b18176..c3422e3 100644 --- a/lib/pages/video/module/attention.dart +++ b/lib/pages/video/module/attention.dart @@ -1136,7 +1136,8 @@ class _AttentionModuleState extends State { onTap: () async { player.pause(); // 跳转到 Vloger 页面并等待返回结果 - final result = await Get.toNamed('/vloger', arguments: videoList[videoModuleController.videoPlayIndex.value]); + final vloggerId = videoList[videoModuleController.videoPlayIndex.value]['memberId']; + final result = await Get.toNamed('/vloger', arguments: {'memberId':vloggerId}); if (result != null) { // 处理返回的参数 print('返回的数据: ${result['followStatus']}'); diff --git a/lib/pages/video/module/friend.dart b/lib/pages/video/module/friend.dart index da71114..7936fec 100644 --- a/lib/pages/video/module/friend.dart +++ b/lib/pages/video/module/friend.dart @@ -1141,7 +1141,8 @@ class _FriendModuleState extends State { onTap: () async { player.pause(); // 跳转到 Vloger 页面并等待返回结果 - final result = await Get.toNamed('/vloger', arguments: videoList[videoModuleController.videoPlayIndex.value]); + final vloggerId = videoList[videoModuleController.videoPlayIndex.value]['memberId']; + final result = await Get.toNamed('/vloger', arguments: {'memberId':vloggerId}); if (result != null) { // 处理返回的参数 print('返回的数据: ${result['followStatus']}'); diff --git a/lib/pages/video/module/recommend.dart b/lib/pages/video/module/recommend.dart index 273116f..335ec49 100644 --- a/lib/pages/video/module/recommend.dart +++ b/lib/pages/video/module/recommend.dart @@ -1125,7 +1125,8 @@ class _RecommendModuleState extends State { onTap: () async { player.pause(); // 跳转到 Vloger 页面并等待返回结果 - final result = await Get.toNamed('/vloger', arguments: videoList[videoModuleController.videoPlayIndex.value]); + final vloggerId = videoList[videoModuleController.videoPlayIndex.value]['memberId']; + final result = await Get.toNamed('/vloger', arguments: {'memberId':vloggerId}); if (result != null) { // 处理返回的参数 print('返回的数据: ${result['followStatus']}'); diff --git a/lib/utils/scan_code_type.dart b/lib/utils/scan_code_type.dart new file mode 100644 index 0000000..2453655 --- /dev/null +++ b/lib/utils/scan_code_type.dart @@ -0,0 +1,29 @@ +// 扫码 类型 +enum ScanCodeType { + verification('hxm'), // 核销码 + friend('hym'), // 好友码 + promotion('tgm'); // 推广码 + + final String prefix; + const ScanCodeType(this.prefix); + + // 根据前缀获取对应的枚举值 + static ScanCodeType? fromPrefix(String prefix) { + for (var type in ScanCodeType.values) { + if (type.prefix == prefix) { + return type; + } + } + return null; + } + + // 根据完整code获取对应的枚举值 + static ScanCodeType? fromCode(String code) { + for (var type in ScanCodeType.values) { + if (code.startsWith('${type.prefix}-')) { + return type; + } + } + return null; + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index a59ec96..ff06fce 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1093,6 +1093,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "6.1.5" + qr: + dependency: transitive + description: + name: qr + sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.2" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.1.0" record: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 1fc8843..bee201f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,8 @@ dependencies: card_swiper: ^3.0.1 flutter_svg: ^2.0.16 easy_refresh: ^3.4.0 + # 二维码 + qr_flutter: ^4.1.0 # 腾讯IM tencent_cloud_chat_sdk: ^8.6.7019+2 # 离线推送