Selaa lähdekoodia

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

hezihao 9 kuukautta sitten
vanhempi
commit
0bba4ce63b
100 muutettua tiedostoa jossa 3125 lisäystä ja 171 poistoa
  1. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher.png
  2. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  3. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  4. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  5. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  6. 17 0
      android/build.gradle.kts
  7. BIN
      assets/images/bg_store_selected_item.webp
  8. BIN
      assets/images/bg_store_user_reviews.webp
  9. BIN
      assets/images/icon_alipay_payment.webp
  10. BIN
      assets/images/icon_alipay_scan_payment.webp
  11. BIN
      assets/images/icon_goods_info_title.webp
  12. BIN
      assets/images/icon_member_retain_close.webp
  13. BIN
      assets/images/icon_store_agree_privacy.webp
  14. BIN
      assets/images/icon_store_back.webp
  15. BIN
      assets/images/icon_store_banner1.webp
  16. BIN
      assets/images/icon_store_banner2.webp
  17. BIN
      assets/images/icon_store_banner3.webp
  18. BIN
      assets/images/icon_store_divider.webp
  19. BIN
      assets/images/icon_store_indicator1.webp
  20. BIN
      assets/images/icon_store_indicator2.webp
  21. BIN
      assets/images/icon_store_indicator3.webp
  22. BIN
      assets/images/icon_store_most.webp
  23. BIN
      assets/images/icon_store_permanent_member.webp
  24. BIN
      assets/images/icon_store_switch_pay.webp
  25. BIN
      assets/images/icon_store_user_review1.webp
  26. BIN
      assets/images/icon_store_user_review2.webp
  27. BIN
      assets/images/icon_store_user_review3.webp
  28. BIN
      assets/images/icon_store_user_review4.webp
  29. BIN
      assets/images/icon_store_user_reviews_logo.webp
  30. BIN
      assets/images/icon_store_user_reviews_title.webp
  31. BIN
      assets/images/icon_wechat_payment.webp
  32. BIN
      assets/images/icon_wechat_scan_payment.webp
  33. BIN
      assets/images/logo/logo.png
  34. 43 0
      assets/string/base/string.xml
  35. 32 0
      flutter_launcher_icons.yaml
  36. 2 2
      ios/Runner.xcodeproj/project.pbxproj
  37. 1 122
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
  38. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
  39. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
  40. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
  41. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
  42. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
  43. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
  44. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
  45. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
  46. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
  47. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
  48. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
  49. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
  50. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
  51. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
  52. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
  53. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
  54. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
  55. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
  56. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
  57. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
  58. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
  59. 22 0
      lib/data/api/atmob_api.dart
  60. 99 0
      lib/data/api/atmob_api.g.dart
  61. 29 0
      lib/data/api/request/order_pay_request.dart
  62. 75 0
      lib/data/api/request/order_pay_request.g.dart
  63. 19 0
      lib/data/api/request/order_status_request.dart
  64. 71 0
      lib/data/api/request/order_status_request.g.dart
  65. 21 0
      lib/data/api/response/item_list_response.dart
  66. 23 0
      lib/data/api/response/item_list_response.g.dart
  67. 48 0
      lib/data/api/response/order_pay_response.dart
  68. 33 0
      lib/data/api/response/order_pay_response.g.dart
  69. 14 0
      lib/data/api/response/order_status_response.dart
  70. 14 0
      lib/data/api/response/order_status_response.g.dart
  71. 4 1
      lib/data/api/response/user_info_response.dart
  72. 2 0
      lib/data/api/response/user_info_response.g.dart
  73. 81 0
      lib/data/bean/goods_info.dart
  74. 42 0
      lib/data/bean/goods_info.g.dart
  75. 4 1
      lib/data/bean/member_info.dart
  76. 2 0
      lib/data/bean/member_info.g.dart
  77. 27 0
      lib/data/bean/pay_way_info.dart
  78. 22 0
      lib/data/bean/pay_way_info.g.dart
  79. 47 0
      lib/data/bean/wechat_payment_sign_bean.dart
  80. 31 0
      lib/data/bean/wechat_payment_sign_bean.g.dart
  81. 6 3
      lib/data/consts/error_code.dart
  82. 54 0
      lib/data/consts/payment_type.dart
  83. 47 41
      lib/data/repository/account_repository.dart
  84. 53 0
      lib/data/repository/store_repository.dart
  85. 19 0
      lib/di/get_it.config.dart
  86. 119 0
      lib/dialog/alipay_qr_code_dialog.dart
  87. 62 0
      lib/dialog/loading_dialog.dart
  88. 171 0
      lib/dialog/member_agreement_dialog.dart
  89. 161 0
      lib/dialog/payment_success_dialog.dart
  90. 83 0
      lib/dialog/wechat_qr_code_dialog.dart
  91. 10 1
      lib/module/login/login_page.dart
  92. 2 0
      lib/module/mine/mine_controller.dart
  93. 13 0
      lib/module/store/store_banner_bean.dart
  94. 431 0
      lib/module/store/store_controller.dart
  95. 860 0
      lib/module/store/store_page.dart
  96. 8 0
      lib/module/store/store_user_reviews_bean.dart
  97. 130 0
      lib/resource/assets.gen.dart
  98. 66 0
      lib/resource/string.gen.dart
  99. 5 0
      lib/router/app_pages.dart
  100. 0 0
      lib/utils/common_expand.dart

BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


+ 17 - 0
android/build.gradle.kts

@@ -30,3 +30,20 @@ subprojects {
 tasks.register<Delete>("clean") {
     delete(rootProject.layout.buildDirectory)
 }
+
+subprojects {
+    plugins.withId("com.android.application") {
+        extensions.findByType<com.android.build.gradle.BaseExtension>()?.let { androidExt ->
+            if (androidExt.namespace == null) {
+                androidExt.namespace = project.group.toString()
+            }
+        }
+    }
+    plugins.withId("com.android.library") {
+        extensions.findByType<com.android.build.gradle.BaseExtension>()?.let { androidExt ->
+            if (androidExt.namespace == null) {
+                androidExt.namespace = project.group.toString()
+            }
+        }
+    }
+}

BIN
assets/images/bg_store_selected_item.webp


BIN
assets/images/bg_store_user_reviews.webp


BIN
assets/images/icon_alipay_payment.webp


BIN
assets/images/icon_alipay_scan_payment.webp


BIN
assets/images/icon_goods_info_title.webp


BIN
assets/images/icon_member_retain_close.webp


BIN
assets/images/icon_store_agree_privacy.webp


BIN
assets/images/icon_store_back.webp


BIN
assets/images/icon_store_banner1.webp


BIN
assets/images/icon_store_banner2.webp


BIN
assets/images/icon_store_banner3.webp


BIN
assets/images/icon_store_divider.webp


BIN
assets/images/icon_store_indicator1.webp


BIN
assets/images/icon_store_indicator2.webp


BIN
assets/images/icon_store_indicator3.webp


BIN
assets/images/icon_store_most.webp


BIN
assets/images/icon_store_permanent_member.webp


BIN
assets/images/icon_store_switch_pay.webp


BIN
assets/images/icon_store_user_review1.webp


BIN
assets/images/icon_store_user_review2.webp


BIN
assets/images/icon_store_user_review3.webp


BIN
assets/images/icon_store_user_review4.webp


BIN
assets/images/icon_store_user_reviews_logo.webp


BIN
assets/images/icon_store_user_reviews_title.webp


BIN
assets/images/icon_wechat_payment.webp


BIN
assets/images/icon_wechat_scan_payment.webp


BIN
assets/images/logo/logo.png


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

@@ -135,5 +135,48 @@
     <string name="tips_dialog_cancel">不保存</string>
     <string name="tips_dialog_desc">是否保存键盘当前的修改?</string>
 
+    <string name="store_pay_way">支付方式</string>
+    <string name="store_pay_now">立即支付</string>
+
+    <string name="member_card_no_vip_desc">,开通会员,享受会员权益</string>
+    <string name="member_vip_desc">您的会员有效期至</string>
+
+    <string name="member_card_permanent_vip_desc1">您已是</string>
+    <string name="member_card_permanent_vip_desc2">终身会员</string>
+    <string name="member_no_logged">游客</string>
+
+
+    <string name="text_span_i_have_read_and_agree">我已阅读并同意</string>
+    <string name="text_span_and">和</string>
+    <string name="text_span_privacy_policy">《隐私政策》</string>
+    <string name="text_span_service_terms">《服务条款》</string>
+
+
+
+    <string name="member_continue_pay">继续支付</string>
+    <string name="member_please_choice_goods">请选择支付商品</string>
+    <string name="member_please_choice_payment">请选择支付方式</string>
+
+    <string name="pay_loading">请求中...</string>
+    <string name="pay_user_cancel">用户取消支付</string>
+    <string name="pay_not_support">不支持该支付类型</string>
+    <string name="pay_wx_evn_error">微信未安装或微信版本不支持</string>
+    <string name="pay_error">支付失败,请稍后重试</string>
+    <string name="pay_query_pay_state">正在查询订单状态..</string>
+    <string name="pay_not_connect_store">无法连接到商店</string>
+    <string name="pay_success_title">支付成功</string>
+    <string name="pay_success_desc">您的订单已成功支付</string>
+    <string name="alipay_qr_code_tips">请使用支付宝扫码支付</string>
+    <string name="wechat_qr_code_tips">请使用微信扫码支付</string>
+    <string name="wechat_pay_qr_code_tips">请使用微信扫码支付</string>
+    <string name="member_payment_failed">开通失败,请稍后重试</string>
+
+    <string name="member_agreement_dialog_title">请阅读并同意以下协议</string>
+    <string name="member_agreement_dialog_agreement">《会员协议》</string>
+    <string name="member_agreement_dialog_auto_renewal">《自动续费协议》。</string>
+    <string name="member_agreement_dialog_desc">进入下一步前,请先阅读并同意</string>
+
+    <string name="member_agreement_dialog_confirm">确定</string>
+    <string name="member_agreement_dialog_cancel">取消</string>
 
 </resources>

+ 32 - 0
flutter_launcher_icons.yaml

@@ -0,0 +1,32 @@
+# flutter pub run flutter_launcher_icons
+flutter_launcher_icons:
+  image_path: "assets/images/logo/logo.png"
+
+  android: "ic_launcher"
+  # image_path_android: "assets/icon/icon.png"
+  min_sdk_android: 21 # android min sdk min:16, default 21
+  # adaptive_icon_background: "assets/icon/background.png"
+  # adaptive_icon_foreground: "assets/icon/foreground.png"
+  # adaptive_icon_monochrome: "assets/icon/monochrome.png"
+
+  ios: true
+  # image_path_ios: "assets/icon/icon.png"
+  remove_alpha_channel_ios: true
+  # image_path_ios_dark_transparent: "assets/icon/icon_dark.png"
+  # image_path_ios_tinted_grayscale: "assets/icon/icon_tinted.png"
+  # desaturate_tinted_to_grayscale_ios: true
+
+  web:
+    generate: false
+    image_path: "path/to/image.png"
+    background_color: "#hexcode"
+    theme_color: "#hexcode"
+
+  windows:
+    generate: false
+    image_path: "path/to/image.png"
+    icon_size: 48 # min:48, max:256, default: 48
+
+  macos:
+    generate: false
+    image_path: "path/to/image.png"

+ 2 - 2
ios/Runner.xcodeproj/project.pbxproj

@@ -427,7 +427,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
-				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
 				CLANG_CXX_LIBRARY = "libc++";
@@ -484,7 +484,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
-				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
 				CLANG_CXX_LIBRARY = "libc++";

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 122
ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png


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

@@ -15,6 +15,8 @@ import 'package:keyboard/data/api/request/keyboard_character_update_request.dart
 import 'package:keyboard/data/api/request/keyboard_list_request.dart';
 import 'package:keyboard/data/api/request/keyboard_update_request.dart';
 import 'package:keyboard/data/api/request/login_request.dart';
+import 'package:keyboard/data/api/request/order_pay_request.dart';
+import 'package:keyboard/data/api/request/order_status_request.dart';
 import 'package:keyboard/data/api/request/send_code_request.dart';
 import 'package:keyboard/data/api/request/user_info_setting_request.dart';
 import 'package:keyboard/data/api/response/character_add_response.dart';
@@ -26,10 +28,13 @@ import 'package:keyboard/data/api/response/character_group_response.dart';
 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/keyboard_character_list_response.dart';
 import 'package:keyboard/data/api/response/keyboard_list_response.dart';
 import 'package:keyboard/data/api/response/login_response.dart';
 import 'package:keyboard/data/api/response/new_user_get_character_response.dart';
+import 'package:keyboard/data/api/response/order_pay_response.dart';
+import 'package:keyboard/data/api/response/order_status_response.dart';
 import 'package:keyboard/data/api/response/user_info_response.dart';
 import 'package:keyboard/data/repository/config_repository.dart';
 import 'package:retrofit/error_logger.dart';
@@ -164,4 +169,21 @@ abstract class AtmobApi {
   //获取配置信息
   @POST("/project/keyboard/v1/confs")
   Future<BaseResponse<ConfigResponse>> confs(@Body() ConfigRequest request);
+
+
+//   商品列表
+  @POST("/project/keyboard/v1/item/list")
+  Future<BaseResponse<ItemListResponse>> getGoodsList(
+    @Body() AppBaseRequest request,
+  );
+
+//下拉发起支付
+  @POST("/project/keyboard/v1/order/pay")
+  Future<BaseResponse<OrderPayResponse>> orderPay(@Body() OrderPayRequest request);
+
+
+  @POST("/project/keyboard/v1/order/status")
+  Future<BaseResponse<OrderStatusResponse>> orderStatus(
+      @Body() OrderStatusRequest request);
+
 }

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

@@ -765,6 +765,105 @@ class _AtmobApi implements AtmobApi {
     return _value;
   }
 
+  @override
+  Future<BaseResponse<ItemListResponse>> getGoodsList(
+    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<ItemListResponse>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/item/list',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<ItemListResponse> _value;
+    try {
+      _value = BaseResponse<ItemListResponse>.fromJson(
+        _result.data!,
+        (json) => ItemListResponse.fromJson(json as Map<String, dynamic>),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
+  @override
+  Future<BaseResponse<OrderPayResponse>> orderPay(
+    OrderPayRequest 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<OrderPayResponse>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/order/pay',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<OrderPayResponse> _value;
+    try {
+      _value = BaseResponse<OrderPayResponse>.fromJson(
+        _result.data!,
+        (json) => OrderPayResponse.fromJson(json as Map<String, dynamic>),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
+  @override
+  Future<BaseResponse<OrderStatusResponse>> orderStatus(
+    OrderStatusRequest 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<OrderStatusResponse>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/order/status',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<OrderStatusResponse> _value;
+    try {
+      _value = BaseResponse<OrderStatusResponse>.fromJson(
+        _result.data!,
+        (json) => OrderStatusResponse.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 ||

+ 29 - 0
lib/data/api/request/order_pay_request.dart

@@ -0,0 +1,29 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../../base/app_base_request.dart';
+
+
+part 'order_pay_request.g.dart';
+
+@JsonSerializable()
+class OrderPayRequest extends AppBaseRequest {
+  @JsonKey(name: 'itemId')
+  int goodsId;
+
+  @JsonKey(name: 'payPlatform')
+  int payPlatform;
+
+  @JsonKey(name: 'payMethod')
+  int payMethod;
+
+  @JsonKey(name: 'returnUrl')
+  String? returnUrl;
+
+  OrderPayRequest(
+      {required this.goodsId,
+      required this.payPlatform,
+      required this.payMethod,this.returnUrl});
+
+  @override
+  Map<String, dynamic> toJson() => _$OrderPayRequestToJson(this);
+}

+ 75 - 0
lib/data/api/request/order_pay_request.g.dart

@@ -0,0 +1,75 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'order_pay_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+OrderPayRequest _$OrderPayRequestFromJson(Map<String, dynamic> json) =>
+    OrderPayRequest(
+        goodsId: (json['itemId'] as num).toInt(),
+        payPlatform: (json['payPlatform'] as num).toInt(),
+        payMethod: (json['payMethod'] as num).toInt(),
+        returnUrl: json['returnUrl'] as String?,
+      )
+      ..appPlatform = (json['appPlatform'] as num).toInt()
+      ..os = json['os'] as String
+      ..osVersion = json['osVersion'] as String
+      ..packageName = json['packageName'] as String?
+      ..appVersionName = json['appVersionName'] as String?
+      ..appVersionCode = (json['appVersionCode'] as num?)?.toInt()
+      ..channelName = json['channelName'] as String?
+      ..appId = (json['appId'] as num?)?.toInt()
+      ..tgPlatform = (json['tgPlatform'] as num?)?.toInt()
+      ..oaid = json['oaid'] as String?
+      ..aaid = json['aaid'] as String?
+      ..androidId = json['androidId'] as String?
+      ..imei = json['imei'] as String?
+      ..simImei0 = json['simImei0'] as String?
+      ..simImei1 = json['simImei1'] as String?
+      ..mac = json['mac'] as String?
+      ..idfa = json['idfa'] as String?
+      ..idfv = json['idfv'] as String?
+      ..machineId = json['machineId'] as String?
+      ..brand = json['brand'] as String?
+      ..model = json['model'] as String?
+      ..wifiName = json['wifiName'] as String?
+      ..region = json['region'] as String?
+      ..locLng = (json['locLng'] as num?)?.toDouble()
+      ..locLat = (json['locLat'] as num?)?.toDouble()
+      ..authToken = json['authToken'] as String?;
+
+Map<String, dynamic> _$OrderPayRequestToJson(OrderPayRequest instance) =>
+    <String, dynamic>{
+      'appPlatform': instance.appPlatform,
+      'os': instance.os,
+      'osVersion': instance.osVersion,
+      'packageName': instance.packageName,
+      'appVersionName': instance.appVersionName,
+      'appVersionCode': instance.appVersionCode,
+      'channelName': instance.channelName,
+      'appId': instance.appId,
+      'tgPlatform': instance.tgPlatform,
+      'oaid': instance.oaid,
+      'aaid': instance.aaid,
+      'androidId': instance.androidId,
+      'imei': instance.imei,
+      'simImei0': instance.simImei0,
+      'simImei1': instance.simImei1,
+      'mac': instance.mac,
+      'idfa': instance.idfa,
+      'idfv': instance.idfv,
+      'machineId': instance.machineId,
+      'brand': instance.brand,
+      'model': instance.model,
+      'wifiName': instance.wifiName,
+      'region': instance.region,
+      'locLng': instance.locLng,
+      'locLat': instance.locLat,
+      'authToken': instance.authToken,
+      'itemId': instance.goodsId,
+      'payPlatform': instance.payPlatform,
+      'payMethod': instance.payMethod,
+      'returnUrl': instance.returnUrl,
+    };

+ 19 - 0
lib/data/api/request/order_status_request.dart

@@ -0,0 +1,19 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../../base/app_base_request.dart';
+
+part 'order_status_request.g.dart';
+
+@JsonSerializable()
+class OrderStatusRequest extends AppBaseRequest {
+  @JsonKey(name: 'outTradeNo')
+  String outTradeNo;
+
+  @JsonKey(name: 'receiptData')
+  String? receiptData;
+
+  OrderStatusRequest(this.outTradeNo, this.receiptData);
+
+  @override
+  Map<String, dynamic> toJson() => _$OrderStatusRequestToJson(this);
+}

+ 71 - 0
lib/data/api/request/order_status_request.g.dart

@@ -0,0 +1,71 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'order_status_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+OrderStatusRequest _$OrderStatusRequestFromJson(Map<String, dynamic> json) =>
+    OrderStatusRequest(
+        json['outTradeNo'] as String,
+        json['receiptData'] as String?,
+      )
+      ..appPlatform = (json['appPlatform'] as num).toInt()
+      ..os = json['os'] as String
+      ..osVersion = json['osVersion'] as String
+      ..packageName = json['packageName'] as String?
+      ..appVersionName = json['appVersionName'] as String?
+      ..appVersionCode = (json['appVersionCode'] as num?)?.toInt()
+      ..channelName = json['channelName'] as String?
+      ..appId = (json['appId'] as num?)?.toInt()
+      ..tgPlatform = (json['tgPlatform'] as num?)?.toInt()
+      ..oaid = json['oaid'] as String?
+      ..aaid = json['aaid'] as String?
+      ..androidId = json['androidId'] as String?
+      ..imei = json['imei'] as String?
+      ..simImei0 = json['simImei0'] as String?
+      ..simImei1 = json['simImei1'] as String?
+      ..mac = json['mac'] as String?
+      ..idfa = json['idfa'] as String?
+      ..idfv = json['idfv'] as String?
+      ..machineId = json['machineId'] as String?
+      ..brand = json['brand'] as String?
+      ..model = json['model'] as String?
+      ..wifiName = json['wifiName'] as String?
+      ..region = json['region'] as String?
+      ..locLng = (json['locLng'] as num?)?.toDouble()
+      ..locLat = (json['locLat'] as num?)?.toDouble()
+      ..authToken = json['authToken'] as String?;
+
+Map<String, dynamic> _$OrderStatusRequestToJson(OrderStatusRequest instance) =>
+    <String, dynamic>{
+      'appPlatform': instance.appPlatform,
+      'os': instance.os,
+      'osVersion': instance.osVersion,
+      'packageName': instance.packageName,
+      'appVersionName': instance.appVersionName,
+      'appVersionCode': instance.appVersionCode,
+      'channelName': instance.channelName,
+      'appId': instance.appId,
+      'tgPlatform': instance.tgPlatform,
+      'oaid': instance.oaid,
+      'aaid': instance.aaid,
+      'androidId': instance.androidId,
+      'imei': instance.imei,
+      'simImei0': instance.simImei0,
+      'simImei1': instance.simImei1,
+      'mac': instance.mac,
+      'idfa': instance.idfa,
+      'idfv': instance.idfv,
+      'machineId': instance.machineId,
+      'brand': instance.brand,
+      'model': instance.model,
+      'wifiName': instance.wifiName,
+      'region': instance.region,
+      'locLng': instance.locLng,
+      'locLat': instance.locLat,
+      'authToken': instance.authToken,
+      'outTradeNo': instance.outTradeNo,
+      'receiptData': instance.receiptData,
+    };

+ 21 - 0
lib/data/api/response/item_list_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_list_response.g.dart';
+
+@JsonSerializable()
+class ItemListResponse {
+  @JsonKey(name: "list")
+  List<GoodsInfo>? goodsInfoList;
+
+  @JsonKey(name: "payOptions")
+  List<PayWayInfo>? payInfoList;
+
+  ItemListResponse(this.goodsInfoList, this.payInfoList);
+
+  factory ItemListResponse.fromJson(Map<String, dynamic> json) =>
+      _$ItemListResponseFromJson(json);
+}

+ 23 - 0
lib/data/api/response/item_list_response.g.dart

@@ -0,0 +1,23 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'item_list_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ItemListResponse _$ItemListResponseFromJson(Map<String, dynamic> json) =>
+    ItemListResponse(
+      (json['list'] as List<dynamic>?)
+          ?.map((e) => GoodsInfo.fromJson(e as Map<String, dynamic>))
+          .toList(),
+      (json['payOptions'] as List<dynamic>?)
+          ?.map((e) => PayWayInfo.fromJson(e as Map<String, dynamic>))
+          .toList(),
+    );
+
+Map<String, dynamic> _$ItemListResponseToJson(ItemListResponse instance) =>
+    <String, dynamic>{
+      'list': instance.goodsInfoList,
+      'payOptions': instance.payInfoList,
+    };

+ 48 - 0
lib/data/api/response/order_pay_response.dart

@@ -0,0 +1,48 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'order_pay_response.g.dart';
+
+@JsonSerializable()
+class OrderPayResponse {
+  @JsonKey(name: 'outTradeNo')
+  String outTradeNo;
+
+  @JsonKey(name: 'wechatPayQrcodeUrl')
+  String? wechatPayQrcodeUrl;
+
+  @JsonKey(name: 'wechatPayRedirectUrl')
+  String? wechatPayRedirectUrl;
+
+  @JsonKey(name: 'wechatPayPrepayJson')
+  String? wechatPayPrepayJson;
+
+  @JsonKey(name: 'alipayQrcodeHtml')
+  String? alipayQrcodeHtml;
+
+  @JsonKey(name: 'aliPaySubmitHtml')
+  String? aliPaySubmitHtml;
+
+  @JsonKey(name: 'alipayOrderString')
+  String? alipayOrderString;
+
+  @JsonKey(name: 'appAccountToken')
+  String? appAccountToken;
+
+  @JsonKey(name: 'wechatPayH5Url')
+  int? wechatPayH5Url;
+
+  OrderPayResponse({
+    required this.outTradeNo,
+    this.wechatPayQrcodeUrl,
+    this.wechatPayRedirectUrl,
+    this.wechatPayPrepayJson,
+    this.alipayQrcodeHtml,
+    this.aliPaySubmitHtml,
+    this.alipayOrderString,
+    this.appAccountToken,
+    this.wechatPayH5Url,
+  });
+
+  factory OrderPayResponse.fromJson(Map<String, dynamic> json) =>
+      _$OrderPayResponseFromJson(json);
+}

+ 33 - 0
lib/data/api/response/order_pay_response.g.dart

@@ -0,0 +1,33 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'order_pay_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+OrderPayResponse _$OrderPayResponseFromJson(Map<String, dynamic> json) =>
+    OrderPayResponse(
+      outTradeNo: json['outTradeNo'] as String,
+      wechatPayQrcodeUrl: json['wechatPayQrcodeUrl'] as String?,
+      wechatPayRedirectUrl: json['wechatPayRedirectUrl'] as String?,
+      wechatPayPrepayJson: json['wechatPayPrepayJson'] as String?,
+      alipayQrcodeHtml: json['alipayQrcodeHtml'] as String?,
+      aliPaySubmitHtml: json['aliPaySubmitHtml'] as String?,
+      alipayOrderString: json['alipayOrderString'] as String?,
+      appAccountToken: json['appAccountToken'] as String?,
+      wechatPayH5Url: (json['wechatPayH5Url'] as num?)?.toInt(),
+    );
+
+Map<String, dynamic> _$OrderPayResponseToJson(OrderPayResponse instance) =>
+    <String, dynamic>{
+      'outTradeNo': instance.outTradeNo,
+      'wechatPayQrcodeUrl': instance.wechatPayQrcodeUrl,
+      'wechatPayRedirectUrl': instance.wechatPayRedirectUrl,
+      'wechatPayPrepayJson': instance.wechatPayPrepayJson,
+      'alipayQrcodeHtml': instance.alipayQrcodeHtml,
+      'aliPaySubmitHtml': instance.aliPaySubmitHtml,
+      'alipayOrderString': instance.alipayOrderString,
+      'appAccountToken': instance.appAccountToken,
+      'wechatPayH5Url': instance.wechatPayH5Url,
+    };

+ 14 - 0
lib/data/api/response/order_status_response.dart

@@ -0,0 +1,14 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'order_status_response.g.dart';
+
+@JsonSerializable()
+class OrderStatusResponse {
+  @JsonKey(name: 'payStatus')
+  int payStatus;
+
+  OrderStatusResponse(this.payStatus);
+
+  factory OrderStatusResponse.fromJson(Map<String, dynamic> json) =>
+      _$OrderStatusResponseFromJson(json);
+}

+ 14 - 0
lib/data/api/response/order_status_response.g.dart

@@ -0,0 +1,14 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'order_status_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+OrderStatusResponse _$OrderStatusResponseFromJson(Map<String, dynamic> json) =>
+    OrderStatusResponse((json['payStatus'] as num).toInt());
+
+Map<String, dynamic> _$OrderStatusResponseToJson(
+  OrderStatusResponse instance,
+) => <String, dynamic>{'payStatus': instance.payStatus};

+ 4 - 1
lib/data/api/response/user_info_response.dart

@@ -42,7 +42,10 @@ class UserInfoResponse {
   @JsonKey(name: 'memberInfo')
   MemberInfo? memberInfo;
 
-  UserInfoResponse(
+  @JsonKey(name: 'name')
+  String? name;
+
+  UserInfoResponse(this.name,
     this.userId,
     this.ssid,
     this.deviceId,

+ 2 - 0
lib/data/api/response/user_info_response.g.dart

@@ -8,6 +8,7 @@ part of 'user_info_response.dart';
 
 UserInfoResponse _$UserInfoResponseFromJson(Map<String, dynamic> json) =>
     UserInfoResponse(
+      json['name'] as String?,
       json['userId'] as String?,
       json['ssid'] as String?,
       json['deviceId'] as String?,
@@ -37,4 +38,5 @@ Map<String, dynamic> _$UserInfoResponseToJson(UserInfoResponse instance) =>
       'userIdOrSsid': instance.userIdOrSsid,
       'imageUrl': instance.imageUrl,
       'memberInfo': instance.memberInfo,
+      'name': instance.name,
     };

+ 81 - 0
lib/data/bean/goods_info.dart

@@ -0,0 +1,81 @@
+import 'package:json_annotation/json_annotation.dart';
+import 'package:keyboard/utils/common_expand.dart';
+
+part 'goods_info.g.dart';
+
+@JsonSerializable()
+class GoodsInfo {
+  @JsonKey(name: "id")
+  late int id;
+  @JsonKey(name: "sort")
+  late int sort;
+  @JsonKey(name: "name")
+  late String name;
+  @JsonKey(name: "subscribable")
+  late int subscribable;
+  @JsonKey(name: "amount")
+  late int amount;
+  @JsonKey(name: "originalAmount")
+  late int originalAmount;
+  @JsonKey(name: "auth")
+  late String auth;
+  @JsonKey(name: "subscriptionMillis")
+  late int subscriptionMillis;
+  @JsonKey(name: "priceDesc")
+  late String priceDesc;
+  @JsonKey(name: 'payOptionIds')
+  List<int>? payOptionIds;
+  @JsonKey(name: 'description')
+  String? description;
+  @JsonKey(name: 'discountDesc')
+  String? discountDesc;
+  @JsonKey(name: 'mostDesc')
+  String? mostDesc;
+
+  GoodsInfo({
+    required this.id,
+    required this.sort,
+    required this.name,
+    required this.subscribable,
+    required this.amount,
+    required this.originalAmount,
+    required this.auth,
+    required this.subscriptionMillis,
+    required this.priceDesc,
+    this.payOptionIds,
+    this.description,
+    this.discountDesc,
+    this.mostDesc,
+  });
+
+  get amountText => (amount / 100).toFormattedString(2);
+
+  get originalAmountText => "${(originalAmount / 100).toFormattedString(2)}元";
+
+  get priceDescNumber => priceDesc.extractAmount();
+
+  get priceDescUnit => priceDesc.extractUnit();
+
+  factory GoodsInfo.fromJson(Map<String, dynamic> json) =>
+      _$GoodsInfoFromJson(json);
+
+  Map<String, dynamic> toJson() => _$GoodsInfoToJson(this);
+
+  GoodsInfo copyWith() {
+    return GoodsInfo(
+      id: id,
+      sort: sort,
+      name: name,
+      subscribable: subscribable,
+      amount: amount,
+      originalAmount: originalAmount,
+      auth: auth,
+      subscriptionMillis: subscriptionMillis,
+      priceDesc: priceDesc,
+      payOptionIds: payOptionIds,
+      description: description,
+      discountDesc: discountDesc,
+      mostDesc: mostDesc,
+    );
+  }
+}

+ 42 - 0
lib/data/bean/goods_info.g.dart

@@ -0,0 +1,42 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'goods_info.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+GoodsInfo _$GoodsInfoFromJson(Map<String, dynamic> json) => GoodsInfo(
+  id: (json['id'] as num).toInt(),
+  sort: (json['sort'] as num).toInt(),
+  name: json['name'] as String,
+  subscribable: (json['subscribable'] as num).toInt(),
+  amount: (json['amount'] as num).toInt(),
+  originalAmount: (json['originalAmount'] as num).toInt(),
+  auth: json['auth'] as String,
+  subscriptionMillis: (json['subscriptionMillis'] as num).toInt(),
+  priceDesc: json['priceDesc'] as String,
+  payOptionIds:
+      (json['payOptionIds'] as List<dynamic>?)
+          ?.map((e) => (e as num).toInt())
+          .toList(),
+  description: json['description'] as String?,
+  discountDesc: json['discountDesc'] as String?,
+  mostDesc: json['mostDesc'] as String?,
+);
+
+Map<String, dynamic> _$GoodsInfoToJson(GoodsInfo instance) => <String, dynamic>{
+  'id': instance.id,
+  'sort': instance.sort,
+  'name': instance.name,
+  'subscribable': instance.subscribable,
+  'amount': instance.amount,
+  'originalAmount': instance.originalAmount,
+  'auth': instance.auth,
+  'subscriptionMillis': instance.subscriptionMillis,
+  'priceDesc': instance.priceDesc,
+  'payOptionIds': instance.payOptionIds,
+  'description': instance.description,
+  'discountDesc': instance.discountDesc,
+  'mostDesc': instance.mostDesc,
+};

+ 4 - 1
lib/data/bean/member_info.dart

@@ -10,7 +10,10 @@ class MemberInfo {
   @JsonKey(name: 'endTimestamp')
   int? endTimestamp;
 
-  MemberInfo(this.isMember, this.endTimestamp);
+  @JsonKey(name: 'permanent')
+  bool? permanent;
+
+  MemberInfo(this.isMember, this.endTimestamp, this.permanent);
 
   factory MemberInfo.fromJson(Map<String, dynamic> json) =>
       _$MemberInfoFromJson(json);

+ 2 - 0
lib/data/bean/member_info.g.dart

@@ -9,10 +9,12 @@ part of 'member_info.dart';
 MemberInfo _$MemberInfoFromJson(Map<String, dynamic> json) => MemberInfo(
   json['isMember'] as bool,
   (json['endTimestamp'] as num?)?.toInt(),
+  json['permanent'] as bool?,
 );
 
 Map<String, dynamic> _$MemberInfoToJson(MemberInfo instance) =>
     <String, dynamic>{
       'isMember': instance.isMember,
       'endTimestamp': instance.endTimestamp,
+      'permanent': instance.permanent,
     };

+ 27 - 0
lib/data/bean/pay_way_info.dart

@@ -0,0 +1,27 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'pay_way_info.g.dart';
+
+@JsonSerializable()
+class PayWayInfo {
+  @JsonKey(name: 'id')
+  int id;
+
+  @JsonKey(name: 'payMethod')
+  int payMethod;
+
+  @JsonKey(name: 'payPlatform')
+  int payPlatform;
+
+  @JsonKey(name: 'title')
+  String title;
+
+  PayWayInfo(this.id, this.payMethod, this.payPlatform, this.title);
+
+  factory PayWayInfo.fromJson(Map<String, dynamic> json) =>
+      _$PayWayInfoFromJson(json);
+
+  PayWayInfo copyWith() {
+    return PayWayInfo(id, payMethod, payPlatform, title);
+  }
+}

+ 22 - 0
lib/data/bean/pay_way_info.g.dart

@@ -0,0 +1,22 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'pay_way_info.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+PayWayInfo _$PayWayInfoFromJson(Map<String, dynamic> json) => PayWayInfo(
+  (json['id'] as num).toInt(),
+  (json['payMethod'] as num).toInt(),
+  (json['payPlatform'] as num).toInt(),
+  json['title'] as String,
+);
+
+Map<String, dynamic> _$PayWayInfoToJson(PayWayInfo instance) =>
+    <String, dynamic>{
+      'id': instance.id,
+      'payMethod': instance.payMethod,
+      'payPlatform': instance.payPlatform,
+      'title': instance.title,
+    };

+ 47 - 0
lib/data/bean/wechat_payment_sign_bean.dart

@@ -0,0 +1,47 @@
+import 'dart:convert';
+
+import 'package:json_annotation/json_annotation.dart';
+
+part 'wechat_payment_sign_bean.g.dart';
+
+@JsonSerializable()
+class WechatPaymentSignBean {
+  @JsonKey(name: 'appid')
+  String appId;
+
+  @JsonKey(name: 'noncestr')
+  String nonceStr;
+
+  @JsonKey(name: 'package')
+  String package;
+
+  @JsonKey(name: 'partnerid')
+  String partnerId;
+
+  @JsonKey(name: 'prepayid')
+  String prepayId;
+
+  @JsonKey(name: 'sign')
+  String sign;
+
+  @JsonKey(name: 'timestamp')
+  String timeStamp;
+
+  WechatPaymentSignBean({
+    this.appId = '',
+    this.nonceStr = '',
+    this.package = '',
+    this.partnerId = '',
+    this.prepayId = '',
+    this.sign = '',
+    this.timeStamp = '',
+  });
+
+  static WechatPaymentSignBean stringToBean(String jsonStr) {
+    final Map<String, dynamic> json = jsonDecode(jsonStr);
+    return WechatPaymentSignBean.fromJson(json);
+  }
+
+  factory WechatPaymentSignBean.fromJson(Map<String, dynamic> json) =>
+      _$WechatPaymentSignBeanFromJson(json);
+}

+ 31 - 0
lib/data/bean/wechat_payment_sign_bean.g.dart

@@ -0,0 +1,31 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'wechat_payment_sign_bean.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+WechatPaymentSignBean _$WechatPaymentSignBeanFromJson(
+  Map<String, dynamic> json,
+) => WechatPaymentSignBean(
+  appId: json['appid'] as String? ?? '',
+  nonceStr: json['noncestr'] as String? ?? '',
+  package: json['package'] as String? ?? '',
+  partnerId: json['partnerid'] as String? ?? '',
+  prepayId: json['prepayid'] as String? ?? '',
+  sign: json['sign'] as String? ?? '',
+  timeStamp: json['timestamp'] as String? ?? '',
+);
+
+Map<String, dynamic> _$WechatPaymentSignBeanToJson(
+  WechatPaymentSignBean instance,
+) => <String, dynamic>{
+  'appid': instance.appId,
+  'noncestr': instance.nonceStr,
+  'package': instance.package,
+  'partnerid': instance.partnerId,
+  'prepayid': instance.prepayId,
+  'sign': instance.sign,
+  'timestamp': instance.timeStamp,
+};

+ 6 - 3
lib/data/consts/error_code.dart

@@ -2,11 +2,14 @@ import '../../resource/string.gen.dart';
 
 class ErrorCode {
   /// 登录相关错误码
-  static const int verificationCodeError = 1005;
-  static const int noLoginError = 1006;
-  static const int noMember = 1007;
+  static const int verificationCodeError = 1005; //验证码错误
+  static const int noLoginError = 1006; //未登录
+  static const int noMember = 1007; //没有会员
 
+/// 会员服务相关错误码
 
+  static const int isMember = 1301; //您已经是会员了
+  static const int payOrderError = 1004;
 }
 
 /// 错误码扩展方法

+ 54 - 0
lib/data/consts/payment_type.dart

@@ -0,0 +1,54 @@
+import '../../resource/assets.gen.dart';
+
+class PayPlatform {
+  static const int android = 1;
+  static const int apple = 2;
+}
+
+class PayMethod {
+  static const int alipay = 1;
+  static const int wechat = 2;
+  static const int apple = 3;
+  static const int google = 4;
+  static const int douYin = 5;
+}
+
+class Currency {
+  static const String cny = "CNY";
+  static const String usd = "USD";
+}
+
+class PayWayType {
+  static const int paymentWayWechat = 1;
+  static const int paymentWayWechatScan = 2;
+  static const int paymentWayAlipay = 3;
+  static const int paymentWayAlipayScan = 4;
+
+  static const int unKnown = -1;
+}
+
+String getPaymentIconPath({required int payMethod, required int payPlatform}) {
+  if (payPlatform == 1 && payMethod == 2) {
+    return Assets.images.iconWechatPayment.path;
+  } else if (payPlatform == 4 && payMethod == 2) {
+    return Assets.images.iconWechatScanPayment.path;
+  } else if (payPlatform == 1 && payMethod == 1) {
+    return Assets.images.iconAlipayPayment.path;
+  } else if (payPlatform == 4 && payMethod == 1) {
+    return Assets.images.iconAlipayScanPayment.path;
+  }
+  return '';
+}
+
+int getPayWayType({required int payMethod, required int payPlatform}) {
+  if (payPlatform == 1 && payMethod == 2) {
+    return PayWayType.paymentWayWechat;
+  } else if (payPlatform == 4 && payMethod == 2) {
+    return PayWayType.paymentWayWechatScan;
+  } else if (payPlatform == 1 && payMethod == 1) {
+    return PayWayType.paymentWayAlipay;
+  } else if (payPlatform == 4 && payMethod == 1) {
+    return PayWayType.paymentWayAlipayScan;
+  }
+  return PayWayType.unKnown;
+}

+ 47 - 41
lib/data/repository/account_repository.dart

@@ -4,6 +4,7 @@ import 'package:get/get.dart';
 import 'package:injectable/injectable.dart';
 import 'package:keyboard/data/api/atmob_api.dart';
 import 'package:keyboard/data/api/request/user_info_setting_request.dart';
+import 'package:keyboard/data/bean/member_info.dart';
 
 import '../../base/app_base_request.dart';
 import '../../di/get_it.dart';
@@ -31,6 +32,7 @@ class AccountRepository {
 
   RxnString loginPhoneNum = RxnString();
   RxBool isLogin = RxBool(false);
+  Rxn<MemberInfo> memberStatusInfo = Rxn<MemberInfo>();
 
   int? _lastRequestCodeTime;
   int _errorCodeTimes = 0;
@@ -53,9 +55,7 @@ class AccountRepository {
 
   // 检查是否在 60 秒内重复请求
   Future<void> loginSendCode(String phoneNum) {
-    final currentTime = DateTime
-        .now()
-        .millisecondsSinceEpoch;
+    final currentTime = DateTime.now().millisecondsSinceEpoch;
     if (currentTime - (_lastRequestCodeTime ?? 0) < 60 * 1000) {
       throw RequestCodeTooOftenException();
     }
@@ -63,13 +63,15 @@ class AccountRepository {
         .loginSendCode(SendCodeRequest(phoneNum))
         .then(HttpHandler.handle(true))
         .then((value) {
-      _lastRequestCodeTime = currentTime;
-      _errorCodeTimes = 0;
-    });
+          _lastRequestCodeTime = currentTime;
+          _errorCodeTimes = 0;
+        });
   }
 
-  Future<LoginResponse> loginUserLogin(String phoneNum,
-      String verificationCode,) {
+  Future<LoginResponse> loginUserLogin(
+    String phoneNum,
+    String verificationCode,
+  ) {
     if (_errorCodeTimes >= 5) {
       throw LoginTooOftenException();
     }
@@ -77,23 +79,23 @@ class AccountRepository {
         .loginUserLogin(LoginRequest(phoneNum, verificationCode))
         .then(HttpHandler.handle(true))
         .then((response) {
-      _errorCodeTimes = 0;
-      onLoginSuccess(phoneNum, response.authToken);
-      return response;
-    })
+          _errorCodeTimes = 0;
+          onLoginSuccess(phoneNum, response.authToken);
+          return response;
+        })
         .catchError((error) {
-      if (error is ServerErrorException &&
-          error.code == ErrorCode.verificationCodeError) {
-        _errorCodeTimes++;
-      }
-      throw error;
-    });
+          if (error is ServerErrorException &&
+              error.code == ErrorCode.verificationCodeError) {
+            _errorCodeTimes++;
+          }
+          throw error;
+        });
   }
 
   void refreshUserInfo() {
     userInfoFuture?.cancel();
     userInfoFuture = AsyncUtil.retryWithExponentialBackoff(
-          () => getUserInfo(),
+      () => getUserInfo(),
       10,
       predicate: (error) {
         if (error is ServerErrorException) {
@@ -104,11 +106,11 @@ class AccountRepository {
     );
     userInfoFuture
         ?.then((userInfo) {
-      AtmobLog.d(tag, "refreshUserInfo success");
-    })
+          AtmobLog.d(tag, "refreshUserInfo success: ${memberStatusInfo.value}");
+        })
         .catchError((error) {
-      AtmobLog.e(tag, "refreshUserInfo error: $error");
-    });
+          AtmobLog.e(tag, "refreshUserInfo error: $error");
+        });
   }
 
   Future<UserInfoResponse> getUserInfo() {
@@ -116,31 +118,35 @@ class AccountRepository {
         .getUserInfo(AppBaseRequest())
         .then(HttpHandler.handle(true))
         .then((response) {
-      _userInfo.value = response;
-
-      return response;
-    });
+          _userInfo.value = response;
+          memberStatusInfo.value = response.memberInfo;
+          return response;
+        });
   }
 
-  Future<void> setUserInfo(
-      {String? name, String? birthday, int? gender, String? imageUrl, List<
-          String>?hobbies, List<String>?characters}) {
+  Future<void> setUserInfo({
+    String? name,
+    String? birthday,
+    int? gender,
+    String? imageUrl,
+    List<String>? hobbies,
+    List<String>? characters,
+  }) {
     return atmobApi
         .setUserInfo(
-      UserInfoSettingRequest(
-          name: name,
-          birthday: birthday,
-          gender: gender,
-          imageUrl: imageUrl,
-          hobbies: hobbies,
-          characters:characters
-      ),
-    )
+          UserInfoSettingRequest(
+            name: name,
+            birthday: birthday,
+            gender: gender,
+            imageUrl: imageUrl,
+            hobbies: hobbies,
+            characters: characters,
+          ),
+        )
         .then(HttpHandler.handle(true));
   }
 
   void onLoginSuccess(String phoneNum, String authToken) {
-
     AccountRepository.token = authToken;
     loginPhoneNum.value = phoneNum;
     refreshUserInfo();
@@ -153,7 +159,7 @@ class AccountRepository {
 
     KVUtil.putString(keyAccountLoginPhoneNum, null);
     KVUtil.putString(keyAccountLoginToken, null);
-
+    memberStatusInfo.value = null;
     loginPhoneNum.value = null;
   }
 

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

@@ -0,0 +1,53 @@
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/data/api/atmob_api.dart';
+import 'package:keyboard/data/api/response/item_list_response.dart';
+import 'package:keyboard/data/api/response/order_pay_response.dart';
+import 'package:keyboard/utils/http_handler.dart';
+
+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 'account_repository.dart';
+
+@lazySingleton
+class StoreRepository {
+  final tag = 'StoreRepository';
+
+  final AtmobApi atmobApi;
+  final AccountRepository accountRepository;
+
+  StoreRepository(this.atmobApi, this.accountRepository);
+
+  Future<ItemListResponse> getGoodsInfoList() async {
+    return await atmobApi
+        .getGoodsList(AppBaseRequest())
+        .then(HttpHandler.handle(true));
+  }
+
+
+  Future<OrderPayResponse> submitAndRequestPay(
+      {required int goodsId,
+        required int payPlatform,
+        required int payMethod}) {
+    return atmobApi
+        .orderPay(OrderPayRequest(
+        goodsId: goodsId, payPlatform: payPlatform, payMethod: payMethod))
+        .then(HttpHandler.handle(false));
+  }
+
+  Future<int>orderStatus(String outTradeNo, {String? receiptData}) {
+    return atmobApi
+        .orderStatus(OrderStatusRequest(outTradeNo, receiptData))
+        .then(HttpHandler.handle(false))
+        .then((data) {
+          if (data.payStatus == PaymentStatus.payStatusSuccess) {
+            accountRepository.refreshUserInfo();
+          }
+      return data.payStatus;
+    });
+  }
+
+
+
+}

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

@@ -22,6 +22,7 @@ import '../data/repository/characters_repository.dart' as _i421;
 import '../data/repository/chat_repository.dart' as _i425;
 import '../data/repository/config_repository.dart' as _i50;
 import '../data/repository/keyboard_repository.dart' as _i274;
+import '../data/repository/store_repository.dart' as _i987;
 import '../dialog/content/character_add_tab_controller.dart' as _i991;
 import '../dialog/content/character_tab_group_content_controller.dart' as _i293;
 import '../module/about/about_controller.dart' as _i256;
@@ -39,7 +40,9 @@ import '../module/keyboard_manage/keyboard_manage_controller.dart' as _i922;
 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 '../plugins/keyboard_android_platform.dart' as _i79;
+import '../utils/payment_status_manager.dart' as _i779;
 import 'network_module.dart' as _i567;
 
 extension GetItInjectableX on _i174.GetIt {
@@ -102,6 +105,15 @@ extension GetItInjectableX on _i174.GetIt {
             currentKeyboardInfo: currentKeyboardInfo,
           ),
     );
+    gh.lazySingleton<_i987.StoreRepository>(
+      () => _i987.StoreRepository(
+        gh<_i243.AtmobApi>(),
+        gh<_i83.AccountRepository>(),
+      ),
+    );
+    gh.lazySingleton<_i779.PaymentStatusManager>(
+      () => _i779.PaymentStatusManager(gh<_i987.StoreRepository>()),
+    );
     gh.factory<_i922.KeyboardManageController>(
       () => _i922.KeyboardManageController(gh<_i274.KeyboardRepository>()),
     );
@@ -149,6 +161,13 @@ extension GetItInjectableX on _i174.GetIt {
         gh<_i274.KeyboardRepository>(),
       ),
     );
+    gh.factory<_i344.StoreController>(
+      () => _i344.StoreController(
+        gh<_i987.StoreRepository>(),
+        gh<_i83.AccountRepository>(),
+        gh<_i779.PaymentStatusManager>(),
+      ),
+    );
     return this;
   }
 }

+ 119 - 0
lib/dialog/alipay_qr_code_dialog.dart

@@ -0,0 +1,119 @@
+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:webview_flutter/webview_flutter.dart';
+
+import '../resource/assets.gen.dart';
+import '../resource/colors.gen.dart';
+import '../resource/string.gen.dart';
+
+class AlipayQrCodeDialog {
+  static const String _tag = 'AlipayQrCodeDialog';
+
+  static void show(
+      {required String qrCodeHtml,
+      VoidCallback? onDismiss,
+      VoidCallback? onCloseCallback,
+      VoidCallback? loadSuccessCallback}) {
+    SmartDialog.show(
+        tag: _tag,
+        onDismiss: onDismiss,
+        backDismiss: false,
+        builder: (_) {
+          return AlipayQrCodeView(
+              qrCodeHtml: qrCodeHtml,
+              onCloseCallback: onCloseCallback,
+              loadSuccessCallback: loadSuccessCallback);
+        },
+        clickMaskDismiss: false);
+  }
+
+  static void dismiss() {
+    SmartDialog.dismiss(tag: _tag);
+  }
+}
+
+class AlipayQrCodeView extends Dialog {
+  final String qrCodeHtml;
+  VoidCallback? loadSuccessCallback;
+  VoidCallback? onCloseCallback;
+
+  final WebViewController webViewController = WebViewController();
+
+  AlipayQrCodeView(
+      {super.key,
+      required this.qrCodeHtml,
+      this.onCloseCallback,
+      this.loadSuccessCallback});
+
+  @override
+  Widget build(BuildContext context) {
+    return IntrinsicHeight(
+      child: Column(
+        children: [
+          Container(
+            padding: EdgeInsets.all(26.w),
+            decoration: BoxDecoration(
+              color: Colors.white,
+              borderRadius: BorderRadius.circular(10.w),
+            ),
+            child: Column(
+              children: [
+                SizedBox(
+                  width: 220.w,
+                  height: 220.w,
+                  child: WebViewWidget(
+                    controller: webViewController
+                      ..loadHtmlString(getHtmlTemplate())
+                      ..setJavaScriptMode(JavaScriptMode.unrestricted)
+                      ..setNavigationDelegate(NavigationDelegate(
+                        onPageFinished: (String url) {
+                          // 使用 CSS 放大页面内容
+                          final scaleFactor = Get.width / 45.w;
+                          webViewController.runJavaScript('''
+                         document.body.style.transform = "scale($scaleFactor)";
+                         document.body.style.transformOrigin = "0 0";  
+                         
+                        document.body.style.touchAction = 'none';
+                        document.documentElement.style.overscrollBehavior = 'none';           
+                      ''');
+                          loadSuccessCallback?.call();
+                          loadSuccessCallback = null;
+                        },
+                      )),
+                  ),
+                ),
+                SizedBox(height: 16.w),
+                Text(StringName.alipayQrCodeTips,
+                    style: TextStyle(fontSize: 14.sp, color: ColorName.black90))
+              ],
+            ),
+          ),
+          SizedBox(height: 20.w),
+          GestureDetector(
+              onTap: () {
+                onCloseCallback?.call();
+                AlipayQrCodeDialog.dismiss();
+              },
+              child: Assets.images.iconMemberRetainClose
+                  .image(width: 32.w, height: 32.w))
+        ],
+      ),
+    );
+  }
+
+  String getHtmlTemplate() {
+    return '''
+    <!DOCTYPE html><html lang=\"en\">
+      <head><meta charset=\"UTF-8\"/>
+        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>
+        <title>Alipay QR Code</title>
+      </head>
+      <body>$qrCodeHtml</body>
+     </html>
+ 
+     ''';
+  }
+}

+ 62 - 0
lib/dialog/loading_dialog.dart

@@ -0,0 +1,62 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+
+import '../resource/colors.gen.dart';
+
+class LoadingDialog {
+  static void show(String msg) {
+    SmartDialog.showLoading(msg: msg);
+  }
+
+  static void hide() {
+    SmartDialog.dismiss();
+  }
+}
+
+class CustomLoadingDialog {
+  static String tag = "CustomLoadingDialog";
+
+  static void show({String? loadingTxt, bool backDismiss = false}) {
+    SmartDialog.show(
+        tag: tag,
+        backDismiss: backDismiss,
+        clickMaskDismiss: false,
+        builder: (_) {
+          return Container(
+              padding: EdgeInsets.all(20.w),
+              decoration: BoxDecoration(
+                color: ColorName.black70,
+                borderRadius: BorderRadius.circular(12.w),
+              ),
+              child: IntrinsicHeight(
+                child: Column(
+                  mainAxisAlignment: MainAxisAlignment.center,
+                  children: [
+                    CircularProgressIndicator(
+                      strokeWidth: 3.w,
+                      valueColor:
+                          const AlwaysStoppedAnimation(ColorName.white75),
+                    ),
+                    Visibility(
+                        visible: loadingTxt != null,
+                        child: SizedBox(height: 12.w)),
+                    Builder(builder: (context) {
+                      if (loadingTxt == null) {
+                        return const SizedBox.shrink();
+                      }
+                      return Text(loadingTxt,
+                          style: TextStyle(
+                              fontSize: 13.sp, color: ColorName.white75));
+                    })
+                  ],
+                ),
+              ));
+        });
+  }
+
+  static void hide() {
+    SmartDialog.dismiss(tag: tag);
+  }
+}

+ 171 - 0
lib/dialog/member_agreement_dialog.dart

@@ -0,0 +1,171 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:keyboard/resource/string.gen.dart';
+
+import '../data/consts/web_url.dart';
+import '../resource/assets.gen.dart';
+import '../resource/colors.gen.dart';
+import '../utils/styles.dart';
+import '../widget/click_text_span.dart';
+
+// 会员协议弹窗
+class MemberAgreementDialog {
+  static const String tag = 'MemberAgreementDialog';
+
+  static void show({Function? btnCancel, Function? btnConfirm}) {
+    SmartDialog.show(
+      tag: tag,
+      backType: SmartBackType.block,
+
+      clickMaskDismiss: true,
+      maskColor: ColorName.black70,
+      builder: (_) {
+        return Column(
+          crossAxisAlignment: CrossAxisAlignment.center,
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: [
+            Container(
+              margin: EdgeInsets.symmetric(horizontal: 31.w),
+              decoration: ShapeDecoration(
+                color: Colors.white,
+                shape: RoundedRectangleBorder(
+                  borderRadius: BorderRadius.circular(20.r),
+                ),
+              ),
+              child: Stack(
+                children: [
+                  Container(
+                    padding: EdgeInsets.symmetric(
+                      horizontal: 16.w,
+                      vertical: 24.h,
+                    ),
+                    child: Column(
+                      mainAxisSize: MainAxisSize.min,
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      mainAxisAlignment: MainAxisAlignment.center,
+                      children: [
+                        Text(
+                          StringName.memberAgreementDialogTitle,
+                          style: Styles.getTextStyleBlack204W500(16.sp),
+                        ),
+                        SizedBox(height: 16.h),
+                        Text.rich(
+                          TextSpan(
+                            children: [
+                              TextSpan(
+                                text: StringName.memberAgreementDialogDesc,
+                                style: TextStyle(
+                                  color: Colors.black.withAlpha(153),
+                                  fontSize: 14.sp,
+                                  fontWeight: FontWeight.w400,
+                                ),
+                              ),
+                              ClickTextSpan(
+                                fontSize: 14.sp,
+                                text: StringName.memberAgreementDialogAgreement,
+                                url: WebUrl.privacyPolicy,
+                              ),
+
+                              TextSpan(
+                                text: StringName.textSpanAnd,
+                                style: TextStyle(
+                                  color: Colors.black.withAlpha(153),
+                                  fontSize: 14.sp,
+                                  fontWeight: FontWeight.w400,
+                                ),
+                              ),
+
+                              ClickTextSpan(
+                                fontSize: 14.sp,
+                                text:
+                                    StringName.memberAgreementDialogAutoRenewal,
+                                url: WebUrl.serviceAgreement,
+                              ),
+                            ],
+                          ),
+                        ),
+                        SizedBox(height: 20.h),
+                        Row(
+                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                          children: [
+                            GestureDetector(
+                              onTap: () {
+                                if (btnCancel != null) {
+                                  btnCancel();
+                                }
+                                SmartDialog.dismiss();
+                              },
+                              child: Container(
+                                height: 40.h,
+                                width: 128.w,
+                                alignment: Alignment.center,
+                                decoration: ShapeDecoration(
+                                  color: const Color(0xFFF5F4F9),
+                                  shape: RoundedRectangleBorder(
+                                    borderRadius: BorderRadius.circular(50.r),
+                                  ),
+                                ),
+                                child: Text(
+                                  StringName.memberAgreementDialogCancel,
+                                  style: Styles.getTextStyleBlack204W500(16.sp),
+                                ),
+                              ),
+                            ),
+                            GestureDetector(
+                              onTap: () {
+                                if (btnConfirm != null) {
+                                  btnConfirm();
+                                }
+                                SmartDialog.dismiss(tag: tag);
+                              },
+                              child: Container(
+                                height: 40.h,
+                                width: 128.r,
+                                alignment: Alignment.center,
+                                decoration: ShapeDecoration(
+                                  shape: RoundedRectangleBorder(
+                                    borderRadius: BorderRadius.circular(50.r),
+                                  ),
+                                  gradient: LinearGradient(
+                                    begin: Alignment.topCenter,
+                                    end: Alignment.centerRight,
+                                    colors: [
+                                      const Color(0xFF7D46FC),
+                                      const Color(0xFFBC87FF),
+                                    ],
+                                  ),
+                                ),
+                                child: Text(
+                                  StringName.memberAgreementDialogConfirm,
+                                  style: Styles.getTextStyleWhiteW500(16.sp),
+                                ),
+                              ),
+                            ),
+                          ],
+                        ),
+                      ],
+                    ),
+                  ),
+                  Positioned(
+                    right: 14.w,
+                    top: 14.h,
+                    child: GestureDetector(
+                      onTap: () {
+                        SmartDialog.dismiss();
+                      },
+                      child: Assets.images.iconCustomDialogClose.image(
+                        width: 24.w,
+                        height: 24.h,
+                      ),
+                    ),
+                  ),
+                ],
+              ),
+            ),
+          ],
+        );
+      },
+    );
+  }
+}

+ 161 - 0
lib/dialog/payment_success_dialog.dart

@@ -0,0 +1,161 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:keyboard/resource/string.gen.dart';
+
+import '../data/consts/web_url.dart';
+import '../resource/assets.gen.dart';
+import '../resource/colors.gen.dart';
+import '../utils/styles.dart';
+import '../widget/click_text_span.dart';
+
+// 支付成功弹窗
+class PaymentSuccessDialog {
+  static const String tag = 'PaymentSuccessDialog';
+
+  static void show({Function? btnCancel, Function? btnConfirm}) {
+    SmartDialog.show(
+      tag: tag,
+      backType: SmartBackType.block,
+      clickMaskDismiss: true,
+      maskColor: ColorName.black70,
+      builder: (_) {
+        return Column(
+          crossAxisAlignment: CrossAxisAlignment.center,
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: [
+            Container(
+              width: double.infinity,
+              margin: EdgeInsets.symmetric(horizontal: 31.w),
+              decoration: ShapeDecoration(
+                color: Colors.white,
+                shape: RoundedRectangleBorder(
+                  borderRadius: BorderRadius.circular(20.r),
+                ),
+              ),
+              child: Stack(
+                children: [
+                  Container(
+                    padding: EdgeInsets.symmetric(
+                      horizontal: 16.w,
+                      vertical: 24.h,
+                    ),
+                    child: Column(
+                      mainAxisSize: MainAxisSize.min,
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      mainAxisAlignment: MainAxisAlignment.center,
+                      children: [
+                        Text(
+                          StringName.memberAgreementDialogTitle,
+                          style: Styles.getTextStyleBlack204W500(16.sp),
+                        ),
+                        SizedBox(height: 16.h),
+                        Text.rich(
+                          TextSpan(
+                            children: [
+                              TextSpan(
+                                text: StringName.memberAgreementDialogDesc,
+                                style: TextStyle(
+                                  color: Colors.black.withAlpha(153),
+                                  fontSize: 14.sp,
+                                  fontWeight: FontWeight.w400,
+                                ),
+                              ),
+                              ClickTextSpan(
+                                fontSize: 14.sp,
+                                text: StringName.memberAgreementDialogAgreement,
+                                url: WebUrl.privacyPolicy,
+                              ),
+
+                              TextSpan(
+                                text: StringName.textSpanAnd,
+                                style: TextStyle(
+                                  color: Colors.black.withAlpha(153),
+                                  fontSize: 14.sp,
+                                  fontWeight: FontWeight.w400,
+                                ),
+                              ),
+
+                              ClickTextSpan(
+                                fontSize: 14.sp,
+                                text:
+                                StringName.memberAgreementDialogAutoRenewal,
+                                url: WebUrl.serviceAgreement,
+                              ),
+                            ],
+                          ),
+                        ),
+                        SizedBox(height: 20.h),
+                        Row(
+                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                          children: [
+                            GestureDetector(
+                              onTap: () {
+                                if (btnCancel != null) {
+                                  btnCancel();
+                                }
+                                SmartDialog.dismiss();
+                              },
+                              child: Container(
+                                height: 48.h,
+                                alignment: Alignment.center,
+                                decoration: ShapeDecoration(
+                                  color: const Color(0xFFF5F4F9),
+                                  shape: RoundedRectangleBorder(
+                                    borderRadius: BorderRadius.circular(31.r),
+                                  ),
+                                ),
+                                child: Text(
+                                  StringName.memberAgreementDialogCancel,
+                                  style: Styles.getTextStyleBlack204W500(16.sp),
+                                ),
+                              ),
+                            ),
+
+                            SizedBox(width: 16.w),
+                            GestureDetector(
+                              onTap: () {
+                                if (btnConfirm != null) {
+                                  btnConfirm();
+                                }
+                                SmartDialog.dismiss(tag: tag);
+                              },
+                              child: Container(
+                                height: 48.h,
+                                alignment: Alignment.center,
+                                decoration: Styles.getActivateButtonDecoration(
+                                  20.r,
+                                ),
+                                child: Text(
+                                  StringName.memberAgreementDialogConfirm,
+                                  style: Styles.getTextStyleWhiteW500(16.sp),
+                                ),
+                              ),
+                            ),
+                          ],
+                        ),
+                      ],
+                    ),
+                  ),
+                  Positioned(
+                    right: 14.w,
+                    top: 14.h,
+                    child: GestureDetector(
+                      onTap: () {
+                        SmartDialog.dismiss();
+                      },
+                      child: Assets.images.iconCustomDialogClose.image(
+                        width: 24.w,
+                        height: 24.h,
+                      ),
+                    ),
+                  ),
+                ],
+              ),
+            ),
+          ],
+        );
+      },
+    );
+  }
+}

+ 83 - 0
lib/dialog/wechat_qr_code_dialog.dart

@@ -0,0 +1,83 @@
+import 'dart:ui';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:qr_flutter/qr_flutter.dart';
+
+import '../resource/assets.gen.dart';
+import '../resource/colors.gen.dart';
+import '../resource/string.gen.dart';
+
+class WechatQrCodeDialog {
+  static const String _tag = 'WechatQrCodeDialog';
+
+  static void show(
+      {required String qrCodeUrl,
+      VoidCallback? onDismiss,
+      VoidCallback? onCloseCallback,
+      VoidCallback? loadSuccessCallback}) {
+    SmartDialog.show(
+        tag: _tag,
+        onDismiss: onDismiss,
+        backDismiss: false,
+        builder: (_) {
+          return WechatQrCodeView(
+              qrCodeUrl: qrCodeUrl, onCloseCallback: onCloseCallback);
+        },
+        clickMaskDismiss: false);
+    loadSuccessCallback?.call();
+  }
+
+  static void dismiss() {
+    SmartDialog.dismiss(tag: _tag);
+  }
+}
+
+class WechatQrCodeView extends Dialog {
+  final String qrCodeUrl;
+  VoidCallback? onCloseCallback;
+
+  WechatQrCodeView({super.key, required this.qrCodeUrl, this.onCloseCallback});
+
+  @override
+  Widget build(BuildContext context) {
+    return IntrinsicHeight(
+      child: Column(
+        children: [
+          Container(
+            padding: EdgeInsets.all(26.w),
+            decoration: BoxDecoration(
+              color: Colors.white,
+              borderRadius: BorderRadius.circular(10.w),
+            ),
+            child: Column(
+              children: [
+                SizedBox(
+                    width: 220.w,
+                    height: 220.w,
+                    child: QrImageView(
+                      data: qrCodeUrl,
+                      version: QrVersions.auto,
+                      size: 220.w,
+                      backgroundColor: Colors.white,
+                    )),
+                SizedBox(height: 16.w),
+                Text(StringName.wechatPayQrCodeTips,
+                    style: TextStyle(fontSize: 14.sp, color: ColorName.black90))
+              ],
+            ),
+          ),
+          SizedBox(height: 20.w),
+          GestureDetector(
+              onTap: () {
+                onCloseCallback?.call();
+                WechatQrCodeDialog.dismiss();
+              },
+              child: Assets.images.iconMemberRetainClose
+                  .image(width: 32.w, height: 32.w))
+        ],
+      ),
+    );
+  }
+}

+ 10 - 1
lib/module/login/login_page.dart

@@ -1,13 +1,22 @@
+
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/src/widgets/framework.dart';
 import 'package:keyboard/base/base_page.dart';
 import 'package:keyboard/module/login/login_controller.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/router/app_pages.dart';
 
 class LoginPage extends BasePage<LoginController> {
   const LoginPage({super.key});
 
+  static start() {
+    Get.toNamed(RoutePath.login);
+  }
+
   @override
   Widget buildBody(BuildContext context) {
-    return Container();
+    return Container(
+      child:  Text('Login Page')
+    );
   }
 }

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

@@ -11,6 +11,7 @@ import '../../data/repository/account_repository.dart';
 import '../../resource/string.gen.dart';
 import '../../utils/http_handler.dart';
 import '../../utils/toast_util.dart';
+import '../store/store_page.dart';
 
 @injectable
 class MineController extends BaseController {
@@ -32,6 +33,7 @@ class MineController extends BaseController {
 
   clickVip() {
     debugPrint('clickVip');
+    StorePage.start();
   }
 
   clickOnlineCustomerService() {

+ 13 - 0
lib/module/store/store_banner_bean.dart

@@ -0,0 +1,13 @@
+import '../../resource/assets.gen.dart';
+
+class StoreBannerBean{
+  final AssetGenImage banner; // 轮播图
+  final AssetGenImage indicatorImg; // 选中时指示器图标
+  final String unSelectedDesc; // 指示器文本
+
+  StoreBannerBean({
+    required this.banner,
+    required this.indicatorImg,
+    required this.unSelectedDesc,
+  });
+}

+ 431 - 0
lib/module/store/store_controller.dart

@@ -0,0 +1,431 @@
+import 'package:agile_pay/flutter_pay.dart';
+import 'package:carousel_slider/carousel_controller.dart';
+import 'package:carousel_slider/carousel_options.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/base/base_controller.dart';
+import 'package:keyboard/data/bean/pay_way_info.dart';
+import 'package:keyboard/data/bean/goods_info.dart';
+import 'package:keyboard/data/repository/account_repository.dart';
+import 'package:keyboard/data/repository/store_repository.dart';
+import 'package:keyboard/module/store/store_banner_bean.dart';
+import 'package:keyboard/module/store/store_user_reviews_bean.dart';
+import 'package:keyboard/utils/async_util.dart';
+import 'package:keyboard/utils/atmob_log.dart';
+
+import '../../data/api/response/user_info_response.dart';
+import '../../data/bean/member_info.dart';
+import '../../data/bean/wechat_payment_sign_bean.dart';
+import '../../data/consts/error_code.dart';
+import '../../data/consts/payment_type.dart';
+import '../../dialog/alipay_qr_code_dialog.dart';
+import '../../dialog/loading_dialog.dart';
+import '../../dialog/member_agreement_dialog.dart';
+import '../../dialog/wechat_qr_code_dialog.dart';
+import '../../resource/assets.gen.dart';
+import '../../resource/string.gen.dart';
+import '../../utils/error_handler.dart';
+import '../../utils/http_handler.dart';
+import '../../utils/payment_status_manager.dart';
+import '../../utils/toast_util.dart';
+import '../login/login_page.dart';
+
+@injectable
+class StoreController extends BaseController implements PaymentStatusCallback {
+  final tag = 'StoreController';
+
+  final StoreRepository storeRepository;
+
+  final AccountRepository accountRepository;
+
+  final PaymentStatusManager paymentStatusManager;
+
+  final RxList<GoodsInfo> goodsInfoList = <GoodsInfo>[].obs;
+
+  final Rxn<GoodsInfo> _selectedGoodsInfoItem = Rxn<GoodsInfo>();
+
+  GoodsInfo? get selectedGoodsInfoItem => _selectedGoodsInfoItem.value;
+
+  final Rxn<PayWayInfo> _selectedPayWay = Rxn<PayWayInfo>();
+
+  PayWayInfo? get selectedPayWay => _selectedPayWay.value;
+
+  final RxList<PayWayInfo> payWayList = <PayWayInfo>[].obs;
+
+  CancelableFuture? _storeDataFuture;
+
+  String? get phone => accountRepository.loginPhoneNum.value;
+
+  bool get isLogin => accountRepository.isLogin.value;
+
+  final isAgree = false.obs;
+
+  MemberInfo? get memberStatusInfo => accountRepository.memberStatusInfo.value;
+
+  UserInfoResponse? get userInfo => accountRepository.userInfo.value;
+
+  final List<StoreBannerBean> bannerList = [
+    StoreBannerBean(
+      banner: Assets.images.iconStoreBanner1,
+      indicatorImg: Assets.images.iconStoreIndicator1,
+      unSelectedDesc: "助攻追爱",
+    ),
+    StoreBannerBean(
+      banner: Assets.images.iconStoreBanner2,
+      indicatorImg: Assets.images.iconStoreIndicator2,
+      unSelectedDesc: "定制人设",
+    ),
+    StoreBannerBean(
+      banner: Assets.images.iconStoreBanner3,
+      indicatorImg: Assets.images.iconStoreIndicator3,
+      unSelectedDesc: "解锁人设",
+    ),
+  ];
+
+  final List<StoreUserReviewsBean> userReviewsList = [
+    StoreUserReviewsBean(
+      avatar: Assets.images.iconStoreUserReview1,
+      userName: "执****念",
+      userReviews: "恋爱键盘超绝!多样人设主题,快捷情话输入,敲出专属浪漫,爱意满屏。1",
+    ),
+    StoreUserReviewsBean(
+      avatar: Assets.images.iconStoreUserReview2,
+      userName: "过****人",
+      userReviews: "恋爱键盘超绝!多样人设主题,快捷情话输入,敲出专属浪漫,爱意满屏。2",
+    ),
+    StoreUserReviewsBean(
+      avatar: Assets.images.iconStoreUserReview3,
+      userName: "不当****solo",
+      userReviews: "恋爱键盘超绝!多样人设主题,快捷情话输入,敲出专属浪漫,爱意满屏。3",
+    ),
+    StoreUserReviewsBean(
+      avatar: Assets.images.iconStoreUserReview4,
+      userName: "用户*****1234",
+      userReviews: "很好,很满意",
+    ),
+  ];
+
+  final CarouselSliderController carouselSliderController =
+      CarouselSliderController();
+
+  final RxInt _currentBanner = 0.obs;
+
+  int get currentBannerIndex => _currentBanner.value;
+
+  StoreController(
+    this.storeRepository,
+    this.accountRepository,
+    this.paymentStatusManager,
+  );
+
+  onBannerChanged(int index, CarouselPageChangedReason reason) {
+    _currentBanner.value = index;
+  }
+
+  void refreshStoreData() {
+    _storeDataFuture?.cancel();
+    _storeDataFuture = AsyncUtil.retryWithExponentialBackoff(
+      () => _requestGoodsInfoList(),
+      4,
+    );
+    _storeDataFuture?.catchError((error) {
+      ErrorHandler.toastError(error);
+    });
+  }
+
+  Future<void> _requestGoodsInfoList() async {
+    try {
+      final response = await storeRepository.getGoodsInfoList();
+      goodsInfoList.clear();
+      payWayList.clear();
+      _selectedGoodsInfoItem.value = null;
+      if (response.goodsInfoList?.isNotEmpty == true) {
+        goodsInfoList.addAll(response.goodsInfoList!);
+        _selectedGoodsInfoItem.value = goodsInfoList.first;
+      }
+      if (response.payInfoList?.isNotEmpty == true) {
+        payWayList.addAll(response.payInfoList!);
+        _selectedPayWay.value = payWayList.first;
+      }
+    } catch (e) {
+      AtmobLog.e(tag, e.toString());
+    }
+  }
+
+  void onGoodsItemClick(GoodsInfo goodsInfo) {
+    _selectedGoodsInfoItem.value = goodsInfo;
+    print('item: ${goodsInfo.toJson()}');
+  }
+
+  void clickPayWaySwitch() {
+    if (payWayList.isNotEmpty) {
+      int currentIndex = payWayList.indexOf(
+        _selectedPayWay.value ?? payWayList.first,
+      );
+      int nextIndex = (currentIndex + 1) % payWayList.length;
+      _selectedPayWay.value = payWayList[nextIndex];
+    }
+  }
+
+  void clickBack() {
+    Get.back();
+  }
+
+  void clickPayNow() {
+    if (selectedGoodsInfoItem == null) {
+      ToastUtil.show(StringName.memberPleaseChoiceGoods);
+      return;
+    }
+    if (selectedPayWay == null) {
+      ToastUtil.show(StringName.memberPleaseChoicePayment);
+      return;
+    }
+    if (!isAgree.value) {
+      MemberAgreementDialog.show(
+        btnConfirm: () {
+          isAgree.value = true;
+          clickPayNow();
+        },
+      );
+      return;
+    }
+    AtmobLog.d(tag, 'clickPayNow: ${selectedGoodsInfoItem!.toJson()}');
+    final buyGoods = selectedGoodsInfoItem!;
+    final buyPayWay = selectedPayWay!;
+
+    int goodsId = buyGoods.id;
+    int payPlatform = buyPayWay.payPlatform;
+    int payMethod = buyPayWay.payMethod;
+    int payWayType = getPayWayType(
+      payMethod: payMethod,
+      payPlatform: payPlatform,
+    );
+
+    LoadingDialog.show(StringName.payLoading);
+    storeRepository
+        .submitAndRequestPay(
+          goodsId: goodsId,
+          payPlatform: payPlatform,
+          payMethod: payMethod,
+        )
+        .then((response) {
+          if (payWayType == PayWayType.paymentWayWechat) {
+            _onWeChatPay(
+              response.outTradeNo,
+              response.wechatPayPrepayJson!,
+              payMethod,
+              buyGoods,
+              buyPayWay,
+            );
+          } else if (payWayType == PayWayType.paymentWayWechatScan) {
+            _onWechatScanPay(
+              response.outTradeNo,
+              response.wechatPayQrcodeUrl!,
+              buyPayWay,
+              buyGoods,
+            );
+          } else if (payWayType == PayWayType.paymentWayAlipay) {
+            _onAliPay(
+              response.outTradeNo,
+              response.alipayOrderString!,
+              payMethod,
+              buyGoods,
+              buyPayWay,
+            );
+          } else if (payWayType == PayWayType.paymentWayAlipayScan) {
+            _onAliScanPay(
+              response.outTradeNo,
+              response.alipayQrcodeHtml!,
+              buyPayWay,
+              buyGoods,
+            );
+          } else {
+            ToastUtil.show(StringName.payNotSupport);
+          }
+        })
+        .catchError((error) {
+          if (error is ServerErrorException) {
+            if (error.code == ErrorCode.payOrderError) {
+              refreshStoreData();
+              ToastUtil.show(error.message);
+            } else if (error.code == ErrorCode.noLoginError) {
+              ToastUtil.show(StringName.accountNoLogin);
+              LoginPage.start();
+            } else {
+              ToastUtil.show(error.message);
+            }
+          } else {
+            ToastUtil.show(StringName.memberPaymentFailed);
+          }
+        })
+        .whenComplete(() {
+          LoadingDialog.hide();
+        });
+  }
+
+  void _onAliScanPay(
+    String outTradeNo,
+    String qrHtml,
+    PayWayInfo payWayInfo,
+    GoodsInfo goodsInfo,
+  ) {
+    AlipayQrCodeDialog.show(
+      qrCodeHtml: qrHtml,
+      loadSuccessCallback: () {
+        checkPaymentStatus(outTradeNo, payWayInfo, goodsInfo);
+      },
+      onCloseCallback: () async {
+        //关闭后再持续查询几秒
+        CustomLoadingDialog.show();
+        await Future.delayed(Duration(seconds: 4));
+        paymentStatusManager.removePollingSubscription(outTradeNo);
+        CustomLoadingDialog.hide();
+      },
+    );
+  }
+
+  void _onAliPay(
+    String outTradeNo,
+    String payJson,
+    int payMethod,
+    GoodsInfo buyGoods,
+    PayWayInfo buyPayWay,
+  ) {
+    final payInfo = AliPayInfo(payJson);
+    requestSdkPay(payInfo, outTradeNo, payMethod, buyGoods, buyPayWay);
+  }
+
+  void _onWeChatPay(
+    String outTradeNo,
+    String payJson,
+    int payMethod,
+    GoodsInfo buyGoods,
+    PayWayInfo buyPayWay,
+  ) {
+    final bean = WechatPaymentSignBean.stringToBean(payJson);
+    final payInfo = WechatPayInfo(
+      appId: bean.appId,
+      partnerId: bean.partnerId,
+      prepayId: bean.prepayId,
+      package: bean.package,
+      noncestr: bean.nonceStr,
+      timestamp: bean.timeStamp,
+      sign: bean.sign,
+    );
+    requestSdkPay(payInfo, outTradeNo, payMethod, buyGoods, buyPayWay);
+  }
+
+  void _onWechatScanPay(
+    String outTradeNo,
+    String qrCodeUrl,
+    PayWayInfo payWayInfo,
+    GoodsInfo goodsInfo,
+  ) {
+    WechatQrCodeDialog.show(
+      qrCodeUrl: qrCodeUrl,
+      loadSuccessCallback: () {
+        checkPaymentStatus(outTradeNo, payWayInfo, goodsInfo);
+      },
+      onCloseCallback: () async {
+        //关闭后再持续查询几秒
+        CustomLoadingDialog.show();
+        await Future.delayed(Duration(seconds: 4));
+        paymentStatusManager.removePollingSubscription(outTradeNo);
+        CustomLoadingDialog.hide();
+      },
+    );
+  }
+
+  void checkPaymentStatus(
+    String orderNo,
+    PayWayInfo payWayInfo,
+    GoodsInfo goodsInfo, {
+    String? receiptData,
+  }) {
+    paymentStatusManager.registerPaymentSuccessCallback(orderNo, this);
+    paymentStatusManager.checkPaymentStatus(
+      orderNo,
+      payWayInfo,
+      goodsInfo,
+      receiptData: receiptData,
+    );
+  }
+
+  void requestSdkPay(
+    dynamic payInfo,
+    String outTradeNo,
+    int payMethod,
+    GoodsInfo buyGoods,
+    PayWayInfo buyPayWay,
+  ) {
+    AgilePay.startPay(
+      payInfo,
+      success: (String? result) {
+        LoadingDialog.show(StringName.payQuerypayState);
+        checkPaymentStatus(
+          outTradeNo,
+          buyPayWay,
+          buyGoods,
+          receiptData: result,
+        );
+      },
+      payError: (int error, String? errorMessage) {
+        debugPrint('zk---payError: $error, $errorMessage');
+        errorPayToast(error);
+        errorEventReport(payMethod);
+      },
+      error: (int errno, String? error) {
+        debugPrint('zk---error: $errno, $error');
+        errorPayToast(errno);
+        errorEventReport(payMethod);
+      },
+    );
+  }
+
+  void errorEventReport(int payMethod) {
+    if (payMethod == PayMethod.wechat) {
+      // EventHandler.report();
+    } else if (payMethod == PayMethod.alipay) {
+      // EventHandler.report();
+    } else if (payMethod == PayMethod.apple) {
+      // EventHandler.report();
+    }
+  }
+
+  void errorPayToast(int errno) {
+    if (errno == AgilePayCode.payCodeNotSupport) {
+      ToastUtil.show(StringName.payNotSupport);
+    } else if (errno == AgilePayCode.payCodeCancelError) {
+      ToastUtil.show(StringName.payUserCancel);
+    } else if (errno == AgilePayCode.payCodeWxEnvError) {
+      ToastUtil.show(StringName.payWxEvnError);
+    } else if (errno == AgilePayCode.payCodeNotConnectStore) {
+      ToastUtil.show(StringName.payNotConnectStore);
+    } else {
+      ToastUtil.show(StringName.payError);
+    }
+  }
+
+  @override
+  void onReady() {
+    super.onReady();
+    refreshStoreData();
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    _storeDataFuture?.cancel();
+    paymentStatusManager.unregisterPaymentSuccessCallback(this);
+  }
+
+  @override
+  void onPaymentSuccess(
+    String orderNo,
+    PayWayInfo payWayInfo,
+    GoodsInfo goodsInfo,
+  ) {
+    // showPaymentSuccessDialog(onConfirm: back, onCancel: back);
+  }
+}

+ 860 - 0
lib/module/store/store_page.dart

@@ -0,0 +1,860 @@
+import 'package:carousel_slider/carousel_slider.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/base/base_page.dart';
+import 'package:keyboard/data/consts/payment_type.dart';
+import 'package:keyboard/data/consts/web_url.dart';
+import 'package:keyboard/module/store/store_controller.dart';
+import 'package:keyboard/module/store/store_user_reviews_bean.dart';
+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 '../../utils/styles.dart';
+import '../../widget/click_text_span.dart';
+
+class StorePage extends BasePage<StoreController> {
+  const StorePage({super.key});
+
+  static start() {
+    Get.toNamed(RoutePath.store);
+  }
+
+  @override
+  backgroundColor() => const Color(0xFFFFF8D4);
+
+  @override
+  bool immersive() {
+    return true;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Stack(
+      children: [
+        SingleChildScrollView(
+          child: Column(
+            children: [
+              _buildBanner(context),
+              SizedBox(height: 12.h),
+              _buildGoodsCard(),
+              _buildVipDesc(),
+              SizedBox(height: 32.h),
+              _buildUserReviews(),
+              SizedBox(height: 20.h),
+              _buildUserNotice(),
+            ],
+          ),
+        ),
+        Positioned(top: 0, left: 0, right: 0, child: _buildTitle()),
+        Positioned(bottom: 0, left: 0, right: 0, child: _buildBuyButtonCard()),
+      ],
+    );
+  }
+
+  _buildTitle() {
+    return SafeArea(
+      child: Container(
+        alignment: Alignment.centerLeft,
+        padding: EdgeInsets.only(top: 16.h, left: 16.w, right: 16.w),
+        child: Row(
+          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+          children: [
+            GestureDetector(
+              onTap: controller.clickBack,
+              child: Assets.images.iconStoreBack.image(
+                width: 32.w,
+                height: 32.w,
+              ),
+            ),
+            Obx(() {
+              return SizedBox(
+                height: 32.h,
+                child: Row(
+                  mainAxisSize: MainAxisSize.min,
+                  mainAxisAlignment: MainAxisAlignment.center,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    // 会员状态信息
+                    _buildMemberInfo(),
+                    SizedBox(width: 8.w),
+                    controller.isLogin
+                        ? Assets.images.iconMineUserLogged.image(
+                          width: 28.w,
+                          height: 28.w,
+                        )
+                        : Assets.images.iconMineUserNoLogin.image(
+                          width: 28.w,
+                          height: 28.w,
+                        ),
+                  ],
+                ),
+              );
+            }),
+          ],
+        ),
+      ),
+    );
+  }
+
+  // 会员信息
+  Widget _buildMemberInfo() {
+    return Container(
+      height: 32.h,
+      alignment: Alignment.center,
+      padding: EdgeInsets.symmetric(horizontal: 12.w),
+      decoration: BoxDecoration(
+        color: Colors.black.withAlpha(77),
+        borderRadius: BorderRadius.circular(21.r),
+      ),
+      child: Text.rich(
+        TextSpan(children: _getMemberStatusText()),
+        textAlign: TextAlign.right,
+      ),
+    );
+  }
+
+  // 会员状态文字逻辑提取
+  List<TextSpan> _getMemberStatusText() {
+    if (!controller.isLogin) {
+      return [
+        TextSpan(
+          text: StringName.memberNoLogged,
+          style: _vipTextStyle(isHighlight: true),
+        ),
+        TextSpan(text: StringName.memberCardNoVipDesc, style: _vipTextStyle()),
+      ];
+    }
+
+    final isMember = controller.memberStatusInfo?.isMember ?? false;
+    final isPermanent = controller.memberStatusInfo?.permanent ?? false;
+    final username = controller.userInfo?.name ?? '';
+
+    if (isMember) {
+      if (isPermanent) {
+        return [
+          TextSpan(
+            text: StringName.memberCardPermanentVipDesc1,
+            style: _vipTextStyle(),
+          ),
+          TextSpan(
+            text: StringName.memberCardPermanentVipDesc2,
+            style: _vipTextStyle(isHighlight: true),
+          ),
+        ];
+      } else {
+        return [
+          TextSpan(text: StringName.memberVipDesc, style: _vipTextStyle()),
+          TextSpan(
+            text: DateUtil.fromMillisecondsSinceEpoch(
+              'yyyy.MM.dd',
+              controller.memberStatusInfo?.endTimestamp ?? 0,
+            ),
+            style: _vipTextStyle(isHighlight: true),
+          ),
+        ];
+      }
+    }
+
+    // 登录但不是会员
+    return [
+      TextSpan(text: username, style: _vipTextStyle(isHighlight: true)),
+      TextSpan(text: StringName.memberCardNoVipDesc, style: _vipTextStyle()),
+    ];
+  }
+
+  // 统一的会员状态文本样式
+  TextStyle _vipTextStyle({bool isHighlight = false}) {
+    return TextStyle(
+      color: isHighlight ? const Color(0xFFFFD273) : Colors.white,
+      fontSize: 12.sp,
+      fontWeight: FontWeight.w400,
+    );
+  }
+
+  Widget _buildGoodsCard() {
+
+    return Container(
+      margin: EdgeInsets.symmetric(horizontal: 16.w),
+      padding: EdgeInsets.only(
+        top: 16.h,
+        left: 16.w,
+        right: 16.w,
+        bottom: 24.h,
+      ),
+      decoration: BoxDecoration(
+        color: Colors.white,
+        borderRadius: BorderRadius.only(
+          topLeft: Radius.circular(16.r),
+          topRight: Radius.circular(16.r),
+        ),
+      ),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        mainAxisAlignment: MainAxisAlignment.start,
+        children: [
+          Assets.images.iconGoodsInfoTitle.image(
+            width: 115.w,
+            fit: BoxFit.cover,
+          ),
+          SizedBox(height: 16.h),
+          Obx(() {
+            return Column(
+              children:
+                  controller.goodsInfoList.map((item) {
+                    return Obx(() {
+                      return GestureDetector(
+                        onTap: () => controller.onGoodsItemClick(item),
+                        child: _buildGoodsItem(
+                          item,
+                          controller.selectedGoodsInfoItem?.id == item.id,
+                        ),
+                      );
+                    });
+                  }).toList(),
+            );
+          }),
+          _buildPayWayCard(),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildPayWayCard() {
+    return GestureDetector(
+      onTap: () => controller.clickPayWaySwitch(),
+      child: Container(
+        height: 36.h,
+        padding: EdgeInsets.symmetric(horizontal: 10.w),
+        decoration: ShapeDecoration(
+          shape: RoundedRectangleBorder(
+            side: BorderSide(width: 1, color: const Color(0xFFECEBE0)),
+            borderRadius: BorderRadius.circular(10.r),
+          ),
+        ),
+        child: Row(
+          children: [
+            Text(
+              StringName.storePayWay,
+              style: Styles.getTextStyleBlack204W400(14.sp),
+            ),
+            const Spacer(),
+            Obx(() {
+              if (controller.selectedPayWay == null) {
+                return SizedBox.shrink();
+              }
+              return Row(
+                children: [
+                  Image.asset(
+                    getPaymentIconPath(
+                      payMethod: controller.selectedPayWay!.payMethod,
+                      payPlatform: controller.selectedPayWay!.payPlatform,
+                    ),
+                    width: 20.w,
+                    height: 20.w,
+                  ),
+                  SizedBox(width: 4.w),
+                  Text(
+                    controller.selectedPayWay?.title ?? '',
+                    style: Styles.getTextStyleBlack204W400(14.sp),
+                  ),
+                  SizedBox(width: 6.w),
+                  Assets.images.iconStoreSwitchPay.image(
+                    width: 20.w,
+                    height: 20.w,
+                    fit: BoxFit.fill,
+                  ),
+                ],
+              );
+            }),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget _buildGoodsItem(item, bool isSelected) {
+    return Container(
+      margin: EdgeInsets.only(bottom: 8.h),
+      width: 296.w,
+      height: 70.h,
+      decoration: ShapeDecoration(
+        gradient: LinearGradient(
+          begin: Alignment(0.77, -0.00),
+          end: Alignment(0.77, 1.00),
+          colors:
+              isSelected
+                  ? [const Color(0xFFFF9416), const Color(0xFFFF7813)]
+                  : [const Color(0xFFFEE057), const Color(0xFFFFC400)],
+        ),
+        shape: RoundedRectangleBorder(
+          borderRadius: BorderRadius.circular(10.r),
+        ),
+      ),
+      child: Row(
+        children: [
+          Container(
+            width: 212.w,
+            height: 70.h,
+            decoration: ShapeDecoration(
+              gradient:
+                  isSelected
+                      ? LinearGradient(
+                        begin: Alignment(-0.06, 0.50),
+                        end: Alignment(1.14, 0.50),
+                        colors: [
+                          const Color(0xFFFFF895),
+                          const Color(0xFFFFE941),
+                        ],
+                      )
+                      : null,
+              color: isSelected ? null : const Color(0xFFFFFDEE),
+              shape: RoundedRectangleBorder(
+                side: BorderSide(width: 1, color: const Color(0xFFFEE86B)),
+                borderRadius: BorderRadius.circular(isSelected ? 8 : 10.r),
+              ),
+            ),
+            child: Stack(
+              children: [
+                if (isSelected)
+                  IgnorePointer(
+                    child: Assets.images.bgStoreSelectedItem.image(
+                      width: 212.w,
+                      height: 70.h,
+                      fit: BoxFit.fill,
+                    ),
+                  ),
+                Padding(
+                  padding: EdgeInsets.only(left: 16.w),
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    mainAxisAlignment: MainAxisAlignment.center,
+                    children: [
+                      Row(
+                        children: [
+                          RichText(
+                            text: TextSpan(
+                              children: [
+                                TextSpan(
+                                  text: '¥',
+                                  style: Styles.getTextStyleFF663300W700(14.sp),
+                                ),
+                                TextSpan(
+                                  text: item.priceDescNumber,
+                                  style: Styles.getTextStyleFF663300W700(18.sp),
+                                ),
+                                TextSpan(
+                                  text: item.priceDescUnit,
+                                  style: Styles.getTextStyleFF663300W400(13.sp),
+                                ),
+                              ],
+                            ),
+                          ),
+                          if (item.mostDesc?.isNotEmpty == true)
+                            Container(
+                              padding: EdgeInsets.only(
+                                left: 16.w,
+                                top: 2.h,
+                                bottom: 2.h,
+                              ),
+                              decoration: BoxDecoration(
+                                image: DecorationImage(
+                                  image: Assets.images.iconStoreMost.provider(),
+                                  fit: BoxFit.cover,
+                                  alignment: Alignment.bottomLeft,
+                                ),
+                              ),
+                              child: Text(
+                                item.mostDesc!,
+                                style: TextStyle(
+                                  color: Colors.white,
+                                  fontSize: 12.sp,
+                                  fontWeight: FontWeight.w500,
+                                  letterSpacing: -0.60,
+                                ),
+                              ),
+                            ),
+                        ],
+                      ),
+                      Text(
+                        item.description!,
+                        style: Styles.getTextStyle99673300W400(12.sp),
+                      ),
+                    ],
+                  ),
+                ),
+              ],
+            ),
+          ),
+          // 右侧
+          Expanded(
+            child: Column(
+              mainAxisAlignment: MainAxisAlignment.center,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                Text(
+                  item.name,
+                  style:
+                      isSelected
+                          ? Styles.getTextStyleFFECBBW500(15.sp)
+                          : Styles.getTextStyleFF663300W500(15.sp),
+                ),
+                Container(
+                  padding: EdgeInsets.symmetric(horizontal: 8.w),
+                  decoration: ShapeDecoration(
+                    color: isSelected ? const Color(0xFFFFECBB) : null,
+                    gradient:
+                        isSelected
+                            ? null
+                            : LinearGradient(
+                              begin: Alignment(0.77, -0.00),
+                              end: Alignment(0.77, 1.00),
+                              colors: [
+                                const Color(0xFFFF9416),
+                                const Color(0xFFFF7813),
+                              ],
+                            ),
+                    shape: RoundedRectangleBorder(
+                      borderRadius: BorderRadius.circular(
+                        isSelected ? 17.r : 10.r,
+                      ),
+                    ),
+                  ),
+                  child: Text(
+                    '¥${item.amountText}',
+                    textAlign: TextAlign.center,
+                    style:
+                        isSelected
+                            ? Styles.getTextStyleFF7F14W500(12.sp)
+                            : Styles.getTextStyleWhiteW500(12.sp),
+                  ),
+                ),
+              ],
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildVipDesc() {
+    return Container(
+      alignment: Alignment.centerLeft,
+      margin: EdgeInsets.symmetric(horizontal: 16.w),
+      padding: EdgeInsets.symmetric(horizontal: 16.w),
+      width: double.infinity,
+      height: 36.h,
+      decoration: ShapeDecoration(
+        gradient: LinearGradient(
+          begin: Alignment(0.00, 0.50),
+          end: Alignment(1.00, 0.50),
+          colors: [const Color(0x7FFFF3A3), const Color(0x21FFF3A3)],
+        ),
+        shape: RoundedRectangleBorder(
+          borderRadius: BorderRadius.only(
+            bottomLeft: Radius.circular(20.r),
+            bottomRight: Radius.circular(20.r),
+          ),
+        ),
+        shadows: [
+          BoxShadow(
+            color: Colors.black.withAlpha(10),
+            blurRadius: 10.r,
+            spreadRadius: 0.r,
+            offset: Offset(0, 8.r),
+          ),
+        ],
+      ),
+      child: Text("*选中商品时展示文案", style: Styles.getTextStyle99673300W400(12.sp)),
+    );
+  }
+
+  // 轮播图
+  Widget _buildBanner(BuildContext context) {
+    return SizedBox(
+      width: double.infinity,
+      child: Stack(
+        children: [
+          CarouselSlider(
+            carouselController: controller.carouselSliderController,
+            options: CarouselOptions(
+              height: 280.h,
+              viewportFraction: 1,
+              initialPage: 0,
+              enableInfiniteScroll: true,
+              reverse: false,
+              autoPlay: true,
+              autoPlayInterval: const Duration(seconds: 3),
+              autoPlayAnimationDuration: const Duration(milliseconds: 800),
+              autoPlayCurve: Curves.fastOutSlowIn,
+              scrollDirection: Axis.horizontal,
+              onPageChanged: (index, reason) {
+                controller.onBannerChanged(index, reason);
+              },
+            ),
+
+            items:
+                controller.bannerList.map((item) {
+                  return item.banner.image(
+                    width: double.infinity,
+                    fit: BoxFit.cover,
+                  );
+                }).toList(),
+          ),
+          Positioned(bottom: 0, left: 0, right: 0, child: _buildIndicator()),
+        ],
+      ),
+    );
+  }
+
+  // 指示器
+  _buildIndicator() {
+    return Container(
+      height: 36.h,
+      margin: EdgeInsets.symmetric(horizontal: 16.w),
+      decoration: ShapeDecoration(
+        color: const Color(0xE5121212),
+        shape: RoundedRectangleBorder(
+          borderRadius: BorderRadius.circular(21.r),
+        ),
+      ),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.spaceAround,
+        children:
+            controller.bannerList.asMap().entries.map((entry) {
+              return Obx(() {
+                final isSelectedBanner =
+                    controller.currentBannerIndex == entry.key;
+                return Row(
+                  mainAxisAlignment: MainAxisAlignment.spaceAround,
+                  children: [
+                    GestureDetector(
+                      onTap:
+                          () => controller.carouselSliderController
+                              .animateToPage(entry.key),
+                      child: SizedBox(
+                        width: 100.w,
+                        child: Stack(
+                          alignment: Alignment.center,
+                          clipBehavior: Clip.none,
+                          children: [
+                            if (isSelectedBanner)
+                              Positioned(
+                                top: -8.h,
+                                child: controller
+                                    .bannerList[entry.key]
+                                    .indicatorImg
+                                    .image(
+                                      width: 100.w,
+                                      height: 40.h,
+                                      fit: BoxFit.fill,
+                                    ),
+                              )
+                            else
+                              Text(
+                                controller.bannerList[entry.key].unSelectedDesc,
+                                style: Styles.getTextStyleWhiteW400(14.sp),
+                              ),
+                          ],
+                        ),
+                      ),
+                    ),
+                    if (entry.key != controller.bannerList.length - 1)
+                      Padding(
+                        padding: EdgeInsets.only(left: 4.w),
+                        child: Assets.images.iconStoreDivider.image(
+                          width: 2.w,
+                          height: 17.h,
+                          fit: BoxFit.fill,
+                        ),
+                      ),
+                  ],
+                );
+              });
+            }).toList(),
+      ),
+    );
+  }
+
+  Widget _buildUserReviews() {
+    return Container(
+      width: double.infinity,
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.center,
+        children: [
+          Assets.images.iconStoreUserReviewsTitle.image(
+            width: 240.w,
+            fit: BoxFit.cover,
+          ),
+          SizedBox(height: 16.h),
+          Container(
+            width: double.infinity,
+            decoration: BoxDecoration(
+              borderRadius: BorderRadius.circular(19.r),
+              gradient: LinearGradient(
+                begin: Alignment.topCenter,
+                end: Alignment.bottomCenter,
+                colors: [Colors.white, Color(0xfffff8d4)],
+              ),
+            ),
+            child: Column(
+              children: [
+                SizedBox(height: 20.h),
+                Container(
+                  width: 326.w,
+                  decoration: BoxDecoration(
+                    borderRadius: BorderRadius.circular(19.r),
+                    image: DecorationImage(
+                      alignment: Alignment.topCenter,
+                      image: Assets.images.bgStoreUserReviews.provider(),
+                      fit: BoxFit.contain,
+                    ),
+                  ),
+                  child: Column(
+                    children: [
+                      Container(
+                        padding: EdgeInsets.only(left: 16.w),
+                        alignment: Alignment.centerLeft,
+                        height: 39.h,
+                        child: Assets.images.iconStoreUserReviewsLogo.image(
+                          width: 92.w,
+                          height: 24.h,
+                        ),
+                      ),
+                      Container(
+                        width: 326.w,
+                        decoration: ShapeDecoration(
+                          color: Colors.white,
+                          shape: RoundedRectangleBorder(
+                            borderRadius: BorderRadius.circular(16.r),
+                          ),
+                          shadows: [
+                            BoxShadow(
+                              color: Colors.black.withAlpha(10),
+                              blurRadius: 10.r,
+                              spreadRadius: 0.r,
+                              offset: Offset(0, 5.r),
+                            ),
+                          ],
+                        ),
+                        child: Column(
+                          children:
+                              controller.userReviewsList.map((item) {
+                                return _buildReviewsItem(item);
+                              }).toList(),
+                        ),
+                      ),
+                    ],
+                  ),
+                ),
+              ],
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildReviewsItem(StoreUserReviewsBean item) {
+    return Container(
+      padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 12.h),
+
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Row(
+            children: [
+              item.avatar.image(width: 28.w, height: 28.h, fit: BoxFit.cover),
+              SizedBox(width: 10.w),
+              Text(
+                item.userName,
+                style: Styles.getTextStyleBlack204W500(14.sp),
+              ),
+              SizedBox(width: 6.w),
+              Assets.images.iconStorePermanentMember.image(
+                width: 70.w,
+                height: 20.h,
+                fit: BoxFit.cover,
+              ),
+            ],
+          ),
+          SizedBox(height: 4.h),
+          Padding(
+            padding: EdgeInsets.only(left: 38.w),
+            child: Text(
+              item.userReviews,
+              style: TextStyle(
+                color: Colors.black.withAlpha(153),
+                fontSize: 12.sp,
+                fontWeight: FontWeight.w400,
+              ),
+            ),
+          ),
+          SizedBox(height: 12.h),
+
+          if (controller.userReviewsList.indexOf(item) !=
+              controller.userReviewsList.length - 1)
+            HorizontalDashedLine(
+              width: 304.w,
+              color: const Color(0XFFEDEBE1),
+              strokeWidth: 2.h,
+              dashLength: 4.w,
+              dashSpace: 4.w,
+            ),
+        ],
+      ),
+    );
+  }
+
+  //  用户须知
+  Widget _buildUserNotice() {
+    return Container(
+      margin: EdgeInsets.symmetric(horizontal: 16.w),
+
+      decoration: BoxDecoration(borderRadius: BorderRadius.circular(16.r)),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Text('购买须知', style: Styles.getTextStyleFF663300W400(12.sp)),
+          SizedBox(height: 8.h),
+          Text(
+            "1. 本产品为互联网技术服务虚拟产品,会员购买成功立即生效,一经开通不支持退款。\n"
+            "2. 本产品为付费会员制产品,会员费用是指开通平台所有功能使用权限的基础费用。开通权限后,部分功能可能会免费(具体以实际展示情况为准)使用,部分功能需消耗一定的虚拟币(金币)方可使用。\n"
+            "3. 各种功能的金币消耗可参考虚拟币规则(金币中心)。\n"
+            "4. 开通会员后,若赠送虚拟币(金币)消耗完毕,可单独购买金币。\n"
+            "5. 根据业务情况,我方有权调整不同功能的虚拟币消耗价格,价格会在最新的金币中心展示。\n"
+            "6. 请在购买前仔细阅读《会员服务协议》和购买须知,确定您同意所有条款后继续操作。",
+            style: Styles.getTextStyle99673300W400(10.sp),
+          ),
+          SizedBox(height: 120.h),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildBuyButtonCard() {
+    return Container(
+      width: 360.w,
+      padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 11.h),
+      decoration: ShapeDecoration(
+        color: Colors.white,
+        shape: RoundedRectangleBorder(
+          borderRadius: BorderRadius.only(
+            topLeft: Radius.circular(24.r),
+            topRight: Radius.circular(24.r),
+          ),
+        ),
+        shadows: [
+          BoxShadow(
+            color: Color(0x99FFE498),
+            blurRadius: 20,
+            offset: Offset(0, 0),
+            spreadRadius: 0,
+          ),
+        ],
+      ),
+      child: Column(
+        children: [
+        GestureDetector(
+          onTap: controller.clickPayNow,
+          child:   Container(
+          width: 328.w,
+          height: 54.h,
+          decoration: ShapeDecoration(
+            gradient: LinearGradient(
+              begin: Alignment(0.60, -0.39),
+              end: Alignment(0.60, 0.95),
+              colors: [
+                const Color(0xFFF95FAC),
+                const Color(0xFFFD5E4D),
+                const Color(0xFFFD5E4D),
+                const Color(0xFFFB8A3C),
+              ],
+            ),
+            shape: RoundedRectangleBorder(
+              borderRadius: BorderRadius.circular(30.55.r),
+            ),
+          ),
+          child: Center(
+            child: Text(
+              StringName.storePayNow,
+              style: Styles.getTextStyleWhiteW500(17.sp),
+            ),
+          ),
+        ),),
+          SizedBox(height: 11.h),
+          Row(
+            mainAxisAlignment: MainAxisAlignment.center,
+            children: [
+              Obx(() {
+                return GestureDetector(
+                  behavior: HitTestBehavior.opaque,
+                  onTap: () {
+                    controller.isAgree.value = !controller.isAgree.value;
+                  },
+                  child:
+                      controller.isAgree.value
+                          ? Assets.images.iconStoreAgreePrivacy.image(
+                            width: 16.w,
+                            height: 16.w,
+                          )
+                          : SizedBox(
+                            child: Container(
+                              padding: EdgeInsets.all(1.w),
+                              width: 16.w,
+                              height: 16.w,
+                              child: Container(
+                                decoration: BoxDecoration(
+                                  shape: BoxShape.circle,
+                                  border: Border.all(
+                                    color: Colors.black.withAlpha(153),
+                                    width: 1.w,
+                                  ),
+                                ),
+                              ),
+                            ),
+                          ),
+                );
+              }),
+              Text.rich(
+                TextSpan(
+                  children: [
+                    TextSpan(
+                      text: StringName.textSpanIHaveReadAndAgree,
+                      style: TextStyle(
+                        color: Colors.black.withAlpha(153),
+                        fontSize: 10.sp,
+                        fontWeight: FontWeight.w400,
+                      ),
+                    ),
+                    ClickTextSpan(
+                      text: StringName.textSpanPrivacyPolicy,
+                      url: WebUrl.privacyPolicy,
+                    ),
+
+                    TextSpan(
+                      text: StringName.textSpanAnd,
+                      style: TextStyle(
+                        color: Colors.black.withAlpha(153),
+                        fontSize: 10.sp,
+                        fontWeight: FontWeight.w400,
+                      ),
+                    ),
+
+                    ClickTextSpan(
+                      text: StringName.textSpanServiceTerms,
+                      url: WebUrl.serviceAgreement,
+                    ),
+                  ],
+                ),
+              ),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 8 - 0
lib/module/store/store_user_reviews_bean.dart

@@ -0,0 +1,8 @@
+import '../../resource/assets.gen.dart';
+
+class StoreUserReviewsBean {
+  final String userName;
+  final String userReviews;
+  final AssetGenImage avatar;
+  StoreUserReviewsBean({required this.userName,required this.userReviews,required this.avatar});
+}

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

@@ -63,10 +63,26 @@ class $AssetsImagesGen {
   AssetGenImage get bgMineVipCard =>
       const AssetGenImage('assets/images/bg_mine_vip_card.webp');
 
+  /// File path: assets/images/bg_store_selected_item.webp
+  AssetGenImage get bgStoreSelectedItem =>
+      const AssetGenImage('assets/images/bg_store_selected_item.webp');
+
+  /// File path: assets/images/bg_store_user_reviews.webp
+  AssetGenImage get bgStoreUserReviews =>
+      const AssetGenImage('assets/images/bg_store_user_reviews.webp');
+
   /// File path: assets/images/icon_about_arrow_left.webp
   AssetGenImage get iconAboutArrowLeft =>
       const AssetGenImage('assets/images/icon_about_arrow_left.webp');
 
+  /// File path: assets/images/icon_alipay_payment.webp
+  AssetGenImage get iconAlipayPayment =>
+      const AssetGenImage('assets/images/icon_alipay_payment.webp');
+
+  /// File path: assets/images/icon_alipay_scan_payment.webp
+  AssetGenImage get iconAlipayScanPayment =>
+      const AssetGenImage('assets/images/icon_alipay_scan_payment.webp');
+
   /// File path: assets/images/icon_arrow_right.webp
   AssetGenImage get iconArrowRight =>
       const AssetGenImage('assets/images/icon_arrow_right.webp');
@@ -174,6 +190,10 @@ class $AssetsImagesGen {
   AssetGenImage get iconDialogCloseBlack =>
       const AssetGenImage('assets/images/icon_dialog_close_black.webp');
 
+  /// File path: assets/images/icon_goods_info_title.webp
+  AssetGenImage get iconGoodsInfoTitle =>
+      const AssetGenImage('assets/images/icon_goods_info_title.webp');
+
   /// File path: assets/images/icon_keyboard_manage_custom.webp
   AssetGenImage get iconKeyboardManageCustom =>
       const AssetGenImage('assets/images/icon_keyboard_manage_custom.webp');
@@ -195,6 +215,10 @@ class $AssetsImagesGen {
   AssetGenImage get iconKeyboardManageX =>
       const AssetGenImage('assets/images/icon_keyboard_manage_x.webp');
 
+  /// File path: assets/images/icon_member_retain_close.webp
+  AssetGenImage get iconMemberRetainClose =>
+      const AssetGenImage('assets/images/icon_member_retain_close.webp');
+
   /// File path: assets/images/icon_mine_about.webp
   AssetGenImage get iconMineAbout =>
       const AssetGenImage('assets/images/icon_mine_about.webp');
@@ -252,6 +276,78 @@ class $AssetsImagesGen {
   AssetGenImage get iconMineVipOrderArrow =>
       const AssetGenImage('assets/images/icon_mine_vip_order_arrow.png');
 
+  /// File path: assets/images/icon_store_agree_privacy.webp
+  AssetGenImage get iconStoreAgreePrivacy =>
+      const AssetGenImage('assets/images/icon_store_agree_privacy.webp');
+
+  /// File path: assets/images/icon_store_back.webp
+  AssetGenImage get iconStoreBack =>
+      const AssetGenImage('assets/images/icon_store_back.webp');
+
+  /// File path: assets/images/icon_store_banner1.webp
+  AssetGenImage get iconStoreBanner1 =>
+      const AssetGenImage('assets/images/icon_store_banner1.webp');
+
+  /// File path: assets/images/icon_store_banner2.webp
+  AssetGenImage get iconStoreBanner2 =>
+      const AssetGenImage('assets/images/icon_store_banner2.webp');
+
+  /// File path: assets/images/icon_store_banner3.webp
+  AssetGenImage get iconStoreBanner3 =>
+      const AssetGenImage('assets/images/icon_store_banner3.webp');
+
+  /// File path: assets/images/icon_store_divider.webp
+  AssetGenImage get iconStoreDivider =>
+      const AssetGenImage('assets/images/icon_store_divider.webp');
+
+  /// File path: assets/images/icon_store_indicator1.webp
+  AssetGenImage get iconStoreIndicator1 =>
+      const AssetGenImage('assets/images/icon_store_indicator1.webp');
+
+  /// File path: assets/images/icon_store_indicator2.webp
+  AssetGenImage get iconStoreIndicator2 =>
+      const AssetGenImage('assets/images/icon_store_indicator2.webp');
+
+  /// File path: assets/images/icon_store_indicator3.webp
+  AssetGenImage get iconStoreIndicator3 =>
+      const AssetGenImage('assets/images/icon_store_indicator3.webp');
+
+  /// File path: assets/images/icon_store_most.webp
+  AssetGenImage get iconStoreMost =>
+      const AssetGenImage('assets/images/icon_store_most.webp');
+
+  /// File path: assets/images/icon_store_permanent_member.webp
+  AssetGenImage get iconStorePermanentMember =>
+      const AssetGenImage('assets/images/icon_store_permanent_member.webp');
+
+  /// File path: assets/images/icon_store_switch_pay.webp
+  AssetGenImage get iconStoreSwitchPay =>
+      const AssetGenImage('assets/images/icon_store_switch_pay.webp');
+
+  /// File path: assets/images/icon_store_user_review1.webp
+  AssetGenImage get iconStoreUserReview1 =>
+      const AssetGenImage('assets/images/icon_store_user_review1.webp');
+
+  /// File path: assets/images/icon_store_user_review2.webp
+  AssetGenImage get iconStoreUserReview2 =>
+      const AssetGenImage('assets/images/icon_store_user_review2.webp');
+
+  /// File path: assets/images/icon_store_user_review3.webp
+  AssetGenImage get iconStoreUserReview3 =>
+      const AssetGenImage('assets/images/icon_store_user_review3.webp');
+
+  /// File path: assets/images/icon_store_user_review4.webp
+  AssetGenImage get iconStoreUserReview4 =>
+      const AssetGenImage('assets/images/icon_store_user_review4.webp');
+
+  /// File path: assets/images/icon_store_user_reviews_logo.webp
+  AssetGenImage get iconStoreUserReviewsLogo =>
+      const AssetGenImage('assets/images/icon_store_user_reviews_logo.webp');
+
+  /// File path: assets/images/icon_store_user_reviews_title.webp
+  AssetGenImage get iconStoreUserReviewsTitle =>
+      const AssetGenImage('assets/images/icon_store_user_reviews_title.webp');
+
   /// File path: assets/images/icon_tab_character_selected.webp
   AssetGenImage get iconTabCharacterSelected =>
       const AssetGenImage('assets/images/icon_tab_character_selected.webp');
@@ -276,6 +372,14 @@ class $AssetsImagesGen {
   AssetGenImage get iconTabMineUnselect =>
       const AssetGenImage('assets/images/icon_tab_mine_unselect.webp');
 
+  /// File path: assets/images/icon_wechat_payment.webp
+  AssetGenImage get iconWechatPayment =>
+      const AssetGenImage('assets/images/icon_wechat_payment.webp');
+
+  /// File path: assets/images/icon_wechat_scan_payment.webp
+  AssetGenImage get iconWechatScanPayment =>
+      const AssetGenImage('assets/images/icon_wechat_scan_payment.webp');
+
   /// List of all assets
   List<AssetGenImage> get values => [
     bgCharacterBoyBanner,
@@ -291,7 +395,11 @@ class $AssetsImagesGen {
     bgKeyboardManageIntimacy,
     bgMine,
     bgMineVipCard,
+    bgStoreSelectedItem,
+    bgStoreUserReviews,
     iconAboutArrowLeft,
+    iconAlipayPayment,
+    iconAlipayScanPayment,
     iconArrowRight,
     iconBlackBack,
     iconCharacterArrowDown,
@@ -317,11 +425,13 @@ class $AssetsImagesGen {
     iconCharacterVip,
     iconCustomDialogClose,
     iconDialogCloseBlack,
+    iconGoodsInfoTitle,
     iconKeyboardManageCustom,
     iconKeyboardManageFavorite,
     iconKeyboardManageIntimacyText,
     iconKeyboardManagePlus,
     iconKeyboardManageX,
+    iconMemberRetainClose,
     iconMineAbout,
     iconMineArrow,
     iconMineBackArrow,
@@ -336,12 +446,32 @@ class $AssetsImagesGen {
     iconMineVipArrow,
     iconMineVipDescArrow,
     iconMineVipOrderArrow,
+    iconStoreAgreePrivacy,
+    iconStoreBack,
+    iconStoreBanner1,
+    iconStoreBanner2,
+    iconStoreBanner3,
+    iconStoreDivider,
+    iconStoreIndicator1,
+    iconStoreIndicator2,
+    iconStoreIndicator3,
+    iconStoreMost,
+    iconStorePermanentMember,
+    iconStoreSwitchPay,
+    iconStoreUserReview1,
+    iconStoreUserReview2,
+    iconStoreUserReview3,
+    iconStoreUserReview4,
+    iconStoreUserReviewsLogo,
+    iconStoreUserReviewsTitle,
     iconTabCharacterSelected,
     iconTabCharacterUnselect,
     iconTabKeyboardSelected,
     iconTabKeyboardUnselect,
     iconTabMineSelected,
     iconTabMineUnselect,
+    iconWechatPayment,
+    iconWechatScanPayment,
   ];
 }
 

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

@@ -94,6 +94,39 @@ class StringName {
   static final String tipsDialogConfirm = 'tips_dialog_confirm'.tr; // 保存
   static final String tipsDialogCancel = 'tips_dialog_cancel'.tr; // 不保存
   static final String tipsDialogDesc = 'tips_dialog_desc'.tr; // 是否保存键盘当前的修改?
+  static final String storePayWay = 'store_pay_way'.tr; // 支付方式
+  static final String storePayNow = 'store_pay_now'.tr; // 立即支付
+  static final String memberCardNoVipDesc = 'member_card_no_vip_desc'.tr; // ,开通会员,享受会员权益
+  static final String memberVipDesc = 'member_vip_desc'.tr; // 您的会员有效期至
+  static final String memberCardPermanentVipDesc1 = 'member_card_permanent_vip_desc1'.tr; // 您已是
+  static final String memberCardPermanentVipDesc2 = 'member_card_permanent_vip_desc2'.tr; // 终身会员
+  static final String memberNoLogged = 'member_no_logged'.tr; // 游客
+  static final String textSpanIHaveReadAndAgree = 'text_span_i_have_read_and_agree'.tr; // 我已阅读并同意
+  static final String textSpanAnd = 'text_span_and'.tr; // 和
+  static final String textSpanPrivacyPolicy = 'text_span_privacy_policy'.tr; // 《隐私政策》
+  static final String textSpanServiceTerms = 'text_span_service_terms'.tr; // 《服务条款》
+  static final String memberContinuePay = 'member_continue_pay'.tr; // 继续支付
+  static final String memberPleaseChoiceGoods = 'member_please_choice_goods'.tr; // 请选择支付商品
+  static final String memberPleaseChoicePayment = 'member_please_choice_payment'.tr; // 请选择支付方式
+  static final String payLoading = 'pay_loading'.tr; // 请求中...
+  static final String payUserCancel = 'pay_user_cancel'.tr; // 用户取消支付
+  static final String payNotSupport = 'pay_not_support'.tr; // 不支持该支付类型
+  static final String payWxEvnError = 'pay_wx_evn_error'.tr; // 微信未安装或微信版本不支持
+  static final String payError = 'pay_error'.tr; // 支付失败,请稍后重试
+  static final String payQuerypayState = 'pay_query_pay_state'.tr; // 正在查询订单状态..
+  static final String payNotConnectStore = 'pay_not_connect_store'.tr; // 无法连接到商店
+  static final String paySuccessTitle = 'pay_success_title'.tr; // 支付成功
+  static final String paySuccessDesc = 'pay_success_desc'.tr; // 您的订单已成功支付
+  static final String alipayQrCodeTips = 'alipay_qr_code_tips'.tr; // 请使用支付宝扫码支付
+  static final String wechatQrCodeTips = 'wechat_qr_code_tips'.tr; // 请使用微信扫码支付
+  static final String wechatPayQrCodeTips = 'wechat_pay_qr_code_tips'.tr; // 请使用微信扫码支付
+  static final String memberPaymentFailed = 'member_payment_failed'.tr; // 开通失败,请稍后重试
+  static final String memberAgreementDialogTitle = 'member_agreement_dialog_title'.tr; // 请阅读并同意以下协议
+  static final String memberAgreementDialogAgreement = 'member_agreement_dialog_agreement'.tr; // 《会员协议》
+  static final String memberAgreementDialogAutoRenewal = 'member_agreement_dialog_auto_renewal'.tr; // 《自动续费协议》。
+  static final String memberAgreementDialogDesc = 'member_agreement_dialog_desc'.tr; // 进入下一步前,请先阅读并同意
+  static final String memberAgreementDialogConfirm = 'member_agreement_dialog_confirm'.tr; // 确定
+  static final String memberAgreementDialogCancel = 'member_agreement_dialog_cancel'.tr; // 取消
 }
 class StringMultiSource {
   StringMultiSource._();
@@ -191,6 +224,39 @@ class StringMultiSource {
       'tips_dialog_confirm': '保存',
       'tips_dialog_cancel': '不保存',
       'tips_dialog_desc': '是否保存键盘当前的修改?',
+      'store_pay_way': '支付方式',
+      'store_pay_now': '立即支付',
+      'member_card_no_vip_desc': ',开通会员,享受会员权益',
+      'member_vip_desc': '您的会员有效期至',
+      'member_card_permanent_vip_desc1': '您已是',
+      'member_card_permanent_vip_desc2': '终身会员',
+      'member_no_logged': '游客',
+      'text_span_i_have_read_and_agree': '我已阅读并同意',
+      'text_span_and': '和',
+      'text_span_privacy_policy': '《隐私政策》',
+      'text_span_service_terms': '《服务条款》',
+      'member_continue_pay': '继续支付',
+      'member_please_choice_goods': '请选择支付商品',
+      'member_please_choice_payment': '请选择支付方式',
+      'pay_loading': '请求中...',
+      'pay_user_cancel': '用户取消支付',
+      'pay_not_support': '不支持该支付类型',
+      'pay_wx_evn_error': '微信未安装或微信版本不支持',
+      'pay_error': '支付失败,请稍后重试',
+      'pay_query_pay_state': '正在查询订单状态..',
+      'pay_not_connect_store': '无法连接到商店',
+      'pay_success_title': '支付成功',
+      'pay_success_desc': '您的订单已成功支付',
+      'alipay_qr_code_tips': '请使用支付宝扫码支付',
+      'wechat_qr_code_tips': '请使用微信扫码支付',
+      'wechat_pay_qr_code_tips': '请使用微信扫码支付',
+      'member_payment_failed': '开通失败,请稍后重试',
+      'member_agreement_dialog_title': '请阅读并同意以下协议',
+      'member_agreement_dialog_agreement': '《会员协议》',
+      'member_agreement_dialog_auto_renewal': '《自动续费协议》。',
+      'member_agreement_dialog_desc': '进入下一步前,请先阅读并同意',
+      'member_agreement_dialog_confirm': '确定',
+      'member_agreement_dialog_cancel': '取消',
     },
   };
 }

+ 5 - 0
lib/router/app_pages.dart

@@ -10,6 +10,8 @@ import 'package:keyboard/module/keyboard/keyboard_controller.dart';
 import 'package:keyboard/module/keyboard_manage/keyboard_manage_controller.dart';
 import 'package:keyboard/module/login/login_controller.dart';
 import 'package:keyboard/module/mine/mine_controller.dart';
+import 'package:keyboard/module/store/store_controller.dart';
+import 'package:keyboard/module/store/store_page.dart';
 
 import '../di/get_it.dart';
 import '../module/about/about_page.dart';
@@ -40,6 +42,7 @@ abstract class RoutePath {
   static const characterCustom = '/characterCustom';
   static const characterCustomDetail = '/characterCustomDetail';
   static const characterCustomList = '/characterCustomList';
+  static const store = '/store';
 }
 
 class AppBinding extends Bindings {
@@ -58,6 +61,7 @@ class AppBinding extends Bindings {
     lazyPut(() => getIt.get<CharacterCustomController>());
     lazyPut(() => getIt.get<CharacterCustomDetailController>());
     lazyPut(() => getIt.get<CharacterCustomListController>());
+    lazyPut(() => getIt.get<StoreController>());
   }
 
   void lazyPut<S>(InstanceBuilderCallback<S> builder) {
@@ -81,4 +85,5 @@ final generalPages = [
     name: RoutePath.characterCustomList,
     page: () => CharacterCustomListPage(),
   ),
+  GetPage(name: RoutePath.store, page: () => StorePage()),
 ];

+ 0 - 0
lib/utils/common_expand.dart


Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä