From 11a73d19ae64d34b3c94c7c4f2c16be740c7adb8 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Wed, 22 Apr 2026 11:39:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E4=BC=9A=E5=91=98=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=8D=8F=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/translations/language/en_us.dart | 6 + lib/core/translations/language/zh_cn.dart | 6 + lib/core/translations/language/zh_hk.dart | 6 + lib/core/translations/language/zh_tw.dart | 6 + lib/core/utils/doc_url_helper.dart | 22 ++- .../member_service_agreement_dialog.dart | 155 ++++++++++++++++++ .../controllers/goods_base_controller.dart | 10 ++ 7 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 lib/modules/common/widgets/member_service_agreement_dialog.dart diff --git a/lib/core/translations/language/en_us.dart b/lib/core/translations/language/en_us.dart index 78be4ed9b..a6bf4f86b 100644 --- a/lib/core/translations/language/en_us.dart +++ b/lib/core/translations/language/en_us.dart @@ -114,6 +114,12 @@ const Map enUS = { ". By clicking \"Agree and Enter\", you indicate that you have fully read and agree to the above agreements. Clicking \"Disagree\" will exit the application.", // ,点击"同意并进入"表示您已充分阅读并同意上述协议。点击"不同意"将退出应用。 "disagree": "Disagree", // 不同意 "agreeAndEnter": "Agree and Enter", // 同意并进入 + "agreeAndContinue": "Agree and Continue", + "memberServiceAgreement": "Membership Service Agreement", + "memberServiceAgreementTitle": "Membership Service Agreement", + "memberServiceAgreementIntro": "To provide you with better membership services, please read carefully before subscribing: ", + "memberServiceAgreementOutro": ". By clicking \"Agree and Continue\", you confirm that you have fully read and accept the above agreement.", + "memberAgreementRejectedTip": "You must agree to the Membership Service Agreement to continue.", // 首页 "deviceStatus": "Device Status", diff --git a/lib/core/translations/language/zh_cn.dart b/lib/core/translations/language/zh_cn.dart index b15441d91..03d97f343 100644 --- a/lib/core/translations/language/zh_cn.dart +++ b/lib/core/translations/language/zh_cn.dart @@ -110,6 +110,12 @@ const Map zhCN = { ",点击'同意并进入'表示您已充分阅读并同意上述协议。点击'不同意'将退出应用。", // ,点击"同意并进入"表示您已充分阅读并同意上述协议。点击"不同意"将退出应用。 "disagree": "不同意", // 不同意 "agreeAndEnter": "同意并进入", // 同意并进入 + "agreeAndContinue": "同意并继续", + "memberServiceAgreement": "《会员服务协议》", + "memberServiceAgreementTitle": "会员服务协议", + "memberServiceAgreementIntro": "为给您提供更优质的会员服务,请您在订阅会员前仔细阅读", + "memberServiceAgreementOutro": "。点击\"同意并继续\"表示您已充分阅读并同意上述协议。", + "memberAgreementRejectedTip": "需同意《会员服务协议》后才能继续支付", // 首页 "deviceStatus": "设备状态", diff --git a/lib/core/translations/language/zh_hk.dart b/lib/core/translations/language/zh_hk.dart index 3257774fb..63528aa87 100644 --- a/lib/core/translations/language/zh_hk.dart +++ b/lib/core/translations/language/zh_hk.dart @@ -110,6 +110,12 @@ const Map zhHK = { "點擊'同意並進入'表示您已充分閱讀並同意上述協議。點擊'不同意'將退出應用。", // ,点击"同意并进入"表示您已充分阅读并同意上述协议。点击"不同意"将退出应用。 "disagree": "不同意", // 不同意 "agreeAndEnter": "同意並進入", // 同意并进入 + "agreeAndContinue": "同意並繼續", + "memberServiceAgreement": "《會員服務協議》", + "memberServiceAgreementTitle": "會員服務協議", + "memberServiceAgreementIntro": "為向您提供更優質的會員服務,請您在訂閱會員前仔細閱讀", + "memberServiceAgreementOutro": "。點擊\"同意並繼續\"表示您已充分閱讀並同意上述協議。", + "memberAgreementRejectedTip": "需同意《會員服務協議》後才能繼續支付", // 首页 "deviceStatus": "設備狀態", diff --git a/lib/core/translations/language/zh_tw.dart b/lib/core/translations/language/zh_tw.dart index 5713c3074..17cdb50a9 100644 --- a/lib/core/translations/language/zh_tw.dart +++ b/lib/core/translations/language/zh_tw.dart @@ -110,6 +110,12 @@ const Map zhTW = { "點擊'同意並進入'表示您已充分閱讀並同意上述協議。點擊'不同意'將退出應用。", // ,点击"同意并进入"表示您已充分阅读并同意上述协议。点击"不同意"将退出应用。 "disagree": "不同意", // 不同意 "agreeAndEnter": "同意並進入", // 同意并进入 + "agreeAndContinue": "同意並繼續", + "memberServiceAgreement": "《會員服務協議》", + "memberServiceAgreementTitle": "會員服務協議", + "memberServiceAgreementIntro": "為向您提供更優質的會員服務,請您在訂閱會員前仔細閱讀", + "memberServiceAgreementOutro": "。點擊\"同意並繼續\"表示您已充分閱讀並同意上述協議。", + "memberAgreementRejectedTip": "需同意《會員服務協議》後才能繼續支付", // 首页 "deviceStatus": "設備狀態", diff --git a/lib/core/utils/doc_url_helper.dart b/lib/core/utils/doc_url_helper.dart index 951ac88bf..38f3bb8a4 100644 --- a/lib/core/utils/doc_url_helper.dart +++ b/lib/core/utils/doc_url_helper.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import '../../data/services/network/dio_manager.dart'; /// 根据当前语言返回 doc 目录名,如 "zh_cn"、"en"、"fr" String docLangPath() { @@ -18,10 +19,23 @@ String docLangPath() { return supported.contains(lang) ? lang : 'en'; } -/// 构建隐私政策 URL +/// 业务服务器上的法律文档目录前缀 +String _legalDocBase() => '${DioManager().baseUrl}/docs/legal'; + +/// 当前语言对应的协议文件后缀(zh / en) +String _legalDocLang() { + final lang = Get.locale?.languageCode ?? 'en'; + return lang == 'zh' ? 'zh' : 'en'; +} + +/// 构建隐私政策 URL(托管在业务服务器上) String privacyPolicyUrl() => - 'https://www.voitrans.net/doc/${docLangPath()}/privacy_policy.html'; + '${_legalDocBase()}/privacy_policy_${_legalDocLang()}.html'; -/// 构建用户协议 URL +/// 构建用户协议 URL(托管在业务服务器上) String userAgreementUrl() => - 'https://www.voitrans.net/doc/${docLangPath()}/user_agreement.html'; + '${_legalDocBase()}/user_agreement_${_legalDocLang()}.html'; + +/// 构建会员服务协议 URL(托管在业务服务器上) +String memberServiceAgreementUrl() => + '${_legalDocBase()}/member_service_agreement_${_legalDocLang()}.html'; diff --git a/lib/modules/common/widgets/member_service_agreement_dialog.dart b/lib/modules/common/widgets/member_service_agreement_dialog.dart new file mode 100644 index 000000000..cd54c85bf --- /dev/null +++ b/lib/modules/common/widgets/member_service_agreement_dialog.dart @@ -0,0 +1,155 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import '../webview_page.dart'; +import '../../../core/utils/doc_url_helper.dart'; + +/// 会员服务协议确认弹窗 +/// 在用户进入支付(充值/购买会员)前必须显式同意 +class MemberServiceAgreementDialog extends StatelessWidget { + final VoidCallback? onAccept; // 同意回调 + final VoidCallback? onReject; // 拒绝回调 + final bool canDismiss; // 是否可通过返回键关闭 + + const MemberServiceAgreementDialog({ + super.key, + this.onAccept, + this.onReject, + this.canDismiss = true, + }); + + /// 显示会员服务协议弹窗,返回值为 true 表示同意,false / null 表示拒绝或取消 + static Future show({bool canDismiss = true}) async { + final result = await Get.dialog( + MemberServiceAgreementDialog( + canDismiss: canDismiss, + onAccept: () => Get.back(result: true), + onReject: () => Get.back(result: false), + ), + barrierDismissible: canDismiss, + ); + return result == true; + } + + @override + Widget build(BuildContext context) { + final bool isDarkMode = Get.theme.brightness == Brightness.dark; + + return PopScope( + canPop: canDismiss, + child: Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.r), + ), + backgroundColor: isDarkMode ? Colors.grey[800] : Colors.white, + child: Container( + padding: EdgeInsets.all(20.w), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'memberServiceAgreementTitle'.tr, // 会员服务协议 + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.bold, + color: isDarkMode ? Colors.white : Colors.black, + ), + ), + SizedBox(height: 16.h), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Text.rich( + TextSpan( + children: [ + TextSpan( + text: 'memberServiceAgreementIntro'.tr, + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode + ? Colors.white70 + : Colors.black87, + ), + ), + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: GestureDetector( + onTap: () { + Get.to(() => WebViewPage( + url: memberServiceAgreementUrl(), + title: 'memberServiceAgreement'.tr, + )); + }, + child: Text( + 'memberServiceAgreement'.tr, // 《会员服务协议》 + style: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF3B82F6), + ), + ), + ), + ), + TextSpan( + text: 'memberServiceAgreementOutro'.tr, + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.white70 : Colors.black87, + ), + ), + ], + ), + textAlign: TextAlign.center, + ), + ), + SizedBox(height: 20.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: () { + if (onReject != null) { + onReject!(); + } else { + Get.back(result: false); + } + }, + style: TextButton.styleFrom( + foregroundColor: isDarkMode + ? Colors.grey[400] + : Colors.black54, + ), + child: Text( + 'disagree'.tr, // 不同意 + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.black54, + ), + ), + ), + ElevatedButton( + onPressed: () { + if (onAccept != null) { + onAccept!(); + } else { + Get.back(result: true); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF0066CC), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.r), + ), + elevation: isDarkMode ? 4 : 2, + padding: EdgeInsets.symmetric( + horizontal: 16.w, vertical: 10.h), + ), + child: Text('agreeAndContinue'.tr), // 同意并继续 + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/modules/goods/controllers/goods_base_controller.dart b/lib/modules/goods/controllers/goods_base_controller.dart index f7aee8219..5f4528e35 100644 --- a/lib/modules/goods/controllers/goods_base_controller.dart +++ b/lib/modules/goods/controllers/goods_base_controller.dart @@ -10,6 +10,7 @@ import '../../../data/services/network/api.dart'; import 'package:flutter/foundation.dart'; import 'dart:async'; import 'package:app_links/app_links.dart'; +import '../../common/widgets/member_service_agreement_dialog.dart'; /// goods 模块基础控制器,封装通用的商品加载与支付流程 abstract class GoodsBaseController extends GetxController { @@ -97,6 +98,15 @@ abstract class GoodsBaseController extends GetxController { method = enabled.first; } + // 3.5 充值前必须显式同意《会员服务协议》(合规要求) + final agreed = await MemberServiceAgreementDialog.show(); + if (!agreed) { + Get.snackbar('tip'.tr, 'memberAgreementRejectedTip'.tr, + snackPosition: SnackPosition.BOTTOM, + margin: const EdgeInsets.all(12)); + return; + } + // 4. 根据选择的支付方式执行支付 bool ok = false; String failMsg = 'paymentFailedTryAgain'.tr; // 支付失败,请重试