Showing
16 changed files
with
378 additions
and
147 deletions
| @@ -33,6 +33,8 @@ class SubmitFeedbackController extends GetxController { | @@ -33,6 +33,8 @@ class SubmitFeedbackController extends GetxController { | ||
| 33 | }); | 33 | }); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | + static const maxVideoSizeBytes = 200 * 1024 * 1024; | ||
| 37 | + | ||
| 36 | Future<void> pickImages() async { | 38 | Future<void> pickImages() async { |
| 37 | final remainingCount = maxImageCount - selectedImages.length; | 39 | final remainingCount = maxImageCount - selectedImages.length; |
| 38 | if (remainingCount <= 0) { | 40 | if (remainingCount <= 0) { |
| @@ -53,7 +55,22 @@ class SubmitFeedbackController extends GetxController { | @@ -53,7 +55,22 @@ class SubmitFeedbackController extends GetxController { | ||
| 53 | 55 | ||
| 54 | if (images.isEmpty) return; | 56 | if (images.isEmpty) return; |
| 55 | 57 | ||
| 56 | - selectedImages.addAll(images.take(remainingCount)); | 58 | + final validImages = <XFile>[]; |
| 59 | + var hasOversizedFile = false; | ||
| 60 | + for (final file in images) { | ||
| 61 | + final size = await file.length(); | ||
| 62 | + if (size > maxVideoSizeBytes) { | ||
| 63 | + hasOversizedFile = true; | ||
| 64 | + continue; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + validImages.add(file); | ||
| 68 | + } | ||
| 69 | + if (hasOversizedFile) { | ||
| 70 | + AppToast.show('单个文件大小不能超过 200MB'); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + selectedImages.addAll(validImages); | ||
| 57 | } | 74 | } |
| 58 | 75 | ||
| 59 | void removeImage(XFile image) { | 76 | void removeImage(XFile image) { |
| @@ -9,6 +9,7 @@ import 'package:doublefeel_flutter/app/modules/home/widgets/today/no_health_data | @@ -9,6 +9,7 @@ import 'package:doublefeel_flutter/app/modules/home/widgets/today/no_health_data | ||
| 9 | import 'package:doublefeel_flutter/app/modules/login/views/email_login_contact_us_bottom_sheet.dart'; | 9 | import 'package:doublefeel_flutter/app/modules/login/views/email_login_contact_us_bottom_sheet.dart'; |
| 10 | import 'package:doublefeel_flutter/app/modules/report_common/models/report_period.dart'; | 10 | import 'package:doublefeel_flutter/app/modules/report_common/models/report_period.dart'; |
| 11 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | 11 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; |
| 12 | +import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; | ||
| 12 | import 'package:doublefeel_flutter/core/config/app_environment_config.dart'; | 13 | import 'package:doublefeel_flutter/core/config/app_environment_config.dart'; |
| 13 | import 'package:doublefeel_flutter/core/constants/intent_keys.dart'; | 14 | import 'package:doublefeel_flutter/core/constants/intent_keys.dart'; |
| 14 | import 'package:doublefeel_flutter/core/logging/app_logger.dart'; | 15 | import 'package:doublefeel_flutter/core/logging/app_logger.dart'; |
| @@ -737,9 +738,11 @@ class TodayController extends GetxController with WidgetsBindingObserver { | @@ -737,9 +738,11 @@ class TodayController extends GetxController with WidgetsBindingObserver { | ||
| 737 | final result = await _payApi.getProductList(); | 738 | final result = await _payApi.getProductList(); |
| 738 | if (result is! AppSuccess<PayProductListResponse>) return; | 739 | if (result is! AppSuccess<PayProductListResponse>) return; |
| 739 | 740 | ||
| 740 | - final products = (result.data.productList ?? const <PayProduct>[]) | ||
| 741 | - .where((product) => _nonEmpty(product.appleId) != null) | ||
| 742 | - .toList(); | 741 | + final products = isIOS() |
| 742 | + ? (result.data.productList ?? const <PayProduct>[]) | ||
| 743 | + .where((product) => _nonEmpty(product.appleId) != null) | ||
| 744 | + .toList() | ||
| 745 | + : (result.data.productList ?? const <PayProduct>[]); | ||
| 743 | yearlyProduct.value = | 746 | yearlyProduct.value = |
| 744 | products.firstWhereOrNull((p) => p.content?.isDiscountOffer == 1); | 747 | products.firstWhereOrNull((p) => p.content?.isDiscountOffer == 1); |
| 745 | // AppLogger.e(yearlyProduct); | 748 | // AppLogger.e(yearlyProduct); |
| @@ -774,26 +777,42 @@ class TodayController extends GetxController with WidgetsBindingObserver { | @@ -774,26 +777,42 @@ class TodayController extends GetxController with WidgetsBindingObserver { | ||
| 774 | } | 777 | } |
| 775 | 778 | ||
| 776 | String originDisplayPrice() { | 779 | String originDisplayPrice() { |
| 777 | - final appleInfo = yearlyProductAppleInfo.value; | ||
| 778 | - if (appleInfo == null) return ''; | ||
| 779 | - final actual = | ||
| 780 | - appleInfo.price > 0 ? appleInfo.price : appleInfo.originPrice; | ||
| 781 | - final inflated = actual / 100.0 * 1.2; | ||
| 782 | - // 保留两位小数,单位与 currencyCode 一致 | ||
| 783 | - return '${appleInfo.currencyCode}${inflated.toStringAsFixed(2)}'; | 780 | + if (isIOS()) { |
| 781 | + final appleInfo = yearlyProductAppleInfo.value; | ||
| 782 | + if (appleInfo == null) return ''; | ||
| 783 | + final actual = | ||
| 784 | + appleInfo.price > 0 ? appleInfo.price : appleInfo.originPrice; | ||
| 785 | + final inflated = actual / 100.0 * 1.2; | ||
| 786 | + // 保留两位小数,单位与 currencyCode 一致 | ||
| 787 | + return '${appleInfo.currencyCode}${inflated.toStringAsFixed(2)}'; | ||
| 788 | + } else if (isOhos()) { | ||
| 789 | + final actual = yearlyProduct.value?.price ?? 0; | ||
| 790 | + final inflated = actual / 100.0 * 1.2; | ||
| 791 | + // 保留两位小数,单位与 currencyCode 一致 | ||
| 792 | + return '¥${inflated.toStringAsFixed(2)}'; | ||
| 793 | + } else { | ||
| 794 | + return ''; | ||
| 795 | + } | ||
| 784 | } | 796 | } |
| 785 | 797 | ||
| 786 | String displayPrice() { | 798 | String displayPrice() { |
| 787 | - final appleInfo = yearlyProductAppleInfo.value; | ||
| 788 | - if (appleInfo == null) return ''; | ||
| 789 | - final price = appleInfo.price; | ||
| 790 | - if (price > 0) { | ||
| 791 | - final priceDescription = appleInfo.priceDescription.trim(); | ||
| 792 | - if (priceDescription.isNotEmpty) { | ||
| 793 | - return priceDescription; | 799 | + if (isIOS()) { |
| 800 | + final appleInfo = yearlyProductAppleInfo.value; | ||
| 801 | + if (appleInfo == null) return ''; | ||
| 802 | + final price = appleInfo.price; | ||
| 803 | + if (price > 0) { | ||
| 804 | + final priceDescription = appleInfo.priceDescription.trim(); | ||
| 805 | + if (priceDescription.isNotEmpty) { | ||
| 806 | + return priceDescription; | ||
| 807 | + } | ||
| 794 | } | 808 | } |
| 809 | + return appleInfo.originPriceDescription; | ||
| 810 | + } else if (isOhos()) { | ||
| 811 | + final actual = (yearlyProduct.value?.price ?? 0) / 100.0; | ||
| 812 | + return '¥${actual.toStringAsFixed(2)}'; | ||
| 813 | + } else { | ||
| 814 | + return ''; | ||
| 795 | } | 815 | } |
| 796 | - return appleInfo.originPriceDescription; | ||
| 797 | } | 816 | } |
| 798 | 817 | ||
| 799 | @override | 818 | @override |
| @@ -9,6 +9,7 @@ import 'package:doublefeel_flutter/app/modules/watch_theme/models/watch_theme_mo | @@ -9,6 +9,7 @@ import 'package:doublefeel_flutter/app/modules/watch_theme/models/watch_theme_mo | ||
| 9 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | 9 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; |
| 10 | import 'package:doublefeel_flutter/app/utils/assets_helper.dart'; | 10 | import 'package:doublefeel_flutter/app/utils/assets_helper.dart'; |
| 11 | import 'package:doublefeel_flutter/app/utils/dialog_utils.dart'; | 11 | import 'package:doublefeel_flutter/app/utils/dialog_utils.dart'; |
| 12 | +import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; | ||
| 12 | import 'package:doublefeel_flutter/core/config/app_environment_config.dart'; | 13 | import 'package:doublefeel_flutter/core/config/app_environment_config.dart'; |
| 13 | import 'package:doublefeel_flutter/core/services/thinking_data_service.dart'; | 14 | import 'package:doublefeel_flutter/core/services/thinking_data_service.dart'; |
| 14 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 15 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| @@ -57,12 +58,14 @@ class MyTab extends GetView<MyController> { | @@ -57,12 +58,14 @@ class MyTab extends GetView<MyController> { | ||
| 57 | ? _ProCard(vipInfo: vipInfo) | 58 | ? _ProCard(vipInfo: vipInfo) |
| 58 | : _UnlockPremiumCard(controller), | 59 | : _UnlockPremiumCard(controller), |
| 59 | const SizedBox(height: 12), | 60 | const SizedBox(height: 12), |
| 60 | - _WatchThemeCard( | ||
| 61 | - themes: controller.watchThemeItems, | ||
| 62 | - isLoading: controller.isLoadingWatchThemes.value, | ||
| 63 | - onTap: () => controller.goWatchThemePage(), | ||
| 64 | - ), | ||
| 65 | - const SizedBox(height: 12), | 61 | + if (isIOS()) ...[ |
| 62 | + _WatchThemeCard( | ||
| 63 | + themes: controller.watchThemeItems, | ||
| 64 | + isLoading: controller.isLoadingWatchThemes.value, | ||
| 65 | + onTap: () => controller.goWatchThemePage(), | ||
| 66 | + ), | ||
| 67 | + const SizedBox(height: 12) | ||
| 68 | + ], | ||
| 66 | _SettingsRow( | 69 | _SettingsRow( |
| 67 | title: context.l10n.accountInformation, | 70 | title: context.l10n.accountInformation, |
| 68 | onTap: () => Get.to( | 71 | onTap: () => Get.to( |
| @@ -10,7 +10,8 @@ import 'today_faq_bottom_sheet.dart'; | @@ -10,7 +10,8 @@ import 'today_faq_bottom_sheet.dart'; | ||
| 10 | void showHrvPrincipleExplanationBottomSheet(BuildContext context) { | 10 | void showHrvPrincipleExplanationBottomSheet(BuildContext context) { |
| 11 | final screenHeight = MediaQuery.of(context).size.height; | 11 | final screenHeight = MediaQuery.of(context).size.height; |
| 12 | final statusBarHeight = MediaQuery.of(context).padding.top; | 12 | final statusBarHeight = MediaQuery.of(context).padding.top; |
| 13 | - final maxChildSize = (screenHeight - statusBarHeight) / screenHeight; | 13 | + final maxChildSize = |
| 14 | + isOhos() ? 0.8 : (screenHeight - statusBarHeight) / screenHeight; | ||
| 14 | Get.bottomSheet( | 15 | Get.bottomSheet( |
| 15 | DraggableScrollableSheet( | 16 | DraggableScrollableSheet( |
| 16 | maxChildSize: maxChildSize, | 17 | maxChildSize: maxChildSize, |
| 1 | import 'package:doublefeel_flutter/app/modules/home/controllers/today_controller.dart'; | 1 | import 'package:doublefeel_flutter/app/modules/home/controllers/today_controller.dart'; |
| 2 | +import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; | ||
| 2 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 3 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| 3 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; | 4 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; |
| 4 | import 'package:flutter/material.dart'; | 5 | import 'package:flutter/material.dart'; |
| @@ -95,9 +96,11 @@ class PremiumCard extends StatelessWidget { | @@ -95,9 +96,11 @@ class PremiumCard extends StatelessWidget { | ||
| 95 | ], | 96 | ], |
| 96 | ), | 97 | ), |
| 97 | const Spacer(), | 98 | const Spacer(), |
| 98 | - Obx(() => controller.yearlyProductAppleInfo.value != | ||
| 99 | - null && | ||
| 100 | - controller.yearlyProduct.value != null | 99 | + Obx(() => (isIOS() |
| 100 | + ? controller.yearlyProductAppleInfo.value != | ||
| 101 | + null && | ||
| 102 | + controller.yearlyProduct.value != null | ||
| 103 | + : controller.yearlyProduct.value != null) | ||
| 101 | ? Row( | 104 | ? Row( |
| 102 | children: [ | 105 | children: [ |
| 103 | Text( | 106 | Text( |
| @@ -110,7 +113,8 @@ class PremiumCard extends StatelessWidget { | @@ -110,7 +113,8 @@ class PremiumCard extends StatelessWidget { | ||
| 110 | ), | 113 | ), |
| 111 | const SizedBox(width: 6), | 114 | const SizedBox(width: 6), |
| 112 | Text( | 115 | Text( |
| 113 | - controller.displayPrice(), | 116 | + l10n.originalPrice( |
| 117 | + controller.displayPrice()), | ||
| 114 | style: TextStyle( | 118 | style: TextStyle( |
| 115 | fontSize: 12, | 119 | fontSize: 12, |
| 116 | fontWeight: FontWeight.w500, | 120 | fontWeight: FontWeight.w500, |
| 1 | import 'dart:async'; | 1 | import 'dart:async'; |
| 2 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | 2 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; |
| 3 | import 'package:doublefeel_flutter/app/utils/assets_helper.dart'; | 3 | import 'package:doublefeel_flutter/app/utils/assets_helper.dart'; |
| 4 | +import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; | ||
| 4 | import 'package:doublefeel_flutter/core/services/thinking_data_service.dart'; | 5 | import 'package:doublefeel_flutter/core/services/thinking_data_service.dart'; |
| 5 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 6 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| 6 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; | 7 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; |
| @@ -287,7 +288,7 @@ class TodayAdCarousel extends StatelessWidget { | @@ -287,7 +288,7 @@ class TodayAdCarousel extends StatelessWidget { | ||
| 287 | ), | 288 | ), |
| 288 | ); | 289 | ); |
| 289 | } | 290 | } |
| 290 | - if (showHrv) { | 291 | + if (isIOS() && showHrv) { |
| 291 | activeBanners.add( | 292 | activeBanners.add( |
| 292 | TodayHrvAdBanner( | 293 | TodayHrvAdBanner( |
| 293 | controller: controller, | 294 | controller: controller, |
| @@ -30,25 +30,25 @@ class MembershipDetailController extends GetxController { | @@ -30,25 +30,25 @@ class MembershipDetailController extends GetxController { | ||
| 30 | 30 | ||
| 31 | final isUnlocking = false.obs; | 31 | final isUnlocking = false.obs; |
| 32 | Future<void> unlock() async { | 32 | Future<void> unlock() async { |
| 33 | - isUnlocking.value = true; | ||
| 34 | - try { | ||
| 35 | - final orderResult = await _payApi.createOrderByApple(123); | ||
| 36 | - if (orderResult is! AppSuccess<ApplePayOrderResponse>) return; | ||
| 37 | - | ||
| 38 | - final orderUuid = orderResult.data.orderInfo?.orderUuid?.trim(); | ||
| 39 | - if (orderUuid == null || orderUuid.isEmpty) { | ||
| 40 | - AppToast.show(l10n.purchaseOrderInfoUnavailable); | ||
| 41 | - return; | ||
| 42 | - } | ||
| 43 | - | ||
| 44 | - final paymentResult = await _performApplePayment( | ||
| 45 | - productId: 'appleProductId', | ||
| 46 | - orderUuid: orderUuid, | ||
| 47 | - ); | ||
| 48 | - await _handleApplePaymentResult(paymentResult); | ||
| 49 | - } finally { | ||
| 50 | - isUnlocking.value = false; | ||
| 51 | - } | 33 | + // isUnlocking.value = true; |
| 34 | + // try { | ||
| 35 | + // final orderResult = await _payApi.createOrderByApple(123); | ||
| 36 | + // if (orderResult is! AppSuccess<ApplePayOrderResponse>) return; | ||
| 37 | + | ||
| 38 | + // final orderUuid = orderResult.data.orderInfo?.orderUuid?.trim(); | ||
| 39 | + // if (orderUuid == null || orderUuid.isEmpty) { | ||
| 40 | + // AppToast.show(l10n.purchaseOrderInfoUnavailable); | ||
| 41 | + // return; | ||
| 42 | + // } | ||
| 43 | + | ||
| 44 | + // final paymentResult = await _performApplePayment( | ||
| 45 | + // productId: 'appleProductId', | ||
| 46 | + // orderUuid: orderUuid, | ||
| 47 | + // ); | ||
| 48 | + // await _handleApplePaymentResult(paymentResult); | ||
| 49 | + // } finally { | ||
| 50 | + // isUnlocking.value = false; | ||
| 51 | + // } | ||
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | Future<void> _handleApplePaymentResult( | 54 | Future<void> _handleApplePaymentResult( |
| 1 | +import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; | ||
| 1 | import 'package:doublefeel_flutter/core/network/api/pay_api.dart'; | 2 | import 'package:doublefeel_flutter/core/network/api/pay_api.dart'; |
| 2 | import 'package:doublefeel_flutter/core/network/api/vip_api.dart'; | 3 | import 'package:doublefeel_flutter/core/network/api/vip_api.dart'; |
| 3 | import 'package:doublefeel_flutter/core/result/app_result.dart'; | 4 | import 'package:doublefeel_flutter/core/result/app_result.dart'; |
| @@ -22,41 +23,84 @@ class MembershipOfferController extends GetxController { | @@ -22,41 +23,84 @@ class MembershipOfferController extends GetxController { | ||
| 22 | final UserPreferencesStorage _userPreferences = | 23 | final UserPreferencesStorage _userPreferences = |
| 23 | Get.find<UserPreferencesStorage>(); | 24 | Get.find<UserPreferencesStorage>(); |
| 24 | final AppPlatformHostApi _platformHostApi = AppPlatformHostApi(); | 25 | final AppPlatformHostApi _platformHostApi = AppPlatformHostApi(); |
| 26 | + final AlipayHostApi _alipayHostApi = AlipayHostApi(); | ||
| 25 | 27 | ||
| 26 | final isUnlocking = false.obs; | 28 | final isUnlocking = false.obs; |
| 27 | Future<void> unlock() async { | 29 | Future<void> unlock() async { |
| 28 | if (isUnlocking.value) return; | 30 | if (isUnlocking.value) return; |
| 29 | 31 | ||
| 30 | final productId = yearlyProduct.value?.id; | 32 | final productId = yearlyProduct.value?.id; |
| 31 | - final appleProductId = yearlyProduct.value?.appleId; | ||
| 32 | - if (productId == null || appleProductId == null || appleProductId.isEmpty) { | ||
| 33 | - AppToast.show(l10n.purchaseProductInfoUnavailable); | ||
| 34 | - return; | ||
| 35 | - } | ||
| 36 | - isUnlocking.value = true; | 33 | + |
| 37 | try { | 34 | try { |
| 38 | - final orderResult = await _payApi.createOrderByApple(productId); | ||
| 39 | - if (orderResult is! AppSuccess<ApplePayOrderResponse>) { | ||
| 40 | - if (isFromOnboard) { | ||
| 41 | - Get.offAllNamed(AppRoutes.home, arguments: {'isFromOnboard': true}); | 35 | + if (isOhos()) { |
| 36 | + if (productId == null) { | ||
| 37 | + AppToast.show(l10n.purchaseProductInfoUnavailable); | ||
| 38 | + return; | ||
| 39 | + } | ||
| 40 | + isUnlocking.value = true; | ||
| 41 | + final orderResult = await _payApi.createCustomOrder( | ||
| 42 | + productId: productId, | ||
| 43 | + paymentChannel: PaymentChannel.alipay.value, | ||
| 44 | + productChannel: ProductChannel.huawei.value, | ||
| 45 | + ); | ||
| 46 | + if (orderResult is! AppSuccess<ApplePayOrderResponse>) { | ||
| 47 | + if (isFromOnboard) { | ||
| 48 | + Get.offAllNamed(AppRoutes.home, arguments: {'isFromOnboard': true}); | ||
| 49 | + } | ||
| 50 | + return; | ||
| 42 | } | 51 | } |
| 43 | - return; | ||
| 44 | - } | ||
| 45 | 52 | ||
| 46 | - final orderUuid = orderResult.data.orderInfo?.orderUuid?.trim(); | ||
| 47 | - if (orderUuid == null || orderUuid.isEmpty) { | ||
| 48 | - AppToast.show(l10n.purchaseOrderInfoUnavailable); | ||
| 49 | - if (isFromOnboard) { | ||
| 50 | - Get.offAllNamed(AppRoutes.home, arguments: {'isFromOnboard': true}); | 53 | + final prepayData = orderResult.data.prepayData?.trim(); |
| 54 | + if (prepayData == null || prepayData.isEmpty) { | ||
| 55 | + AppToast.show(l10n.purchaseOrderInfoUnavailable); | ||
| 56 | + if (isFromOnboard) { | ||
| 57 | + Get.offAllNamed(AppRoutes.home, arguments: {'isFromOnboard': true}); | ||
| 58 | + } | ||
| 59 | + return; | ||
| 51 | } | 60 | } |
| 52 | - return; | ||
| 53 | - } | ||
| 54 | 61 | ||
| 55 | - final paymentResult = await _performApplePayment( | ||
| 56 | - productId: appleProductId, | ||
| 57 | - orderUuid: orderUuid, | ||
| 58 | - ); | ||
| 59 | - await _handleApplePaymentResult(paymentResult); | 62 | + //todo 鸿蒙Alipay prepayData |
| 63 | + final result = await _alipayHostApi.launchAliPay(prepayData); | ||
| 64 | + if (result == AliPayResultCode.success) { | ||
| 65 | + _trackSuccessPayOrder(); | ||
| 66 | + await _completeSuccessfulPurchase(); | ||
| 67 | + } else if (result == AliPayResultCode.error) { | ||
| 68 | + if (isFromOnboard) { | ||
| 69 | + Get.offAllNamed(AppRoutes.home, arguments: {'isFromOnboard': true}); | ||
| 70 | + } | ||
| 71 | + } | ||
| 72 | + } else if (isIOS()) { | ||
| 73 | + final appleProductId = yearlyProduct.value?.appleId; | ||
| 74 | + if (productId == null || | ||
| 75 | + appleProductId == null || | ||
| 76 | + appleProductId.isEmpty) { | ||
| 77 | + AppToast.show(l10n.purchaseProductInfoUnavailable); | ||
| 78 | + return; | ||
| 79 | + } | ||
| 80 | + isUnlocking.value = true; | ||
| 81 | + final orderResult = await _payApi.createOrderByApple(productId); | ||
| 82 | + if (orderResult is! AppSuccess<ApplePayOrderResponse>) { | ||
| 83 | + if (isFromOnboard) { | ||
| 84 | + Get.offAllNamed(AppRoutes.home, arguments: {'isFromOnboard': true}); | ||
| 85 | + } | ||
| 86 | + return; | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + final orderUuid = orderResult.data.orderInfo?.orderUuid?.trim(); | ||
| 90 | + if (orderUuid == null || orderUuid.isEmpty) { | ||
| 91 | + AppToast.show(l10n.purchaseOrderInfoUnavailable); | ||
| 92 | + if (isFromOnboard) { | ||
| 93 | + Get.offAllNamed(AppRoutes.home, arguments: {'isFromOnboard': true}); | ||
| 94 | + } | ||
| 95 | + return; | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + final paymentResult = await _performApplePayment( | ||
| 99 | + productId: appleProductId, | ||
| 100 | + orderUuid: orderUuid, | ||
| 101 | + ); | ||
| 102 | + await _handleApplePaymentResult(paymentResult); | ||
| 103 | + } | ||
| 60 | } finally { | 104 | } finally { |
| 61 | isUnlocking.value = false; | 105 | isUnlocking.value = false; |
| 62 | } | 106 | } |
| @@ -87,13 +131,16 @@ class MembershipOfferController extends GetxController { | @@ -87,13 +131,16 @@ class MembershipOfferController extends GetxController { | ||
| 87 | } | 131 | } |
| 88 | trackMap['product_type'] = yearlyProduct.value?.appleId; | 132 | trackMap['product_type'] = yearlyProduct.value?.appleId; |
| 89 | trackMap['price'] = yearlyProduct.value?.price; | 133 | trackMap['price'] = yearlyProduct.value?.price; |
| 90 | - | ||
| 91 | - final appleInfo = yearlyProductAppleInfo.value; | ||
| 92 | - trackMap['currency_code'] = appleInfo?.currencyCode ?? ''; | ||
| 93 | - | ||
| 94 | - var actualPrice = appleInfo?.price ?? 0; | ||
| 95 | - trackMap['actual_price'] = | ||
| 96 | - actualPrice > 0 ? actualPrice : (appleInfo?.originPrice ?? 0); | 134 | + if (isOhos()) { |
| 135 | + trackMap['currency_code'] = 'CN'; | ||
| 136 | + trackMap['actual_price'] = yearlyProduct.value?.price; | ||
| 137 | + } else if (isIOS()) { | ||
| 138 | + final appleInfo = yearlyProductAppleInfo.value; | ||
| 139 | + trackMap['currency_code'] = appleInfo?.currencyCode ?? ''; | ||
| 140 | + var actualPrice = appleInfo?.price ?? 0; | ||
| 141 | + trackMap['actual_price'] = | ||
| 142 | + actualPrice > 0 ? actualPrice : (appleInfo?.originPrice ?? 0); | ||
| 143 | + } | ||
| 97 | 144 | ||
| 98 | ta.track( | 145 | ta.track( |
| 99 | 'success_doublefeel_pay_order', | 146 | 'success_doublefeel_pay_order', |
| @@ -192,19 +239,24 @@ class MembershipOfferController extends GetxController { | @@ -192,19 +239,24 @@ class MembershipOfferController extends GetxController { | ||
| 192 | final result = await _payApi.getProductList(); | 239 | final result = await _payApi.getProductList(); |
| 193 | if (result is! AppSuccess<PayProductListResponse>) return; | 240 | if (result is! AppSuccess<PayProductListResponse>) return; |
| 194 | 241 | ||
| 195 | - final products = (result.data.productList ?? const <PayProduct>[]) | ||
| 196 | - .where((product) => _nonEmpty(product.appleId) != null) | ||
| 197 | - .toList(); | ||
| 198 | - yearlyProduct.value = | ||
| 199 | - products.firstWhereOrNull((p) => p.content?.isDiscountOffer == 1); | ||
| 200 | - | ||
| 201 | - if (yearlyProduct.value != null) { | ||
| 202 | - final appleProductId = _nonEmpty(yearlyProduct.value!.appleId); | ||
| 203 | - if (appleProductId == null) return; | ||
| 204 | - | ||
| 205 | - yearlyProductAppleInfo.value = await _requestAppleProductInfo( | ||
| 206 | - productId: appleProductId, | ||
| 207 | - baseUnit: yearlyProduct.value!.content?.baseUnit() ?? 1); | 242 | + if (isIOS()) { |
| 243 | + final products = (result.data.productList ?? const <PayProduct>[]) | ||
| 244 | + .where((product) => _nonEmpty(product.appleId) != null) | ||
| 245 | + .toList(); | ||
| 246 | + yearlyProduct.value = | ||
| 247 | + products.firstWhereOrNull((p) => p.content?.isDiscountOffer == 1); | ||
| 248 | + if (yearlyProduct.value != null) { | ||
| 249 | + final appleProductId = _nonEmpty(yearlyProduct.value!.appleId); | ||
| 250 | + if (appleProductId == null) return; | ||
| 251 | + | ||
| 252 | + yearlyProductAppleInfo.value = await _requestAppleProductInfo( | ||
| 253 | + productId: appleProductId, | ||
| 254 | + baseUnit: yearlyProduct.value!.content?.baseUnit() ?? 1); | ||
| 255 | + } | ||
| 256 | + } else if (isOhos()) { | ||
| 257 | + final products = (result.data.productList ?? const <PayProduct>[]); | ||
| 258 | + yearlyProduct.value = | ||
| 259 | + products.firstWhereOrNull((p) => p.content?.isDiscountOffer == 1); | ||
| 208 | } | 260 | } |
| 209 | } | 261 | } |
| 210 | 262 |
| 1 | import 'package:doublefeel_flutter/app/modules/membership_offer/controllers/membership_offer_controller.dart'; | 1 | import 'package:doublefeel_flutter/app/modules/membership_offer/controllers/membership_offer_controller.dart'; |
| 2 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | 2 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; |
| 3 | +import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; | ||
| 3 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 4 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| 4 | import 'package:doublefeel_flutter/data/models/pay/pay_models.dart'; | 5 | import 'package:doublefeel_flutter/data/models/pay/pay_models.dart'; |
| 5 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; | 6 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; |
| @@ -78,51 +79,97 @@ class MembershipOfferView extends GetView<MembershipOfferController> { | @@ -78,51 +79,97 @@ class MembershipOfferView extends GetView<MembershipOfferController> { | ||
| 78 | 'assets/images/user_onboarding/ic_membership_box.png'), | 79 | 'assets/images/user_onboarding/ic_membership_box.png'), |
| 79 | const SizedBox(height: 4), | 80 | const SizedBox(height: 4), |
| 80 | Obx(() { | 81 | Obx(() { |
| 81 | - final yearInfo = | ||
| 82 | - controller.yearlyProductAppleInfo.value; | ||
| 83 | final yearProduct = controller.yearlyProduct.value; | 82 | final yearProduct = controller.yearlyProduct.value; |
| 84 | - | ||
| 85 | - return yearInfo == null || yearProduct == null | ||
| 86 | - ? const SizedBox( | ||
| 87 | - child: CircularProgressIndicator()) | ||
| 88 | - : Column( | ||
| 89 | - children: [ | ||
| 90 | - Text( | ||
| 91 | - context.l10n.originalPricePerYear( | ||
| 92 | - _originDisplayPrice(yearInfo)), | ||
| 93 | - textAlign: TextAlign.center, | ||
| 94 | - style: const TextStyle( | ||
| 95 | - color: Color(0xFF78787D), | ||
| 96 | - fontSize: 16, | ||
| 97 | - decoration: | ||
| 98 | - TextDecoration.lineThrough, | ||
| 99 | - decorationColor: Color(0xFF78787D), | ||
| 100 | - letterSpacing: 0, | 83 | + if (isOhos()) { |
| 84 | + return (yearProduct == null) | ||
| 85 | + ? const SizedBox( | ||
| 86 | + child: CircularProgressIndicator()) | ||
| 87 | + : Column( | ||
| 88 | + children: [ | ||
| 89 | + Text( | ||
| 90 | + context.l10n.originalPricePerYear( | ||
| 91 | + _originDisplayPriceOhos( | ||
| 92 | + yearProduct)), | ||
| 93 | + textAlign: TextAlign.center, | ||
| 94 | + style: const TextStyle( | ||
| 95 | + color: Color(0xFF78787D), | ||
| 96 | + fontSize: 16, | ||
| 97 | + decoration: | ||
| 98 | + TextDecoration.lineThrough, | ||
| 99 | + decorationColor: Color(0xFF78787D), | ||
| 100 | + letterSpacing: 0, | ||
| 101 | + ), | ||
| 102 | + ), | ||
| 103 | + const SizedBox(height: 6), | ||
| 104 | + Text( | ||
| 105 | + _displayPriceOhos(yearProduct), | ||
| 106 | + textAlign: TextAlign.center, | ||
| 107 | + style: TextStyle( | ||
| 108 | + color: context.colors.primary, | ||
| 109 | + fontSize: 28, | ||
| 110 | + fontWeight: FontWeight.w600, | ||
| 111 | + letterSpacing: 0, | ||
| 112 | + ), | ||
| 113 | + ), | ||
| 114 | + const SizedBox(height: 6), | ||
| 115 | + Text( | ||
| 116 | + _planSubtitleOhos(yearProduct), | ||
| 117 | + textAlign: TextAlign.center, | ||
| 118 | + style: const TextStyle( | ||
| 119 | + color: Color(0xFF78787D), | ||
| 120 | + fontSize: 12, | ||
| 121 | + letterSpacing: 0, | ||
| 122 | + ), | ||
| 101 | ), | 123 | ), |
| 102 | - ), | ||
| 103 | - const SizedBox(height: 6), | ||
| 104 | - Text( | ||
| 105 | - _displayPrice(yearInfo), | ||
| 106 | - textAlign: TextAlign.center, | ||
| 107 | - style: TextStyle( | ||
| 108 | - color: context.colors.primary, | ||
| 109 | - fontSize: 28, | ||
| 110 | - fontWeight: FontWeight.w600, | ||
| 111 | - letterSpacing: 0, | 124 | + ], |
| 125 | + ); | ||
| 126 | + } else if (isIOS()) { | ||
| 127 | + final yearInfo = | ||
| 128 | + controller.yearlyProductAppleInfo.value; | ||
| 129 | + return (yearInfo == null || yearProduct == null) | ||
| 130 | + ? const SizedBox( | ||
| 131 | + child: CircularProgressIndicator()) | ||
| 132 | + : Column( | ||
| 133 | + children: [ | ||
| 134 | + Text( | ||
| 135 | + context.l10n.originalPricePerYear( | ||
| 136 | + _originDisplayPrice(yearInfo)), | ||
| 137 | + textAlign: TextAlign.center, | ||
| 138 | + style: const TextStyle( | ||
| 139 | + color: Color(0xFF78787D), | ||
| 140 | + fontSize: 16, | ||
| 141 | + decoration: | ||
| 142 | + TextDecoration.lineThrough, | ||
| 143 | + decorationColor: Color(0xFF78787D), | ||
| 144 | + letterSpacing: 0, | ||
| 145 | + ), | ||
| 112 | ), | 146 | ), |
| 113 | - ), | ||
| 114 | - const SizedBox(height: 6), | ||
| 115 | - Text( | ||
| 116 | - _planSubtitle(yearProduct, yearInfo), | ||
| 117 | - textAlign: TextAlign.center, | ||
| 118 | - style: const TextStyle( | ||
| 119 | - color: Color(0xFF78787D), | ||
| 120 | - fontSize: 12, | ||
| 121 | - letterSpacing: 0, | 147 | + const SizedBox(height: 6), |
| 148 | + Text( | ||
| 149 | + _displayPrice(yearInfo), | ||
| 150 | + textAlign: TextAlign.center, | ||
| 151 | + style: TextStyle( | ||
| 152 | + color: context.colors.primary, | ||
| 153 | + fontSize: 28, | ||
| 154 | + fontWeight: FontWeight.w600, | ||
| 155 | + letterSpacing: 0, | ||
| 156 | + ), | ||
| 122 | ), | 157 | ), |
| 123 | - ), | ||
| 124 | - ], | ||
| 125 | - ); | 158 | + const SizedBox(height: 6), |
| 159 | + Text( | ||
| 160 | + _planSubtitle(yearProduct, yearInfo), | ||
| 161 | + textAlign: TextAlign.center, | ||
| 162 | + style: const TextStyle( | ||
| 163 | + color: Color(0xFF78787D), | ||
| 164 | + fontSize: 12, | ||
| 165 | + letterSpacing: 0, | ||
| 166 | + ), | ||
| 167 | + ), | ||
| 168 | + ], | ||
| 169 | + ); | ||
| 170 | + } else { | ||
| 171 | + return SizedBox(); | ||
| 172 | + } | ||
| 126 | }), | 173 | }), |
| 127 | ], | 174 | ], |
| 128 | ), | 175 | ), |
| @@ -186,6 +233,22 @@ class MembershipOfferView extends GetView<MembershipOfferController> { | @@ -186,6 +233,22 @@ class MembershipOfferView extends GetView<MembershipOfferController> { | ||
| 186 | return appleInfo.originPriceDescription; | 233 | return appleInfo.originPriceDescription; |
| 187 | } | 234 | } |
| 188 | 235 | ||
| 236 | + String _originDisplayPriceOhos(PayProduct product) { | ||
| 237 | + final inflated = (product.price ?? 0) / 100.0 * 1.2; | ||
| 238 | + return '¥${inflated.toStringAsFixed(2)}'; | ||
| 239 | + } | ||
| 240 | + | ||
| 241 | + String _displayPriceOhos(PayProduct product) { | ||
| 242 | + final actual = (product.price ?? 0) / 100.0; | ||
| 243 | + return '¥${actual.toStringAsFixed(2)}'; | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | + String _planSubtitleOhos(PayProduct product) { | ||
| 247 | + final monthly = (product.price ?? 0) / 100.0 / 12; | ||
| 248 | + | ||
| 249 | + return l10n.purchaseMonthlyUnitPrice(monthly.toStringAsFixed(2)); | ||
| 250 | + } | ||
| 251 | + | ||
| 189 | String? _nonEmpty(String? value) { | 252 | String? _nonEmpty(String? value) { |
| 190 | final trimmed = value?.trim(); | 253 | final trimmed = value?.trim(); |
| 191 | if (trimmed == null || trimmed.isEmpty) return null; | 254 | if (trimmed == null || trimmed.isEmpty) return null; |
| 1 | +import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; | ||
| 1 | import 'package:doublefeel_flutter/data/models/pay/apple_pay_order_response.dart'; | 2 | import 'package:doublefeel_flutter/data/models/pay/apple_pay_order_response.dart'; |
| 2 | 3 | ||
| 3 | import '../../result/app_result.dart'; | 4 | import '../../result/app_result.dart'; |
| @@ -17,8 +18,12 @@ class PayApi { | @@ -17,8 +18,12 @@ class PayApi { | ||
| 17 | final response = await _dioClient.dio.get( | 18 | final response = await _dioClient.dio.get( |
| 18 | ApiPaths.paymentProducts, | 19 | ApiPaths.paymentProducts, |
| 19 | queryParameters: { | 20 | queryParameters: { |
| 20 | - 'product_type': productType ?? 1, | ||
| 21 | - 'product_channel': 2, | 21 | + 'product_type': productType ?? ProductType.vip.value, |
| 22 | + 'product_channel': isOhos() | ||
| 23 | + ? ProductChannel.huawei.value | ||
| 24 | + : (isIOS() | ||
| 25 | + ? ProductChannel.apple.value | ||
| 26 | + : ProductChannel.self.value), | ||
| 22 | }, | 27 | }, |
| 23 | ); | 28 | ); |
| 24 | return PayProductListResponse.fromJson( | 29 | return PayProductListResponse.fromJson( |
| @@ -35,8 +40,8 @@ class PayApi { | @@ -35,8 +40,8 @@ class PayApi { | ||
| 35 | ApiPaths.paymentOrder, | 40 | ApiPaths.paymentOrder, |
| 36 | data: CreatePayOrderRequest( | 41 | data: CreatePayOrderRequest( |
| 37 | productId: productId, | 42 | productId: productId, |
| 38 | - productChannel: 2, | ||
| 39 | - paymentChannel: 3, | 43 | + productChannel: ProductChannel.apple.value, |
| 44 | + paymentChannel: PaymentChannel.apple.value, | ||
| 40 | ).toJson(), | 45 | ).toJson(), |
| 41 | ); | 46 | ); |
| 42 | return ApplePayOrderResponse.fromJson( | 47 | return ApplePayOrderResponse.fromJson( |
| @@ -66,6 +71,28 @@ class PayApi { | @@ -66,6 +71,28 @@ class PayApi { | ||
| 66 | ); | 71 | ); |
| 67 | } | 72 | } |
| 68 | 73 | ||
| 74 | + Future<AppResult<ApplePayOrderResponse>> createCustomOrder({ | ||
| 75 | + required int productId, | ||
| 76 | + required int paymentChannel, | ||
| 77 | + required int productChannel, | ||
| 78 | + }) { | ||
| 79 | + return safeCall( | ||
| 80 | + call: () async { | ||
| 81 | + final response = await _dioClient.dio.post( | ||
| 82 | + ApiPaths.paymentOrder, | ||
| 83 | + data: CreatePayOrderRequest( | ||
| 84 | + productId: productId, | ||
| 85 | + paymentChannel: paymentChannel, | ||
| 86 | + productChannel: productChannel, | ||
| 87 | + ).toJson(), | ||
| 88 | + ); | ||
| 89 | + return ApplePayOrderResponse.fromJson( | ||
| 90 | + response.data as Map<String, dynamic>, | ||
| 91 | + ); | ||
| 92 | + }, | ||
| 93 | + ); | ||
| 94 | + } | ||
| 95 | + | ||
| 69 | Future<AppResult<SubscriptionProductListResponse>> getSubscriptionList() { | 96 | Future<AppResult<SubscriptionProductListResponse>> getSubscriptionList() { |
| 70 | return safeCall( | 97 | return safeCall( |
| 71 | call: () async { | 98 | call: () async { |
| @@ -89,3 +116,44 @@ class PayApi { | @@ -89,3 +116,44 @@ class PayApi { | ||
| 89 | ); | 116 | ); |
| 90 | } | 117 | } |
| 91 | } | 118 | } |
| 119 | + | ||
| 120 | +enum ProductType { | ||
| 121 | + /// 会员 (1) | ||
| 122 | + vip(1), | ||
| 123 | + | ||
| 124 | + /// 订阅会员 (10) | ||
| 125 | + subscriptionVip(10); | ||
| 126 | + | ||
| 127 | + const ProductType(this.value); | ||
| 128 | + final int value; | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +/// 商品渠道 | ||
| 132 | +enum ProductChannel { | ||
| 133 | + /// 自营 (1) | ||
| 134 | + self(1), | ||
| 135 | + | ||
| 136 | + /// 苹果 (2) | ||
| 137 | + apple(2), | ||
| 138 | + | ||
| 139 | + /// 华为 (3) | ||
| 140 | + huawei(3); | ||
| 141 | + | ||
| 142 | + const ProductChannel(this.value); | ||
| 143 | + final int value; | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +/// 支付渠道 | ||
| 147 | +enum PaymentChannel { | ||
| 148 | + /// 微信 (1) | ||
| 149 | + wechat(1), | ||
| 150 | + | ||
| 151 | + /// 支付宝 (2) | ||
| 152 | + alipay(2), | ||
| 153 | + | ||
| 154 | + /// 苹果 (3) | ||
| 155 | + apple(3); | ||
| 156 | + | ||
| 157 | + const PaymentChannel(this.value); | ||
| 158 | + final int value; | ||
| 159 | +} |
| 1 | class ApplePayOrderResponse { | 1 | class ApplePayOrderResponse { |
| 2 | - const ApplePayOrderResponse({this.orderInfo}); | 2 | + const ApplePayOrderResponse({this.orderInfo, this.prepayData}); |
| 3 | 3 | ||
| 4 | final ApplePayOrderInfo? orderInfo; | 4 | final ApplePayOrderInfo? orderInfo; |
| 5 | + final String? prepayData; | ||
| 5 | 6 | ||
| 6 | factory ApplePayOrderResponse.fromJson(Map<String, dynamic> json) { | 7 | factory ApplePayOrderResponse.fromJson(Map<String, dynamic> json) { |
| 7 | return ApplePayOrderResponse( | 8 | return ApplePayOrderResponse( |
| 9 | + prepayData: json['prepay_data'] as String?, | ||
| 8 | orderInfo: json['order_info'] == null | 10 | orderInfo: json['order_info'] == null |
| 9 | ? null | 11 | ? null |
| 10 | : ApplePayOrderInfo.fromJson( | 12 | : ApplePayOrderInfo.fromJson( |
| @@ -15,6 +17,7 @@ class ApplePayOrderResponse { | @@ -15,6 +17,7 @@ class ApplePayOrderResponse { | ||
| 15 | 17 | ||
| 16 | Map<String, dynamic> toJson() { | 18 | Map<String, dynamic> toJson() { |
| 17 | final val = <String, dynamic>{}; | 19 | final val = <String, dynamic>{}; |
| 20 | + if (prepayData != null) val['prepay_data'] = prepayData; | ||
| 18 | if (orderInfo != null) val['order_info'] = orderInfo!.toJson(); | 21 | if (orderInfo != null) val['order_info'] = orderInfo!.toJson(); |
| 19 | return val; | 22 | return val; |
| 20 | } | 23 | } |
| @@ -1188,7 +1188,7 @@ | @@ -1188,7 +1188,7 @@ | ||
| 1188 | "annualMemberDiscounts": "年度会员优惠", | 1188 | "annualMemberDiscounts": "年度会员优惠", |
| 1189 | "specialOffers": "优惠", | 1189 | "specialOffers": "优惠", |
| 1190 | "currentPrice": "现价", | 1190 | "currentPrice": "现价", |
| 1191 | - "originalPrice": "原价{price}", | 1191 | + "originalPrice": "原价{price}/年", |
| 1192 | "@originalPrice": { | 1192 | "@originalPrice": { |
| 1193 | "description": "原价标签", | 1193 | "description": "原价标签", |
| 1194 | "placeholders": { | 1194 | "placeholders": { |
| @@ -667,7 +667,7 @@ | @@ -667,7 +667,7 @@ | ||
| 667 | "annualMemberDiscounts": "年度会员优惠", | 667 | "annualMemberDiscounts": "年度会员优惠", |
| 668 | "specialOffers": "优惠", | 668 | "specialOffers": "优惠", |
| 669 | "currentPrice": "现价", | 669 | "currentPrice": "现价", |
| 670 | - "originalPrice": "原价{price}", | 670 | + "originalPrice": "原价{price}/年", |
| 671 | "freeRedemptionOffer": "免费兑换优惠", | 671 | "freeRedemptionOffer": "免费兑换优惠", |
| 672 | "cellPhoneNumber": "手机号", | 672 | "cellPhoneNumber": "手机号", |
| 673 | "todayOnWeeklyCalendar": "今", | 673 | "todayOnWeeklyCalendar": "今", |
| @@ -667,7 +667,7 @@ | @@ -667,7 +667,7 @@ | ||
| 667 | "annualMemberDiscounts": "年度會員優惠", | 667 | "annualMemberDiscounts": "年度會員優惠", |
| 668 | "specialOffers": "優惠", | 668 | "specialOffers": "優惠", |
| 669 | "currentPrice": "現價", | 669 | "currentPrice": "現價", |
| 670 | - "originalPrice": "原價{price}", | 670 | + "originalPrice": "原價{price}/年", |
| 671 | "freeRedemptionOffer": "免費兌換優惠", | 671 | "freeRedemptionOffer": "免費兌換優惠", |
| 672 | "cellPhoneNumber": "手機號", | 672 | "cellPhoneNumber": "手機號", |
| 673 | "todayOnWeeklyCalendar": "今", | 673 | "todayOnWeeklyCalendar": "今", |
| @@ -4132,7 +4132,7 @@ abstract class AppLocalizations { | @@ -4132,7 +4132,7 @@ abstract class AppLocalizations { | ||
| 4132 | /// 原价标签 | 4132 | /// 原价标签 |
| 4133 | /// | 4133 | /// |
| 4134 | /// In zh, this message translates to: | 4134 | /// In zh, this message translates to: |
| 4135 | - /// **'原价{price}'** | 4135 | + /// **'原价{price}/年'** |
| 4136 | String originalPrice(String price); | 4136 | String originalPrice(String price); |
| 4137 | 4137 | ||
| 4138 | /// No description provided for @freeRedemptionOffer. | 4138 | /// No description provided for @freeRedemptionOffer. |
| @@ -2205,7 +2205,7 @@ class AppLocalizationsZh extends AppLocalizations { | @@ -2205,7 +2205,7 @@ class AppLocalizationsZh extends AppLocalizations { | ||
| 2205 | 2205 | ||
| 2206 | @override | 2206 | @override |
| 2207 | String originalPrice(String price) { | 2207 | String originalPrice(String price) { |
| 2208 | - return '原价$price'; | 2208 | + return '原价$price/年'; |
| 2209 | } | 2209 | } |
| 2210 | 2210 | ||
| 2211 | @override | 2211 | @override |
| @@ -5007,7 +5007,7 @@ class AppLocalizationsZhHans extends AppLocalizationsZh { | @@ -5007,7 +5007,7 @@ class AppLocalizationsZhHans extends AppLocalizationsZh { | ||
| 5007 | 5007 | ||
| 5008 | @override | 5008 | @override |
| 5009 | String originalPrice(String price) { | 5009 | String originalPrice(String price) { |
| 5010 | - return '原价$price'; | 5010 | + return '原价$price/年'; |
| 5011 | } | 5011 | } |
| 5012 | 5012 | ||
| 5013 | @override | 5013 | @override |
| @@ -7809,7 +7809,7 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { | @@ -7809,7 +7809,7 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { | ||
| 7809 | 7809 | ||
| 7810 | @override | 7810 | @override |
| 7811 | String originalPrice(String price) { | 7811 | String originalPrice(String price) { |
| 7812 | - return '原價$price'; | 7812 | + return '原價$price/年'; |
| 7813 | } | 7813 | } |
| 7814 | 7814 | ||
| 7815 | @override | 7815 | @override |
-
Please register or login to post a comment