diff --git a/assets/images/bg_frame_module_pic.png b/assets/images/bg_frame_module.png index 777b5a5..777b5a5 100644 --- a/assets/images/bg_frame_module_pic.png +++ b/assets/images/bg_frame_module.png diff --git a/lib/common/request/apis.dart b/lib/common/request/apis.dart index c078760..a0e61fb 100644 --- a/lib/common/request/apis.dart +++ b/lib/common/request/apis.dart @@ -85,4 +85,11 @@ class Apis { /// 商品列表 static const String productList = 'order/course/combo/list'; + + /// 创建订单 + static const String createOrder = 'order/create/order'; + + /// 获取阿里支付token + static const String getAliPayToken = 'pay/alipay/token'; + } diff --git a/lib/common/request/dao/shop_dao.dart b/lib/common/request/dao/shop_dao.dart new file mode 100644 index 0000000..73c1850 --- /dev/null +++ b/lib/common/request/dao/shop_dao.dart @@ -0,0 +1,21 @@ +import '../../../models/product_entity.dart'; +import '../request_client.dart'; + +class ShopDao { + ///商品列表 + static Future productList() async { + return await requestClient.get>(Apis.productList); + } + + ///创建订单 + static Future createOrder(ProductEntity productEntity) async { + return await requestClient + .post>(Apis.createOrder, data: {'courseComboId': productEntity.id}); + } + + ///获取ali支付订单信息 + static Future getAliPayToken(String orderNo) async { + return await requestClient + .post>(Apis.getAliPayToken, data: {'orderNo': orderNo}); + } +} diff --git a/lib/pages/games/bloc.dart b/lib/pages/games/bloc.dart index f21ae28..51952b4 100644 --- a/lib/pages/games/bloc.dart +++ b/lib/pages/games/bloc.dart @@ -1,14 +1,38 @@ import 'package:bloc/bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; +import 'package:wow_english/common/extension/string_extension.dart'; import 'event.dart'; +import 'game_entity.dart'; import 'state.dart'; class GamesBloc extends Bloc { late MethodChannel _methodChannel; + //手动初始化4个GameEntity对象 + final List _games = [ + GameEntity() + ..id = 1 + ..imageName = 'pic_module_game'.assetPng + ..name = '游戏1', + GameEntity() + ..id = 2 + ..imageName = 'pic_module_game'.assetPng + ..name = '游戏2', + GameEntity() + ..id = 3 + ..imageName = 'pic_module_game'.assetPng + ..name = '游戏3', + GameEntity() + ..id = 4 + ..imageName = 'pic_module_game'.assetPng + ..name = '游戏4' + ]; + + List get listData => _games; + GamesBloc() : super(GamesState().init()) { on(_init); on(_gotoGamePage); diff --git a/lib/pages/games/game_entity.dart b/lib/pages/games/game_entity.dart new file mode 100644 index 0000000..ce642bf --- /dev/null +++ b/lib/pages/games/game_entity.dart @@ -0,0 +1,11 @@ + + + +class GameEntity { + + late int id; + + late String imageName; + + late String name; +} \ No newline at end of file diff --git a/lib/pages/games/view.dart b/lib/pages/games/view.dart index 1dcc9d3..8c3ad75 100644 --- a/lib/pages/games/view.dart +++ b/lib/pages/games/view.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/common/widgets/we_app_bar.dart'; import 'package:wow_english/pages/games/state.dart'; @@ -26,12 +25,10 @@ class _GamesPageView extends StatelessWidget { @override Widget build(BuildContext context) { return BlocListener( - listener: (context, state) { - - }, + listener: (context, state) {}, child: Scaffold( appBar: const WEAppBar( - titleText: '游戏列表', + titleText: '游戏专区', centerTitle: false, ), body: _gamesView(), @@ -39,41 +36,39 @@ class _GamesPageView extends StatelessWidget { ); } - Widget _gamesView() => BlocBuilder( - builder: (context, state) { + Widget _gamesView() => + BlocBuilder(builder: (context, state) { final bloc = BlocProvider.of(context); //屏幕中间横着放四张图片一行展示(尺寸120*200),每张图片下方有行文字 - return GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 4, - crossAxisSpacing: 10, - mainAxisSpacing: 10, - childAspectRatio: 0.6, - ), - itemCount: 4, - itemBuilder: (BuildContext context, int index) { - // final entity = bloc.listData[index]; - return GestureDetector( - onTap: () { - bloc.add(GotoGamePageEvent(1)); - }, - child: Container( - decoration: BoxDecoration( - border: Border.all( - width: 1.0, - color: const Color(0xFF140C10), - ), - borderRadius: BorderRadius.circular(21), - ), - child: Column( - children: [ - Image.asset('pic_module_study'.assetPng, width: 120, height: 200), - Text('游戏名称', style: TextStyle(fontSize: 14.sp, color: const Color(0xFF140C10))) - ], - ), + return Container( + margin: EdgeInsets.symmetric(horizontal: 50.0.w), + child: GridView.builder( + padding: EdgeInsets.zero, + // physics: const NeverScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + crossAxisSpacing: 10, + mainAxisSpacing: 10, + childAspectRatio: 0.6, ), - ); + itemCount: bloc.listData.length, + itemBuilder: (BuildContext context, int index) { + final gameEntity = bloc.listData[index]; + return GestureDetector( + onTap: () { + bloc.add(GotoGamePageEvent(gameEntity.id)); + }, + child: Column( + children: [ + Image.asset(gameEntity.imageName, + width: 120, height: 200), + Text(gameEntity.name, + style: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF140C10))) + ], + ), + ); + })); }); - }); } - diff --git a/lib/pages/moduleSelect/view.dart b/lib/pages/moduleSelect/view.dart index 076a6d7..463fbd5 100644 --- a/lib/pages/moduleSelect/view.dart +++ b/lib/pages/moduleSelect/view.dart @@ -52,8 +52,18 @@ class _HomePageView extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Image.asset('pic_module_study'.assetPng, - width: 162.5.w, height: 203.5.h), + Stack( + alignment: AlignmentDirectional.center, + children: [ + Image.asset('bg_frame_module'.assetPng, + width: 162.5.w, height: 203.5.h), + Center( + child: Image.asset( + 'pic_module_study'.assetPng, + width: 140.5.w, + height: 172.h), + ) + ]), 10.verticalSpace, Image.asset('label_module_study'.assetPng, width: 124.w, height: 34.h), @@ -69,8 +79,14 @@ class _HomePageView extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Image.asset('pic_module_game'.assetPng, - width: 162.5.w, height: 203.5.h), + Stack( + alignment: AlignmentDirectional.center, + children: [ + Image.asset('bg_frame_module'.assetPng, + width: 162.5.w, height: 203.5.h), + Image.asset('pic_module_game'.assetPng, + width: 140.5.w, height: 172.h) + ]), 10.verticalSpace, Image.asset('label_module_game'.assetPng, width: 124.w, height: 34.h), diff --git a/lib/pages/shop/home/widgets/product_item.dart b/lib/pages/shop/home/widgets/product_item.dart index 8fd000f..cab0371 100644 --- a/lib/pages/shop/home/widgets/product_item.dart +++ b/lib/pages/shop/home/widgets/product_item.dart @@ -31,37 +31,36 @@ class ProductItem extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Container( - // width: 124.w, - // decoration: BoxDecoration( - // border: Border.all( - // width: 1.0, - // color: const Color(0xFF333333), - // ), - // image: const DecorationImage( - // - // image: NetworkImage(entity?.coverUrl??''), - // ) - // ), - // ), - CachedNetworkImage( - imageUrl: entity?.picUrl ?? '', - fit: BoxFit.fill, - imageBuilder: (context, imageProvider) => - Container( - decoration: BoxDecoration( - border: Border.all( - width: 1.0, - color: const Color(0xFF333333), - ), - borderRadius: BorderRadius.circular(5.0), - ), - ), - placeholder: (context, url) => CircularProgressIndicator(), - // errorWidget: (context, url, error) => const Icon(Icons.error), - height: 124.h, + Container( width: 124.w, + decoration: BoxDecoration( + border: Border.all( + width: 1.0, + color: const Color(0xFF333333), + ), + image: DecorationImage( + image: NetworkImage(entity?.picUrl ?? ''), + ) + ), ), + // CachedNetworkImage( + // imageUrl: entity?.picUrl ?? '', + // fit: BoxFit.fill, + // imageBuilder: (context, imageProvider) => + // Container( + // decoration: BoxDecoration( + // border: Border.all( + // width: 1.0, + // color: const Color(0xFF333333), + // ), + // borderRadius: BorderRadius.circular(5.0), + // ), + // ), + // placeholder: (context, url) => const CircularProgressIndicator(), + // // errorWidget: (context, url, error) => const Icon(Icons.error), + // height: 124.h, + // width: 124.w, + // ), 21.5.horizontalSpace, Expanded( child: Column( diff --git a/lib/pages/shopping/bloc.dart b/lib/pages/shopping/bloc.dart index e56c61f..c929830 100644 --- a/lib/pages/shopping/bloc.dart +++ b/lib/pages/shopping/bloc.dart @@ -1,7 +1,14 @@ import 'package:bloc/bloc.dart'; +import 'package:fluwx/fluwx.dart'; +import 'package:tobias/tobias.dart'; +import 'package:wow_english/generated/json/base/json_convert_content.dart'; import 'package:wow_english/models/product_entity.dart'; +import '../../common/request/dao/shop_dao.dart'; +import '../../common/request/exception.dart'; +import '../../utils/loading.dart'; import '../../utils/log_util.dart'; +import '../../utils/toast_util.dart'; import 'event.dart'; import 'state.dart'; @@ -20,6 +27,7 @@ class ShoppingBloc extends Bloc { //页面初始化时刻 on(_init); on(_changePaymentChannel); + on(_startPay); } void _init(InitEvent event, Emitter emit) async { @@ -37,8 +45,67 @@ class ShoppingBloc extends Bloc { } } - void _gotoPay() { - // 跳转到支付页面 + void _startPay(DoPayEvent event, + Emitter emitter) async { + Log.d("开始支付 ${event.productEntity} ${event.paymentChannel}"); + //如果event.productEntity为空,中断流程并toast提示 + if (event.productEntity == null) { + showToast("商品信息为空"); + return; + } + final productEntitySafely = event.productEntity!; + try { + await loading(() async { + final Map orderInfo = await ShopDao.createOrder(productEntitySafely); + Log.d("orderInfo $orderInfo"); + final String? orderNo = orderInfo.getOrNull("orderNo"); + if (orderNo == null) { + showToast("订单创建失败"); + return; + } + Log.d("orderNo $orderNo"); + + if (event.paymentChannel == PaymentChannel.wechatPay) { + Fluwx fluwx = Fluwx(); + fluwx.registerApi(appId: "wxd930ea5d5a228f5f", + universalLink: "https://app-api.wowenglish.com.cn/.well-known/apple-app-site-association"); + // fluwx.pay( + // which: Payment( + // appId: _orderInfo['appid'].toString(), + // partnerId: _orderInfo['partnerid'].toString(), + // prepayId: _orderInfo['prepayid'].toString(), + // packageValue: _orderInfo['package'].toString(), + // nonceStr: _orderInfo['noncestr'].toString(), + // timestamp: _orderInfo['timestamp'], + // sign: _orderInfo['sign'].toString(), + // )); + } else { + final Map aliPayOrderInfo = await ShopDao.getAliPayToken(orderNo); + Log.d("aliPayOrderInfo=$aliPayOrderInfo type=${aliPayOrderInfo.runtimeType}"); + final String? aliPayInfo = aliPayOrderInfo.getOrNull("token"); + if (aliPayInfo == null) { + showToast("支付宝订单创建失败"); + return; + } + Log.d("aliPayInfo=$aliPayInfo"); + ///打印aliPayOrderInfo的type + Tobias tobias = Tobias(); + final Map aliPayResult = await tobias.pay(aliPayInfo); + Log.d("aliPayResult=$aliPayResult"); + // 判断resultStatus 为9000则代表支付成功 + if (aliPayResult.getOrNull("resultStatus") == "9000") { + showToast("支付成功"); + emit(PaySuccessState()); + } else { + showToast("支付失败"); + } + } + }); + } catch (e) { + if (e is ApiException) { + showToast(e.message ?? '请求失败,请检查网络连接'); + } + } } } diff --git a/lib/pages/shopping/event.dart b/lib/pages/shopping/event.dart index ead0a38..62b0db0 100644 --- a/lib/pages/shopping/event.dart +++ b/lib/pages/shopping/event.dart @@ -1,3 +1,5 @@ +import 'package:wow_english/models/product_entity.dart'; + import 'bloc.dart'; abstract class ShoppingEvent {} @@ -8,4 +10,13 @@ class ChangePaymentChannelEvent extends ShoppingEvent { final PaymentChannel paymentChannel; ChangePaymentChannelEvent(this.paymentChannel); +} + +class DoPayEvent extends ShoppingEvent { + + final ProductEntity? productEntity; + + final PaymentChannel paymentChannel; + + DoPayEvent(this.productEntity, this.paymentChannel); } \ No newline at end of file diff --git a/lib/pages/shopping/state.dart b/lib/pages/shopping/state.dart index 39088d4..28f9de1 100644 --- a/lib/pages/shopping/state.dart +++ b/lib/pages/shopping/state.dart @@ -8,4 +8,6 @@ class ShoppingState { } } -class PaymentChannelChangeState extends ShoppingState {} \ No newline at end of file +class PaymentChannelChangeState extends ShoppingState {} + +class PaySuccessState extends ShoppingState {} \ No newline at end of file diff --git a/lib/pages/shopping/view.dart b/lib/pages/shopping/view.dart index 5237fe9..32cdcfe 100644 --- a/lib/pages/shopping/view.dart +++ b/lib/pages/shopping/view.dart @@ -8,6 +8,7 @@ import 'package:wow_english/models/product_entity.dart'; import '../../common/core/assets_const.dart'; import '../../common/widgets/we_app_bar.dart'; import '../../utils/image_util.dart'; +import '../../utils/log_util.dart'; import 'bloc.dart'; import 'event.dart'; import 'state.dart'; @@ -66,13 +67,18 @@ class _ShoppingView extends StatelessWidget { @override Widget build(BuildContext context) { final bloc = BlocProvider.of(context); + // var title1 = bloc.productData?.name ?? ''; return BlocListener( listener: (context, state) { + Log.d("wqf state=$state"); + if (state is PaySuccessState) { + Navigator.pop(context); + } }, child: Scaffold( appBar: const WEAppBar( //标题传进来的 - titleText: '支付', + titleText: "商品详情", ), body: Container( margin: const EdgeInsets.only(left: 80.0, top: 28.0, right: 56.0), @@ -80,7 +86,7 @@ class _ShoppingView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ CachedNetworkImage( - imageUrl: "${bloc.productData?.detailPicUrl}", + imageUrl: bloc.productData?.detailPicUrl ?? '', imageBuilder: (context, imageProvider) => Container( decoration: BoxDecoration( @@ -91,8 +97,7 @@ class _ShoppingView extends StatelessWidget { borderRadius: BorderRadius.circular(5.0), ), ), - placeholder: (context, url) => - const CircularProgressIndicator(), + placeholder: (context, url) => const CircularProgressIndicator(), // errorWidget: (context, url, error) => const Icon(Icons.error), height: 210.0.h, width: 210.0.w, @@ -153,14 +158,15 @@ Widget _paymentWidget() => text: PaymentChannel.aliPay.payChannelName, groupValue: bloc.curPaymentChannel.payChannelType, onChanged: (newValue) { - bloc.add( - ChangePaymentChannelEvent(PaymentChannel.aliPay)); + bloc.add(ChangePaymentChannelEvent(PaymentChannel.aliPay)); }, ), const SizedBox(height: 15.0), // 确认支付按钮 InkWell( onTap: () { + Log.d('点击支付按钮 ${bloc.productData}'); + bloc.add(DoPayEvent(bloc.productData, bloc.curPaymentChannel)); }, child: Image( width: 125.w, diff --git a/pubspec.yaml b/pubspec.yaml index 63705f8..a907ebf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -66,7 +66,7 @@ dependencies: # 拍照,从相册中选择 https://pub.flutter-io.cn/packages/image_picker image_picker: ^0.8.7+5 # 支付宝支付SDK https://pub.flutter-io.cn/packages/tobias - tobias: ^3.1.0 + tobias: ^3.3.2 # 微信SDK相关 https://pub.flutter-io.cn/packages/fluwx fluwx: ^4.5.5 # json数据解析 https://pub.flutter-io.cn/packages/json_annotation @@ -74,7 +74,9 @@ dependencies: # double丢失精度问题 https://pub.dev/packages/decimal decimal: ^2.3.2 # 网络图片缓存 https://pub.flutter-io.cn/packages/cached_network_image - cached_network_image: ^3.2.3 + cached_network_image: ^3.3.1 +# # 网络图片缓存 https://pub.dev/packages/extended_image +# extended_image: ^4.0.0 # 常用工具类(时间轴,倒计时等) https://pub.flutter-io.cn/packages/common_utils common_utils: ^2.1.0 # 获取设备信息 https://pub.flutter-io.cn/packages/device_info_plus