Quellcode durchsuchen

Merge remote-tracking branch 'origin/v1.0.0' into v1.0.0

hezihao vor 8 Monaten
Ursprung
Commit
713eb7f165
42 geänderte Dateien mit 1135 neuen und 32 gelöschten Zeilen
  1. 1 0
      assets/anim/anim_discount_ticket_dialog_data.json
  2. 1 0
      assets/anim/anim_surprise_dialog_data.json
  3. 1 0
      assets/anim/anim_tab_character_selected_data.json
  4. 1 0
      assets/anim/anim_tab_keyboard_selected_data.json
  5. 1 0
      assets/anim/anim_tab_mine_selected_data.json
  6. BIN
      assets/images/bg_surprise_dialog.webp
  7. BIN
      assets/images/bg_surprise_dialog_button.webp
  8. BIN
      assets/images/bg_surprise_dialog_prices.webp
  9. BIN
      assets/images/bg_ticket.dialog.webp
  10. BIN
      assets/images/bg_ticket_dialog_prices.webp
  11. BIN
      assets/images/bg_ticket_dialog_prices2.webp
  12. BIN
      assets/images/icon_surprise_dialog_button.webp
  13. BIN
      assets/images/icon_surprise_dialog_only.webp
  14. BIN
      assets/images/icon_surprise_dialog_title.webp
  15. BIN
      assets/images/icon_ticket_dialog_button.webp
  16. 5 0
      assets/string/base/string.xml
  17. 7 0
      lib/data/api/atmob_api.dart
  18. 33 0
      lib/data/api/atmob_api.g.dart
  19. 21 0
      lib/data/api/response/item_retention_response.dart
  20. 27 0
      lib/data/api/response/item_retention_response.g.dart
  21. 7 0
      lib/data/repository/store_repository.dart
  22. 4 0
      lib/di/get_it.config.dart
  23. 5 5
      lib/dialog/content/character_tab_group_content_controller.dart
  24. 5 1
      lib/module/character/content/character_group_content_controller.dart
  25. 1 1
      lib/module/character/content/character_group_content_view.dart
  26. 18 10
      lib/module/character_custom/character_custom_page.dart
  27. 12 3
      lib/module/character_custom/detail/character_custom_detail_controller.dart
  28. 3 3
      lib/module/main/main_controller.dart
  29. 15 5
      lib/module/main/main_page.dart
  30. 17 2
      lib/module/mine/mine_controller.dart
  31. 9 0
      lib/module/store/discount/discount_controller.dart
  32. 33 0
      lib/module/store/discount/discount_view.dart
  33. 1 1
      lib/module/store/store_page.dart
  34. 99 0
      lib/module/store/suprise/goods_surprise_controller.dart
  35. 303 0
      lib/module/store/suprise/surprise_dialog.dart
  36. 286 0
      lib/module/store/ticket/discount_ticket_dialog.dart
  37. 84 0
      lib/resource/assets.gen.dart
  38. 4 0
      lib/resource/string.gen.dart
  39. 6 0
      lib/router/app_pages.dart
  40. 0 0
      lib/widget/horizontal_dashed_line.dart
  41. 120 1
      pubspec.lock
  42. 5 0
      pubspec.yaml

Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
assets/anim/anim_discount_ticket_dialog_data.json


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
assets/anim/anim_surprise_dialog_data.json


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
assets/anim/anim_tab_character_selected_data.json


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
assets/anim/anim_tab_keyboard_selected_data.json


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
assets/anim/anim_tab_mine_selected_data.json


BIN
assets/images/bg_surprise_dialog.webp


BIN
assets/images/bg_surprise_dialog_button.webp


BIN
assets/images/bg_surprise_dialog_prices.webp


BIN
assets/images/bg_ticket.dialog.webp


BIN
assets/images/bg_ticket_dialog_prices.webp


BIN
assets/images/bg_ticket_dialog_prices2.webp


BIN
assets/images/icon_surprise_dialog_button.webp


BIN
assets/images/icon_surprise_dialog_only.webp


BIN
assets/images/icon_surprise_dialog_title.webp


BIN
assets/images/icon_ticket_dialog_button.webp


+ 5 - 0
assets/string/base/string.xml

@@ -188,4 +188,9 @@
     <string name="pay_success_dialog_confirm">知道了</string>
     <string name="pay_success_dialog_permanent">恭喜您成为终身会员,感谢您的支持</string>
 
+
+    <string name="surprise_dialog_end_desc">后结束</string>
+    <string name="surprise_dialog_only">仅需</string>
+
+
 </resources>

+ 7 - 0
lib/data/api/atmob_api.dart

@@ -29,6 +29,7 @@ import 'package:keyboard/data/api/response/character_page_response.dart';
 import 'package:keyboard/data/api/response/character_unlock_response.dart';
 import 'package:keyboard/data/api/response/config_response.dart';
 import 'package:keyboard/data/api/response/item_list_response.dart';
+import 'package:keyboard/data/api/response/item_retention_response.dart';
 import 'package:keyboard/data/api/response/keyboard_character_list_response.dart';
 import 'package:keyboard/data/api/response/keyboard_list_response.dart';
 import 'package:keyboard/data/api/response/login_response.dart';
@@ -182,8 +183,14 @@ abstract class AtmobApi {
   Future<BaseResponse<OrderPayResponse>> orderPay(@Body() OrderPayRequest request);
 
 
+  // 查询支付结果
   @POST("/project/keyboard/v1/order/status")
   Future<BaseResponse<OrderStatusResponse>> orderStatus(
       @Body() OrderStatusRequest request);
 
+  //获取挽留弹框商品
+  @POST("/project/keyboard/v1/item/detainment")
+  Future<BaseResponse<ItemRetentionResponse>> getItemRetention(
+      @Body() AppBaseRequest request);
+
 }

+ 33 - 0
lib/data/api/atmob_api.g.dart

@@ -864,6 +864,39 @@ class _AtmobApi implements AtmobApi {
     return _value;
   }
 
+  @override
+  Future<BaseResponse<ItemRetentionResponse>> getItemRetention(
+    AppBaseRequest request,
+  ) async {
+    final _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{};
+    final _headers = <String, dynamic>{};
+    final _data = <String, dynamic>{};
+    _data.addAll(request.toJson());
+    final _options = _setStreamType<BaseResponse<ItemRetentionResponse>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/item/detainment',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<ItemRetentionResponse> _value;
+    try {
+      _value = BaseResponse<ItemRetentionResponse>.fromJson(
+        _result.data!,
+        (json) => ItemRetentionResponse.fromJson(json as Map<String, dynamic>),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
   RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
     if (T != dynamic &&
         !(requestOptions.responseType == ResponseType.bytes ||

+ 21 - 0
lib/data/api/response/item_retention_response.dart

@@ -0,0 +1,21 @@
+
+import 'package:json_annotation/json_annotation.dart';
+import 'package:keyboard/data/bean/pay_way_info.dart';
+
+import '../../bean/goods_info.dart';
+
+part 'item_retention_response.g.dart';
+
+@JsonSerializable()
+class ItemRetentionResponse {
+  @JsonKey(name: "firstAmount")
+  GoodsInfo? firstAmount;
+
+  @JsonKey(name: "secondAmount")
+  GoodsInfo? secondAmount;
+
+  ItemRetentionResponse({this.firstAmount, this.secondAmount});
+
+  factory ItemRetentionResponse.fromJson(Map<String, dynamic> json) =>
+      _$ItemRetentionResponseFromJson(json);
+}

+ 27 - 0
lib/data/api/response/item_retention_response.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'item_retention_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ItemRetentionResponse _$ItemRetentionResponseFromJson(
+  Map<String, dynamic> json,
+) => ItemRetentionResponse(
+  firstAmount:
+      json['firstAmount'] == null
+          ? null
+          : GoodsInfo.fromJson(json['firstAmount'] as Map<String, dynamic>),
+  secondAmount:
+      json['secondAmount'] == null
+          ? null
+          : GoodsInfo.fromJson(json['secondAmount'] as Map<String, dynamic>),
+);
+
+Map<String, dynamic> _$ItemRetentionResponseToJson(
+  ItemRetentionResponse instance,
+) => <String, dynamic>{
+  'firstAmount': instance.firstAmount,
+  'secondAmount': instance.secondAmount,
+};

+ 7 - 0
lib/data/repository/store_repository.dart

@@ -8,6 +8,7 @@ import '../../base/app_base_request.dart';
 import '../../utils/payment_status_manager.dart';
 import '../api/request/order_pay_request.dart';
 import '../api/request/order_status_request.dart';
+import '../api/response/item_retention_response.dart';
 import 'account_repository.dart';
 
 @lazySingleton
@@ -19,6 +20,7 @@ class StoreRepository {
 
   StoreRepository(this.atmobApi, this.accountRepository);
 
+
   Future<ItemListResponse> getGoodsInfoList() async {
     return await atmobApi
         .getGoodsList(AppBaseRequest())
@@ -48,6 +50,11 @@ class StoreRepository {
     });
   }
 
+  Future<ItemRetentionResponse> getItemRetention()  {
+    return  atmobApi
+        .getItemRetention(AppBaseRequest())
+        .then(HttpHandler.handle(true));
+  }
 
 
 }

+ 4 - 0
lib/di/get_it.config.dart

@@ -41,6 +41,7 @@ import '../module/login/login_controller.dart' as _i1008;
 import '../module/main/main_controller.dart' as _i731;
 import '../module/mine/mine_controller.dart' as _i732;
 import '../module/store/store_controller.dart' as _i344;
+import '../module/store/suprise/goods_surprise_controller.dart' as _i935;
 import '../plugins/keyboard_android_platform.dart' as _i79;
 import '../utils/payment_status_manager.dart' as _i779;
 import 'network_module.dart' as _i567;
@@ -111,6 +112,9 @@ extension GetItInjectableX on _i174.GetIt {
         gh<_i83.AccountRepository>(),
       ),
     );
+    gh.factory<_i935.GoodsSurpriseController>(
+      () => _i935.GoodsSurpriseController(gh<_i987.StoreRepository>()),
+    );
     gh.lazySingleton<_i779.PaymentStatusManager>(
       () => _i779.PaymentStatusManager(gh<_i987.StoreRepository>()),
     );

+ 5 - 5
lib/dialog/content/character_tab_group_content_controller.dart

@@ -5,6 +5,7 @@ import 'package:injectable/injectable.dart';
 import 'package:keyboard/base/base_controller.dart';
 import 'package:keyboard/data/repository/characters_repository.dart';
 import 'package:keyboard/dialog/character_details_dialog.dart';
+import 'package:keyboard/module/store/store_page.dart';
 import 'package:keyboard/utils/atmob_log.dart';
 import 'package:keyboard/utils/http_handler.dart';
 import 'package:keyboard/utils/toast_util.dart';
@@ -41,17 +42,14 @@ class CharacterTabGroupContentController extends BaseController {
       controlFinishLoad: true,
       controlFinishRefresh: true,
     );
-    // 等待页面渲染完成后再加载数据
-    WidgetsBinding.instance.addPostFrameCallback((_) {
-      refreshData();
-    });
+
 
   }
 
   @override
   onReady() {
     super.onReady();
-
+    refreshData();
     AtmobLog.d("CharacterTabGroupContentController", "onReady");
   }
 
@@ -128,6 +126,7 @@ class CharacterTabGroupContentController extends BaseController {
         .catchError((error) {
           if (error is ServerErrorException && error.code == 1005) {
             ToastUtil.show('请开通会员解锁权益~');
+            StorePage.start();
           }if (error is ServerErrorException && error.code == 1001) {
             ToastUtil.show('键盘人设已达上限,请取消其他人设再添加~');
           } 
@@ -155,6 +154,7 @@ class CharacterTabGroupContentController extends BaseController {
         .catchError((error) {
           if (error is ServerErrorException && error.code == 1005) {
             ToastUtil.show('请开通会员解锁权益~');
+            StorePage.start();
           } else {
             ErrorHandler.toastError(error);
           }

+ 5 - 1
lib/module/character/content/character_group_content_controller.dart

@@ -6,6 +6,7 @@ import 'package:keyboard/base/base_controller.dart';
 import 'package:keyboard/data/repository/characters_repository.dart';
 import 'package:keyboard/dialog/character_details_dialog.dart';
 import 'package:keyboard/module/character/character_controller.dart';
+import 'package:keyboard/module/store/store_page.dart';
 import 'package:keyboard/utils/atmob_log.dart';
 import 'package:keyboard/utils/http_handler.dart';
 import 'package:keyboard/utils/toast_util.dart';
@@ -130,7 +131,9 @@ class CharacterGroupContentController extends BaseController {
       ToastUtil.show('添加成功~');
     } catch (error) {
       if (error is ServerErrorException && error.code == 1005) {
-        ToastUtil.show('请开通会员解锁权益~');
+        StorePage.start();
+
+        ErrorHandler.toastError(error);
       } else {
         ErrorHandler.toastError(error);
       }
@@ -155,6 +158,7 @@ class CharacterGroupContentController extends BaseController {
         .catchError((error) {
           if (error is ServerErrorException && error.code == 1005) {
             ToastUtil.show('请开通会员解锁权益~');
+            StorePage.start();
           } else {
             ErrorHandler.toastError(error);
           }

+ 1 - 1
lib/module/character/content/character_group_content_view.dart

@@ -72,7 +72,7 @@ class CharacterGroupContentView
             borderRadius: BorderRadius.circular(12.r),
           ),
         ),
-        height: 88.h,
+
         child: Row(
           children: [
             _buildAvatar(imageUrl: characterInfo.imageUrl),

+ 18 - 10
lib/module/character_custom/character_custom_page.dart

@@ -29,13 +29,22 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
 
   @override
   Widget buildBody(BuildContext context) {
-    return Obx(() {
-      if (controller.currentStep.value == StepType.home) {
-        return _buildCustomHomePage();
-      } else {
-        return _buildStepsPage();
-      }
-    });
+    return PopScope(
+      canPop: false,
+      onPopInvokedWithResult: (didPop, result) {
+        if (didPop) {
+          return;
+        }
+        controller.clickBack();
+      },
+      child: Obx(() {
+        if (controller.currentStep.value == StepType.home) {
+          return _buildCustomHomePage();
+        } else {
+          return _buildStepsPage();
+        }
+      }),
+    );
   }
 
   // 定制首页
@@ -459,13 +468,12 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
           "(最多选择${controller.currentCharacterCustomConfig?.maxCharacterNum ?? 3}个)",
       items: controller.characterLabelsList,
       selectedLabels: controller.characterSelectLabels,
-      isShowEmoji: false,
+      isShowEmoji: true,
       onSelected: (character) {
         controller.selectCharacter(character);
       },
       isCustomEnabled:
-          controller.currentCharacterCustomConfig?.customCharacter ==
-          true,
+          controller.currentCharacterCustomConfig?.customCharacter == true,
       onCustomClick: () {
         controller.clickCharacterCustom();
       },

+ 12 - 3
lib/module/character_custom/detail/character_custom_detail_controller.dart

@@ -4,8 +4,12 @@ import 'package:keyboard/data/bean/custom_config_info.dart';
 import 'package:keyboard/data/repository/characters_repository.dart';
 import 'package:get/get.dart';
 import 'package:keyboard/data/repository/config_repository.dart';
+import 'package:keyboard/module/store/store_page.dart';
 import 'package:keyboard/utils/atmob_log.dart';
 
+import '../../../utils/http_handler.dart';
+import '../../../utils/toast_util.dart';
+
 // 定制人设详情页
 @injectable
 class CharacterCustomDetailController extends BaseController {
@@ -77,7 +81,6 @@ class CharacterCustomDetailController extends BaseController {
       int currentIndex = _girlAvatars.indexOf(_avatarUrl.value);
       _avatarUrl.value = _girlAvatars[(currentIndex + 1) % _girlAvatars.length];
     }
-
   }
 
   void _getArgs() {
@@ -145,8 +148,14 @@ class CharacterCustomDetailController extends BaseController {
         birthday: _birthday.value,
         imageUrl: _avatarUrl.value,
       );
-    } catch (e) {
-      AtmobLog.e(tag, "生成专属人设失败: $e");
+    } catch (error) {
+      if (error is ServerErrorException && error.code == 1005) {
+        ToastUtil.show('请开通会员解锁权益~');
+        StorePage.start();
+      }
+      if (error is ServerErrorException) {
+        ToastUtil.show(error.message);
+      }
     }
   }
 }

+ 3 - 3
lib/module/main/main_controller.dart

@@ -21,19 +21,19 @@ class MainController extends BaseController {
     TabBean(
       StringName.mainTabKeyboard,
       Assets.images.iconTabKeyboardUnselect.path,
-      Assets.images.iconTabKeyboardSelected.path,
+      Assets.anim.animTabKeyboardSelectedData,
       KeyBoardView(),
     ),
     TabBean(
       StringName.mainTabCharacter,
       Assets.images.iconTabCharacterUnselect.path,
-      Assets.images.iconTabCharacterSelected.path,
+      Assets.anim.animTabCharacterSelectedData,
       CharacterView(),
     ),
     TabBean(
       StringName.mainTabMine,
       Assets.images.iconTabMineUnselect.path,
-      Assets.images.iconTabMineSelected.path,
+      Assets.anim.animTabMineSelectedData,
       MineView(),
     ),
   ];

+ 15 - 5
lib/module/main/main_page.dart

@@ -1,8 +1,10 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:get/get.dart';
+import 'package:lottie/lottie.dart';
 
 import '../../base/base_page.dart';
+import '../../resource/assets.gen.dart';
 import '../../router/app_pages.dart';
 import 'main_controller.dart';
 
@@ -56,11 +58,19 @@ class MainPage extends BasePage<MainController> {
 
               return Column(
                 children: [
-                  Image.asset(
-                    isSelected ? tabBean.selectedIcon : tabBean.normalIcon,
-                    width: 28.w,
-                    height: 28.w,
-                  ),
+                  isSelected
+                      ? Lottie.asset(
+                        tabBean.selectedIcon,
+                        repeat: false,
+                        width: 28.w,
+                        height: 28.w,
+                      )
+                      : Image.asset(
+                        tabBean.normalIcon,
+                        width: 28.w,
+                        height: 28.w,
+                      ),
+
                   Text(
                     tabBean.title,
                     style: TextStyle(

+ 17 - 2
lib/module/mine/mine_controller.dart

@@ -1,17 +1,23 @@
+import 'package:flutter/material.dart';
 import 'package:flutter/rendering.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:get/get.dart';
 import 'package:get/get_core/src/get_main.dart';
 import 'package:injectable/injectable.dart';
 import 'package:keyboard/base/base_controller.dart';
 import 'package:keyboard/module/about/about_page.dart';
 import 'package:keyboard/module/feedback/feedback_page.dart';
+import 'package:keyboard/module/store/discount/discount_view.dart';
 
 import '../../data/consts/error_code.dart';
 import '../../data/repository/account_repository.dart';
+import '../store/discount/discount_controller.dart';
+import '../store/suprise/surprise_dialog.dart';
 import '../../resource/string.gen.dart';
 import '../../utils/http_handler.dart';
 import '../../utils/toast_util.dart';
 import '../store/store_page.dart';
+import '../store/ticket/discount_ticket_dialog.dart';
 
 @injectable
 class MineController extends BaseController {
@@ -67,8 +73,17 @@ class MineController extends BaseController {
 
   clickPersonalProfile() {
     debugPrint('clickPersonalProfile');
-    accountRepository.setUserInfo(name: "老铁",birthday: "2021-03-17",gender: 1,hobbies: ["a","b","c"],characters: ["测试"]);
-
+    // SurpriseDialog.show(clickConfirm: StorePage.start);
+    DiscountTicketDialog.show();
+    // SmartDialog.show(
+    //   backType: SmartBackType.block,
+    //   clickMaskDismiss: true,
+    //   alignment: Alignment.bottomCenter,
+    //   keepSingle: true,
+    //   onDismiss: () => Get.delete<DiscountController>(),
+    //
+    //   builder: (_) => DiscountView(),
+    // );
   }
 
   clickFeedback() {

+ 9 - 0
lib/module/store/discount/discount_controller.dart

@@ -0,0 +1,9 @@
+import 'package:keyboard/base/base_controller.dart';
+
+class DiscountController extends BaseController{
+  @override
+  void onInit() {
+    super.onInit();
+  }
+
+}

+ 33 - 0
lib/module/store/discount/discount_view.dart

@@ -0,0 +1,33 @@
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:keyboard/base/base_view.dart';
+import 'package:keyboard/module/store/discount/discount_controller.dart';
+import 'package:get/get.dart';
+import 'package:flutter/material.dart';
+
+class DiscountView extends BaseView<DiscountController> {
+  const DiscountView({super.key});
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Container(
+      decoration: ShapeDecoration(
+        gradient: LinearGradient(
+          begin: Alignment(0.50, 0.00),
+          end: Alignment(0.50, 1.00),
+          colors: [const Color(0xFF9EED00), const Color(0xFF48D900)],
+        ),
+        shape: RoundedRectangleBorder(
+          borderRadius: BorderRadius.only(
+            topLeft: Radius.circular(28.r),
+            topRight: Radius.circular(28.r),
+          ),
+        ),
+      ),
+      child: Column(
+        mainAxisSize: MainAxisSize.min,
+
+      ),
+    );
+  }
+}

+ 1 - 1
lib/module/store/store_page.dart

@@ -12,7 +12,7 @@ import 'package:keyboard/resource/string.gen.dart';
 import '../../resource/assets.gen.dart';
 import '../../router/app_pages.dart';
 import '../../utils/date_util.dart';
-import '../../utils/horizontal_dashed_line.dart';
+import '../../widget/horizontal_dashed_line.dart';
 import '../../utils/styles.dart';
 import '../../widget/click_text_span.dart';
 

+ 99 - 0
lib/module/store/suprise/goods_surprise_controller.dart

@@ -0,0 +1,99 @@
+import 'dart:async';
+
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:injectable/injectable.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/module/store/suprise/surprise_dialog.dart';
+import '../../../base/base_controller.dart';
+import '../../../data/bean/goods_info.dart';
+import '../../../data/repository/store_repository.dart';
+import '../../../utils/async_util.dart';
+import '../../../utils/error_handler.dart';
+import 'package:keyboard/utils/atmob_log.dart';
+
+@injectable
+class GoodsSurpriseController extends BaseController {
+  final tag = 'GoodsSurpriseController';
+  static const int countdownTime = 10 * 60 * 100; // 10分钟(毫秒单位,10ms 为 1 计数)
+  final RxInt timeLeft = countdownTime.obs; // 剩余时间(10ms 为单位)
+  Timer? _timer;
+  final StoreRepository storeRepository;
+
+  CancelableFuture? _storeDataFuture;
+
+  final Rxn<GoodsInfo> _firstAmount = Rxn<GoodsInfo>();
+
+  GoodsInfo? get firstAmount => _firstAmount.value;
+
+  final Rxn<GoodsInfo> _secondAmount = Rxn<GoodsInfo>();
+
+  GoodsInfo? get secondAmount => _secondAmount.value;
+
+  GoodsSurpriseController(this.storeRepository);
+
+  @override
+  void onInit() {
+    startCountdown();
+    super.onInit();
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    refreshStoreData();
+  }
+
+  void startCountdown() {
+    _timer = Timer.periodic(const Duration(milliseconds: 10), (timer) {
+      if (timeLeft.value > 0) {
+        timeLeft.value--;
+      } else {
+        timer.cancel();
+      }
+    });
+  }
+
+  void onCountdownEnd() {
+    SmartDialog.dismiss(tag: SurpriseDialog.tag);
+  }
+
+  @override
+  void onClose() {
+    _timer?.cancel();
+    super.onClose();
+    _storeDataFuture?.cancel();
+  }
+
+  Future<void> getItemRetention() async {
+    storeRepository.getItemRetention();
+  }
+
+  void refreshStoreData() {
+    _storeDataFuture?.cancel();
+    _storeDataFuture = AsyncUtil.retryWithExponentialBackoff(
+      () => _requestGoodsInfoList(),
+      4,
+    );
+    _storeDataFuture?.catchError((error) {
+      ErrorHandler.toastError(error);
+    });
+  }
+
+  Future<void> _requestGoodsInfoList() async {
+    try {
+      final response = await storeRepository.getItemRetention();
+
+      if (response.firstAmount != null) {
+        _firstAmount.value = response.firstAmount;
+       print('firstAmount: ${response.firstAmount?.toJson()}');
+      }
+
+      if (response.secondAmount != null) {
+        _secondAmount.value = response.secondAmount;
+        print('secondAmount: ${response.secondAmount?.toJson()}');
+      }
+    } catch (e) {
+      AtmobLog.e(tag, e.toString());
+    }
+  }
+}

+ 303 - 0
lib/module/store/suprise/surprise_dialog.dart

@@ -0,0 +1,303 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/resource/string.gen.dart';
+import 'package:lottie/lottie.dart';
+
+import '../../../resource/assets.gen.dart';
+import '../../../resource/colors.gen.dart';
+import '../../../utils/styles.dart';
+import '../../../widget/horizontal_dashed_line.dart';
+import 'goods_surprise_controller.dart';
+
+class SurpriseDialog {
+  static const String tag = 'SurpriseDialog';
+
+  static void show({VoidCallback? clickConfirm}) {
+    SmartDialog.show(
+      tag: tag,
+      backType: SmartBackType.block,
+      clickMaskDismiss: false,
+      maskColor: ColorName.black70,
+      builder: (_) {
+        final controller = Get.find<GoodsSurpriseController>();
+        return Stack(
+          children: [
+            Positioned(
+              top: 0,
+              left: 0,
+              right: 0,
+              bottom: 0,
+              child: Column(
+                crossAxisAlignment: CrossAxisAlignment.center,
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  Stack(
+                    clipBehavior: Clip.none,
+                    children: [
+                      Container(
+                        width: 328.w,
+                        height: 290.h,
+                        decoration: BoxDecoration(
+                          image: DecorationImage(
+                            image: Assets.images.bgSurpriseDialog.provider(),
+                            fit: BoxFit.cover,
+                          ),
+                        ),
+                        child: Column(
+                          mainAxisAlignment: MainAxisAlignment.center,
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          children: [
+                            SizedBox(height: 23.h),
+                            Container(
+                              width: 240.w,
+                              height: 108.h,
+                              decoration: BoxDecoration(
+                                image: DecorationImage(
+                                  image:
+                                      Assets.images.bgSurpriseDialogPrices
+                                          .provider(),
+                                  fit: BoxFit.cover,
+                                ),
+                              ),
+                              child: Column(
+                                mainAxisAlignment: MainAxisAlignment.center,
+                                crossAxisAlignment: CrossAxisAlignment.center,
+                                children: [
+                                  Obx(() {
+                                    return _buildPricesPartOne(controller);
+                                  }),
+                                  HorizontalDashedLine(
+                                    width: 200.w,
+                                    color: Color(0xFFFF9451),
+                                    strokeWidth: 2.h,
+                                    dashLength: 5.w,
+                                    dashSpace: 3.w,
+                                  ),
+                                  Obx(() {
+                                    return Text(
+                                      controller.firstAmount?.description ?? "",
+                                      style: TextStyle(
+                                        color: const Color(0xFFFFC172),
+                                        fontSize: 10.sp,
+                                        fontWeight: FontWeight.w400,
+                                        height: 2,
+                                      ),
+                                    );
+                                  }),
+                                ],
+                              ),
+                            ),
+                            SizedBox(height: 16.h),
+                            GestureDetector(
+                              onTap: () {
+                                clickConfirm?.call();
+                                SmartDialog.dismiss(tag: tag);
+                              },
+                              child: Container(
+                                width: 240.w,
+                                height: 60.h,
+                                decoration: BoxDecoration(
+                                  image: DecorationImage(
+                                    image:
+                                        Assets.images.bgSurpriseDialogButton
+                                            .provider(),
+                                    fit: BoxFit.cover,
+                                  ),
+                                ),
+                                child: Column(
+                                  mainAxisAlignment: MainAxisAlignment.center,
+                                  crossAxisAlignment: CrossAxisAlignment.center,
+                                  children: [
+
+                                    SizedBox(
+                                      child: Assets
+                                          .images
+                                          .iconSurpriseDialogButton
+                                          .image(width: 120.w, height: 20.h),
+                                    ),
+                                    Obx(() {
+                                      int totalMilliseconds =
+                                          controller.timeLeft.value;
+                                      int minutes = (totalMilliseconds ~/ 6000);
+                                      int seconds =
+                                          (totalMilliseconds ~/ 100) % 60;
+                                      int milliseconds =
+                                          (totalMilliseconds % 100); // 毫秒
+                                      return Row(
+                                        mainAxisAlignment:
+                                            MainAxisAlignment.center,
+                                        crossAxisAlignment:
+                                            CrossAxisAlignment.center,
+                                        children: [
+                                          _buildTimeBox(
+                                            minutes.toString().padLeft(2, '0'),
+                                          ),
+                                          VerticalDots(
+                                            height: 8.h,
+                                            dotSize: 2.w,
+                                          ),
+                                          _buildTimeBox(
+                                            seconds.toString().padLeft(2, '0'),
+                                          ),
+                                          VerticalDots(
+                                            height: 8.h,
+                                            dotSize: 2.w,
+                                          ),
+                                          _buildTimeBox(
+                                            milliseconds.toString().padLeft(
+                                              2,
+                                              '0',
+                                            ),
+                                          ),
+                                          Text(
+                                            StringName.surpriseDialogEndDesc,
+                                            style: Styles.getTextStyleWhiteW400(
+                                              10.sp,
+                                            ),
+                                          ),
+                                        ],
+                                      );
+                                    }),
+                                  ],
+                                ),
+                              ),
+                            ),
+                          ],
+                        ),
+                      ),
+                      Positioned(
+                        top: -50,
+                        right: 0,
+                        left: 0,
+                        child: Assets.images.iconSurpriseDialogTitle.image(
+                          width: 264.w,
+                          height: 95.h,
+                        ),
+                      ),
+                    ],
+                  ),
+
+                  Container(
+                    margin: EdgeInsets.only(top: 24.h),
+                    child: GestureDetector(
+                      onTap: () {
+                        SmartDialog.dismiss(tag: tag);
+                      },
+                      child: Assets.images.iconCharacterDialogClose.image(
+                        width: 40.r,
+                        height: 40.r,
+                      ),
+                    ),
+                  ),
+                ],
+              ),
+            ),
+            IgnorePointer(
+              child: Lottie.asset(
+                Assets.anim.animSurpriseDialogData,
+                repeat: false,
+                width: 360.w,
+              ),
+            ),
+          ],
+        );
+      },
+    );
+  }
+
+  static Widget _buildPricesPartOne(GoodsSurpriseController controller) {
+    return Row(
+      mainAxisAlignment: MainAxisAlignment.center,
+      crossAxisAlignment: CrossAxisAlignment.center,
+      children: [
+        Text(
+          controller.firstAmount?.priceDescNumber ?? "",
+          style: TextStyle(
+            color: Colors.white,
+            fontSize: 48.sp,
+            fontWeight: FontWeight.w700,
+          ),
+        ),
+        Column(
+          children: [
+            Container(
+              width: 30.w,
+              height: 18.h,
+              decoration: BoxDecoration(
+                image: DecorationImage(
+                  image: Assets.images.iconSurpriseDialogOnly.provider(),
+                  fit: BoxFit.cover,
+                ),
+              ),
+              child: Center(
+                child: Text(
+                  StringName.surpriseDialogOnly,
+                  style: TextStyle(
+                    color: const Color(0xFFFF451D),
+                    fontSize: 11.sp,
+                    fontWeight: FontWeight.w500,
+                  ),
+                ),
+              ),
+            ),
+            Text(
+              controller.firstAmount?.priceDescUnit ?? '',
+              style: TextStyle(
+                color: Colors.white,
+                fontSize: 14.sp,
+                fontWeight: FontWeight.w700,
+              ),
+            ),
+          ],
+        ),
+      ],
+    );
+  }
+
+  static Widget _buildTimeBox(String value) {
+    return Container(
+      width: 18.w,
+      height: 15.h,
+      decoration: ShapeDecoration(
+        color: Colors.white,
+        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4.r)),
+      ),
+      child: Center(
+        child: Text(value, style: Styles.getTextStyleBlack204W500(10.sp)),
+      ),
+    );
+  }
+}
+
+class VerticalDots extends StatelessWidget {
+  final double dotSize;
+  final double height;
+  final Color? color;
+
+  const VerticalDots({
+    super.key,
+    required this.dotSize,
+    required this.height,
+    this.color = Colors.white,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return SizedBox(
+      width: 2.w,
+      height: 8.h,
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: List.generate(2, (index) {
+          return Container(
+            width: dotSize,
+            height: dotSize,
+            decoration: BoxDecoration(color: color, shape: BoxShape.circle),
+          );
+        }),
+      ),
+    );
+  }
+}

+ 286 - 0
lib/module/store/ticket/discount_ticket_dialog.dart

@@ -0,0 +1,286 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:lottie/lottie.dart';
+
+import '../../../resource/assets.gen.dart';
+import '../../../resource/colors.gen.dart';
+import '../../../utils/styles.dart';
+import '../suprise/goods_surprise_controller.dart';
+
+class DiscountTicketDialog {
+  static const String tag = 'DiscountTicketDialog';
+
+  static void show({VoidCallback? clickConfirm}) {
+    SmartDialog.show(
+      tag: tag,
+      backType: SmartBackType.block,
+      clickMaskDismiss: false,
+      maskColor: ColorName.black70,
+      builder: (_) {
+        final controller = Get.find<GoodsSurpriseController>();
+        return Stack(
+          children: [
+            Positioned(
+              top: 0,
+              left: 0,
+              right: 0,
+              bottom: 0,
+              child: Column(
+                crossAxisAlignment: CrossAxisAlignment.center,
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  Stack(
+                    clipBehavior: Clip.none,
+                    children: [
+                      Column(
+                        mainAxisAlignment: MainAxisAlignment.center,
+                        crossAxisAlignment: CrossAxisAlignment.center,
+                        children: [
+                          SizedBox(
+                            width: 360.h,
+                            height: 110.h,
+                            child: IgnorePointer(
+                              child: Lottie.asset(
+                                Assets.anim.animDiscountTicketDialogData,
+                                repeat: false,
+                                fit: BoxFit.contain,
+                              ),
+                            ),
+                          ),
+                          Container(
+                            margin: EdgeInsets.only(left: 31.h, right: 14.h),
+                            width: 317.w,
+                            height: 364.h,
+                            decoration: BoxDecoration(
+                              image: DecorationImage(
+                                image: Assets.images.bgTicketDialog.provider(),
+                                fit: BoxFit.cover,
+                              ),
+                              boxShadow: [
+                                BoxShadow(
+                                  color: Colors.black.withAlpha(4), // 阴影颜色
+                                  blurRadius: 10, // 模糊程度
+                                  spreadRadius: 2, // 扩散程度
+                                  offset: Offset(4, 4), // 偏移量 (x, y)
+                                ),
+                              ],
+                            ),
+
+                            child: Stack(
+                              children: [
+                                Column(
+                                  mainAxisAlignment: MainAxisAlignment.start,
+                                  crossAxisAlignment: CrossAxisAlignment.center,
+                                  children: [
+                                    SizedBox(height: 160.h),
+                                    Container(
+                                      margin: EdgeInsets.only(right: 16.h),
+                                      decoration: BoxDecoration(
+                                        image: DecorationImage(
+                                          image:
+                                              Assets.images.bgTicketDialogPrices
+                                                  .provider(),
+                                          fit: BoxFit.cover,
+                                          alignment: Alignment.center,
+                                        ),
+                                      ),
+                                      width: 260.w,
+                                      height: 100.h,
+                                      child: Column(
+                                        children: [
+                                          Container(
+                                            margin: EdgeInsets.only(top: 10.h),
+                                            child: Obx(() {
+                                              return Text(
+                                                controller.secondAmount?.name ??
+                                                    "",
+                                                style: TextStyle(
+                                                  color: const Color(
+                                                    0xFFC39858,
+                                                  ),
+                                                  fontSize: 14.sp,
+                                                  fontWeight: FontWeight.w400,
+                                                ),
+                                              );
+                                            }),
+                                          ),
+                                          Obx(() {
+                                            return Text.rich(
+                                              TextSpan(
+                                                children: [
+                                                  TextSpan(
+                                                    text: '¥',
+                                                    style: TextStyle(
+                                                      color: const Color(
+                                                        0xFFF55208,
+                                                      ),
+                                                      fontSize: 27.sp,
+                                                      fontWeight:
+                                                          FontWeight.w700,
+                                                    ),
+                                                  ),
+                                                  TextSpan(
+                                                    text:
+                                                        controller
+                                                            .secondAmount
+                                                            ?.amountText,
+                                                    style: TextStyle(
+                                                      color: const Color(
+                                                        0xFFF55208,
+                                                      ),
+                                                      fontSize: 36.sp,
+                                                      fontWeight:
+                                                          FontWeight.w700,
+                                                    ),
+                                                  ),
+                                                ],
+                                              ),
+                                            );
+                                          }),
+                                        ],
+                                      ),
+                                    ),
+                                    Container(
+                                      margin: EdgeInsets.only(right: 25.h),
+                                      width: 317.w,
+                                      height: 16.h,
+                                      decoration: BoxDecoration(
+                                        image: DecorationImage(
+                                          image:
+                                              Assets
+                                                  .images
+                                                  .bgTicketDialogPrices2
+                                                  .provider(),
+                                          fit: BoxFit.cover,
+                                        ),
+                                      ),
+                                    ),
+                                    SizedBox(height: 9.h),
+                                    GestureDetector(
+                                      onTap: () {
+                                        clickConfirm?.call();
+                                        SmartDialog.dismiss(tag: tag);
+                                      },
+                                      child: Container(
+                                        margin: EdgeInsets.only(right: 20.h),
+                                        width: 200.w,
+                                        height: 54.h,
+                                        child: Assets
+                                            .images
+                                            .iconTicketDialogButton
+                                            .image(width: 200.w, height: 54.h),
+                                      ),
+                                    ),
+                                  ],
+                                ),
+
+                                // 倒计时
+                                Positioned(
+                                  right: 40.w,
+                                  child: Container(
+                                    height: 24.h,
+                                    width: 107.w,
+                                    alignment: Alignment.center,
+                                    decoration: ShapeDecoration(
+                                      gradient: LinearGradient(
+                                        begin: Alignment.centerLeft,
+                                        end: Alignment.centerRight,
+                                        colors: [
+                                          const Color(0xFFFF68F4),
+                                          const Color(0xFFF96432),
+                                        ],
+                                      ),
+                                      shape: RoundedRectangleBorder(
+                                        side: BorderSide(
+                                          width: 1,
+                                          color: Colors.white,
+                                        ),
+                                        borderRadius: BorderRadius.only(
+                                          topLeft: Radius.circular(15.r),
+                                          topRight: Radius.circular(16.r),
+                                          bottomRight: Radius.circular(16.r),
+                                        ),
+                                      ),
+                                    ),
+                                    margin: EdgeInsets.only(top: 140.h),
+                                    child: Obx(() {
+                                      int totalMilliseconds =
+                                          controller.timeLeft.value;
+                                      int minutes = (totalMilliseconds ~/ 6000);
+                                      int seconds =
+                                          (totalMilliseconds ~/ 100) % 60;
+                                      int milliseconds =
+                                          (totalMilliseconds % 100); // 毫秒
+                                      return Text(
+                                        "${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}:${milliseconds.toString().padLeft(2, '0')} 后过期",
+                                        style: TextStyle(
+                                          color: Colors.white,
+                                          fontSize: 12.sp,
+                                          fontWeight: FontWeight.w500,
+                                        ),
+                                      );
+                                    }),
+                                  ),
+                                ),
+                              ],
+                            ),
+                          ),
+                        ],
+                      ),
+                    ],
+                  ),
+
+                  Container(
+                    margin: EdgeInsets.only(top: 24.h),
+                    child: GestureDetector(
+                      onTap: () {
+                        SmartDialog.dismiss(tag: tag);
+                      },
+                      child: Assets.images.iconCharacterDialogClose.image(
+                        width: 40.r,
+                        height: 40.r,
+                      ),
+                    ),
+                  ),
+                ],
+              ),
+            ),
+          ],
+        );
+      },
+    );
+  }
+}
+
+class VerticalDots extends StatelessWidget {
+  final double dotSize;
+  final double height;
+  final Color? color;
+
+  const VerticalDots({
+    super.key,
+    required this.dotSize,
+    required this.height,
+    this.color = Colors.white,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return SizedBox(
+      width: 2.w,
+      height: 8.h,
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: List.generate(2, (index) {
+          return Container(
+            width: dotSize,
+            height: dotSize,
+            decoration: BoxDecoration(color: color, shape: BoxShape.circle),
+          );
+        }),
+      ),
+    );
+  }
+}

+ 84 - 0
lib/resource/assets.gen.dart

@@ -9,6 +9,39 @@
 
 import 'package:flutter/widgets.dart';
 
+class $AssetsAnimGen {
+  const $AssetsAnimGen();
+
+  /// File path: assets/anim/anim_discount_ticket_dialog_data.json
+  String get animDiscountTicketDialogData =>
+      'assets/anim/anim_discount_ticket_dialog_data.json';
+
+  /// File path: assets/anim/anim_surprise_dialog_data.json
+  String get animSurpriseDialogData =>
+      'assets/anim/anim_surprise_dialog_data.json';
+
+  /// File path: assets/anim/anim_tab_character_selected_data.json
+  String get animTabCharacterSelectedData =>
+      'assets/anim/anim_tab_character_selected_data.json';
+
+  /// File path: assets/anim/anim_tab_keyboard_selected_data.json
+  String get animTabKeyboardSelectedData =>
+      'assets/anim/anim_tab_keyboard_selected_data.json';
+
+  /// File path: assets/anim/anim_tab_mine_selected_data.json
+  String get animTabMineSelectedData =>
+      'assets/anim/anim_tab_mine_selected_data.json';
+
+  /// List of all assets
+  List<String> get values => [
+    animDiscountTicketDialogData,
+    animSurpriseDialogData,
+    animTabCharacterSelectedData,
+    animTabKeyboardSelectedData,
+    animTabMineSelectedData,
+  ];
+}
+
 class $AssetsImagesGen {
   const $AssetsImagesGen();
 
@@ -71,6 +104,30 @@ class $AssetsImagesGen {
   AssetGenImage get bgStoreUserReviews =>
       const AssetGenImage('assets/images/bg_store_user_reviews.webp');
 
+  /// File path: assets/images/bg_surprise_dialog.webp
+  AssetGenImage get bgSurpriseDialog =>
+      const AssetGenImage('assets/images/bg_surprise_dialog.webp');
+
+  /// File path: assets/images/bg_surprise_dialog_button.webp
+  AssetGenImage get bgSurpriseDialogButton =>
+      const AssetGenImage('assets/images/bg_surprise_dialog_button.webp');
+
+  /// File path: assets/images/bg_surprise_dialog_prices.webp
+  AssetGenImage get bgSurpriseDialogPrices =>
+      const AssetGenImage('assets/images/bg_surprise_dialog_prices.webp');
+
+  /// File path: assets/images/bg_ticket.dialog.webp
+  AssetGenImage get bgTicketDialog =>
+      const AssetGenImage('assets/images/bg_ticket.dialog.webp');
+
+  /// File path: assets/images/bg_ticket_dialog_prices.webp
+  AssetGenImage get bgTicketDialogPrices =>
+      const AssetGenImage('assets/images/bg_ticket_dialog_prices.webp');
+
+  /// File path: assets/images/bg_ticket_dialog_prices2.webp
+  AssetGenImage get bgTicketDialogPrices2 =>
+      const AssetGenImage('assets/images/bg_ticket_dialog_prices2.webp');
+
   /// File path: assets/images/icon_about_arrow_left.webp
   AssetGenImage get iconAboutArrowLeft =>
       const AssetGenImage('assets/images/icon_about_arrow_left.webp');
@@ -360,6 +417,18 @@ class $AssetsImagesGen {
   AssetGenImage get iconStoreUserReviewsTitle =>
       const AssetGenImage('assets/images/icon_store_user_reviews_title.webp');
 
+  /// File path: assets/images/icon_surprise_dialog_button.webp
+  AssetGenImage get iconSurpriseDialogButton =>
+      const AssetGenImage('assets/images/icon_surprise_dialog_button.webp');
+
+  /// File path: assets/images/icon_surprise_dialog_only.webp
+  AssetGenImage get iconSurpriseDialogOnly =>
+      const AssetGenImage('assets/images/icon_surprise_dialog_only.webp');
+
+  /// File path: assets/images/icon_surprise_dialog_title.webp
+  AssetGenImage get iconSurpriseDialogTitle =>
+      const AssetGenImage('assets/images/icon_surprise_dialog_title.webp');
+
   /// File path: assets/images/icon_tab_character_selected.webp
   AssetGenImage get iconTabCharacterSelected =>
       const AssetGenImage('assets/images/icon_tab_character_selected.webp');
@@ -384,6 +453,10 @@ class $AssetsImagesGen {
   AssetGenImage get iconTabMineUnselect =>
       const AssetGenImage('assets/images/icon_tab_mine_unselect.webp');
 
+  /// File path: assets/images/icon_ticket_dialog_button.webp
+  AssetGenImage get iconTicketDialogButton =>
+      const AssetGenImage('assets/images/icon_ticket_dialog_button.webp');
+
   /// File path: assets/images/icon_wechat_payment.webp
   AssetGenImage get iconWechatPayment =>
       const AssetGenImage('assets/images/icon_wechat_payment.webp');
@@ -409,6 +482,12 @@ class $AssetsImagesGen {
     bgMineVipCard,
     bgStoreSelectedItem,
     bgStoreUserReviews,
+    bgSurpriseDialog,
+    bgSurpriseDialogButton,
+    bgSurpriseDialogPrices,
+    bgTicketDialog,
+    bgTicketDialogPrices,
+    bgTicketDialogPrices2,
     iconAboutArrowLeft,
     iconAlipayPayment,
     iconAlipayScanPayment,
@@ -479,12 +558,16 @@ class $AssetsImagesGen {
     iconStoreUserReview4,
     iconStoreUserReviewsLogo,
     iconStoreUserReviewsTitle,
+    iconSurpriseDialogButton,
+    iconSurpriseDialogOnly,
+    iconSurpriseDialogTitle,
     iconTabCharacterSelected,
     iconTabCharacterUnselect,
     iconTabKeyboardSelected,
     iconTabKeyboardUnselect,
     iconTabMineSelected,
     iconTabMineUnselect,
+    iconTicketDialogButton,
     iconWechatPayment,
     iconWechatScanPayment,
   ];
@@ -493,6 +576,7 @@ class $AssetsImagesGen {
 class Assets {
   const Assets._();
 
+  static const $AssetsAnimGen anim = $AssetsAnimGen();
   static const $AssetsImagesGen images = $AssetsImagesGen();
 }
 

+ 4 - 0
lib/resource/string.gen.dart

@@ -133,6 +133,8 @@ class StringName {
   static final String paySuccessDialogDesc = 'pay_success_dialog_desc'.tr; // 到期。
   static final String paySuccessDialogConfirm = 'pay_success_dialog_confirm'.tr; // 知道了
   static final String paySuccessDialogPermanent = 'pay_success_dialog_permanent'.tr; // 恭喜您成为终身会员,感谢您的支持
+  static final String surpriseDialogEndDesc = 'surprise_dialog_end_desc'.tr; // 后结束
+  static final String surpriseDialogOnly = 'surprise_dialog_only'.tr; // 仅需
 }
 class StringMultiSource {
   StringMultiSource._();
@@ -269,6 +271,8 @@ class StringMultiSource {
       'pay_success_dialog_desc': '到期。',
       'pay_success_dialog_confirm': '知道了',
       'pay_success_dialog_permanent': '恭喜您成为终身会员,感谢您的支持',
+      'surprise_dialog_end_desc': '后结束',
+      'surprise_dialog_only': '仅需',
     },
   };
 }

+ 6 - 0
lib/router/app_pages.dart

@@ -14,6 +14,9 @@ import 'package:keyboard/module/store/store_controller.dart';
 import 'package:keyboard/module/store/store_page.dart';
 
 import '../di/get_it.dart';
+import '../module/store/discount/discount_controller.dart';
+import '../module/store/suprise/goods_surprise_controller.dart';
+import '../module/store/suprise/surprise_dialog.dart';
 import '../module/about/about_page.dart';
 import '../module/browser/browser_page.dart';
 import '../module/character/character_controller.dart';
@@ -62,6 +65,9 @@ class AppBinding extends Bindings {
     lazyPut(() => getIt.get<CharacterCustomDetailController>());
     lazyPut(() => getIt.get<CharacterCustomListController>());
     lazyPut(() => getIt.get<StoreController>());
+    lazyPut(() => getIt.get<GoodsSurpriseController>());
+    lazyPut(() => getIt.get<DiscountController>());
+
   }
 
   void lazyPut<S>(InstanceBuilderCallback<S> builder) {

lib/utils/horizontal_dashed_line.dart → lib/widget/horizontal_dashed_line.dart


+ 120 - 1
pubspec.lock

@@ -9,6 +9,29 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "80.0.0"
+  agile_pay:
+    dependency: "direct main"
+    description:
+      path: "plugins/agile_pay"
+      relative: true
+    source: path
+    version: "0.0.1"
+  alipay_kit:
+    dependency: transitive
+    description:
+      name: alipay_kit
+      sha256: "6d6086b4cda1e0cd9b29b6dfe8d0ce88b9efc1c9f2c8d6fb39ac24c4e0f79b06"
+      url: "https://pub.dev"
+    source: hosted
+    version: "6.0.0"
+  alipay_kit_android:
+    dependency: transitive
+    description:
+      name: alipay_kit_android
+      sha256: "402917c30e5a1c1bb36cab7c99e355f65a98da96c2666657805a985b00937f6f"
+      url: "https://pub.dev"
+    source: hosted
+    version: "6.0.0"
   analyzer:
     dependency: transitive
     description:
@@ -177,6 +200,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.3.1"
+  carousel_slider:
+    dependency: "direct main"
+    description:
+      name: carousel_slider
+      sha256: "7b006ec356205054af5beaef62e2221160ea36b90fb70a35e4deacd49d0349ae"
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.0.0"
   characters:
     dependency: transitive
     description:
@@ -201,6 +232,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.11.0"
+  cli_util:
+    dependency: transitive
+    description:
+      name: cli_util
+      sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.4.2"
   clock:
     dependency: transitive
     description:
@@ -398,6 +437,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "5.10.0"
+  flutter_launcher_icons:
+    dependency: "direct dev"
+    description:
+      name: flutter_launcher_icons
+      sha256: bfa04787c85d80ecb3f8777bde5fc10c3de809240c48fa061a2c2bf15ea5211c
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.14.3"
   flutter_lints:
     dependency: "direct dev"
     description:
@@ -597,6 +644,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "4.1.2"
+  image:
+    dependency: transitive
+    description:
+      name: image
+      sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
+      url: "https://pub.dev"
+    source: hosted
+    version: "4.5.4"
   image_size_getter:
     dependency: transitive
     description:
@@ -605,6 +660,38 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.4.0"
+  in_app_purchase:
+    dependency: transitive
+    description:
+      name: in_app_purchase
+      sha256: "11a40f148eeb4f681a0572003e2b33432e110c90c1bbb4f9ef83b81ec0c4f737"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.2.1"
+  in_app_purchase_android:
+    dependency: transitive
+    description:
+      name: in_app_purchase_android
+      sha256: "475b6c6541b54e6878263270d291ca8d5d7ef044263115bdec45e1e59737e3ad"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.4.0+1"
+  in_app_purchase_platform_interface:
+    dependency: transitive
+    description:
+      name: in_app_purchase_platform_interface
+      sha256: "1d353d38251da5b9fea6635c0ebfc6bb17a2d28d0e86ea5e083bf64244f1fb4c"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.4.0"
+  in_app_purchase_storekit:
+    dependency: transitive
+    description:
+      name: in_app_purchase_storekit
+      sha256: "276831961023055b55a2156c1fc043f50f6215ff49fb0f5f2273da6eeb510ecf"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.3.21"
   injectable:
     dependency: "direct main"
     description:
@@ -732,6 +819,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.3.0"
+  lottie:
+    dependency: "direct main"
+    description:
+      name: lottie
+      sha256: c5fa04a80a620066c15cf19cc44773e19e9b38e989ff23ea32e5903ef1015950
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.3.1"
   markdown:
     dependency: "direct main"
     description:
@@ -1004,6 +1099,22 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.5.0"
+  qr:
+    dependency: transitive
+    description:
+      name: qr
+      sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.2"
+  qr_flutter:
+    dependency: "direct main"
+    description:
+      name: qr_flutter
+      sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097"
+      url: "https://pub.dev"
+    source: hosted
+    version: "4.1.0"
   recase:
     dependency: transitive
     description:
@@ -1178,7 +1289,7 @@ packages:
     source: hosted
     version: "1.4.1"
   synchronized:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: synchronized
       sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6"
@@ -1457,6 +1568,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "3.18.4"
+  wechat_kit:
+    dependency: transitive
+    description:
+      name: wechat_kit
+      sha256: ec5844289d01902a73204ffe7ad111503bfb67301341db8ec718a216fed66b89
+      url: "https://pub.dev"
+    source: hosted
+    version: "6.0.2"
   win32:
     dependency: transitive
     description:

+ 5 - 0
pubspec.yaml

@@ -73,6 +73,9 @@ dependencies:
   #QR
   qr_flutter: ^4.1.0
 
+  # 动画
+  lottie: ^3.3.1
+
   #android日志打印
   atmob_logging:
     version: ^0.0.5
@@ -149,8 +152,10 @@ flutter:
   uses-material-design: true
 
   assets:
+    - assets/anim/
     - assets/images/
 
+
 wechat_kit:
   ios: no_pay # 默认 pay
   app_id: wx