import 'dart:async'; import 'dart:convert'; import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; import 'package:kaer_with_panels/app/services/api_service/kr_auth_api.dart'; import 'package:kaer_with_panels/app/common/app_run_data.dart'; import 'package:kaer_with_panels/app/model/enum/kr_request_type.dart'; import 'package:kaer_with_panels/app/model/kr_area_code.dart'; import 'package:kaer_with_panels/app/utils/kr_common_util.dart'; import 'package:kaer_with_panels/app/localization/app_translations.dart'; import 'package:kaer_with_panels/app/utils/kr_event_bus.dart'; import 'package:kaer_with_panels/app/services/kr_site_config_service.dart'; import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart'; import 'package:kaer_with_panels/app/utils/kr_secure_storage.dart'; import '../../../localization/kr_language_utils.dart'; import 'package:kaer_with_panels/app/services/kr_subscribe_service.dart'; import 'package:kaer_with_panels/app/modules/hi_user_info/controllers/hi_user_info_controller.dart'; /// 登录类型 enum KRLoginProgressStatus { /// 检查是否注册 kr_check, /// 验证码登陆 kr_loginByCode, /// 密码登陆 kr_loginByPsd, /// 注册发送验证码 kr_registerSendCode, /// 这次设置密码 kr_registerSetPsd, /// 忘记密码发送验证码 kr_forgetPsdSendCode, /// 忘记密码设置密码 kr_forgetPsdSetPsd, } extension KRLoginTypeExt on KRLoginProgressStatus { int get value { switch (this) { case KRLoginProgressStatus.kr_check: return 0; case KRLoginProgressStatus.kr_loginByCode: return 2; case KRLoginProgressStatus.kr_loginByPsd: return 3; case KRLoginProgressStatus.kr_registerSendCode: return 4; case KRLoginProgressStatus.kr_registerSetPsd: return 5; case KRLoginProgressStatus.kr_forgetPsdSendCode: return 6; case KRLoginProgressStatus.kr_forgetPsdSetPsd: return 7; } } } class KRLoginController extends GetxController with GetSingleTickerProviderStateMixin { /// 订阅服务 final KRSubscribeService kr_subscribeService = KRSubscribeService(); /// 是否注册 RxBool kr_isRegistered = false.obs; /// 登陆类型 var kr_loginType = KRLoginType.kr_email.obs; /// 登陆进度状态 var kr_loginStatus = KRLoginProgressStatus.kr_registerSendCode.obs; /// 是否使用密码登录(手机号模式下有效:true=密码登录,false=验证码登录) var kr_isPasswordLogin = true.obs; /// 验证码倒计时 var _countdown = 60; // 倒计时初始值 DateTime? _endTime; // 倒计时结束时间 late Timer _timer; var kr_countdownText = AppTranslations.kr_login.sendCode.obs; /// 是否允许发送验证码 RxBool kr_canSendCode = true.obs; /// 国家编码列表 var kr_cutSeleteCodeIndex = 0.obs; /// 是否加密密码 var kr_obscureText = true.obs; /// 匹配邮箱列表 RxList kr_emailList = [].obs; RxBool kr_isDropdownVisible = false.obs; /// 邮箱历史记录 RxList kr_emailHistory = [].obs; /// 定位 final LayerLink kr_layerLink = LayerLink(); OverlayEntry? overlayEntry; // 悬浮框 bool isDropdownVisible = false; // 控制悬浮框显示状态 /// 动画 late AnimationController animationController; late Animation animation; var height = 100.0.obs; /// 账号编辑控制器 late TextEditingController accountController = TextEditingController(); /// 验证码编辑控制器 late TextEditingController codeController = TextEditingController(); /// 密码编辑控制器 late TextEditingController psdController = TextEditingController(); /// 密码编辑控制器 late TextEditingController agPsdController = TextEditingController(); var kr_accountHasText = false.obs; var kr_codeHasText = false.obs; var kr_psdHasText = false.obs; var kr_agPsdHasText = false.obs; // 添加邀请码相关控制 final TextEditingController inviteCodeController = TextEditingController(); final RxBool kr_inviteCodeHasText = false.obs; // 添加 FocusNode late FocusNode kr_accountFocusNode; late FocusNode kr_passwordFocusNode; late FocusNode kr_confirmPasswordFocusNode; late FocusNode kr_codeFocusNode; late FocusNode kr_inviteCodeFocusNode; // 添加获取按钮文本的方法 String kr_getNextBtnText() { switch (kr_loginStatus.value) { case KRLoginProgressStatus.kr_check: return AppTranslations.kr_login.passwordLogin; // 显示"登录" case KRLoginProgressStatus.kr_loginByCode: return AppTranslations.kr_login.passwordLogin; // 已废弃,保留以防兼容性问题 case KRLoginProgressStatus.kr_loginByPsd: return AppTranslations.kr_login.passwordLogin; case KRLoginProgressStatus.kr_registerSendCode: return AppTranslations.kr_login.next; case KRLoginProgressStatus.kr_registerSetPsd: return AppTranslations.kr_login.passwordLogin; case KRLoginProgressStatus.kr_forgetPsdSendCode: return AppTranslations.kr_login.next; case KRLoginProgressStatus.kr_forgetPsdSetPsd: return AppTranslations.kr_login.setAndLogin; } } @override void onInit() { super.onInit(); // 1. 检查并获取路由参数 if (Get.arguments != null && Get.arguments is Map) { final String? entry = Get.arguments['entry']; print('entry: $entry'); // 2. 根据 entry 的值设置初始的 kr_loginStatus if (entry == 'forget_psd') { // 如果是从“忘记密码”进入,直接设置为“忘记密码-发送验证码”状态 kr_loginStatus.value = KRLoginProgressStatus.kr_forgetPsdSendCode; } else if (entry == 'bind_email') { // 如果是从“绑定邮箱”进入,直接设置为“注册-发送验证码”状态 kr_loginStatus.value = KRLoginProgressStatus.kr_registerSetPsd; } else if (entry == 'login') { // 登录 kr_loginStatus.value = KRLoginProgressStatus.kr_loginByPsd; } // 如果没有匹配的 entry 值,则保持默认的 kr_loginByPsd 状态 } // 初始化计时器 _timer = Timer(Duration.zero, () {}); animationController = AnimationController( duration: Duration(milliseconds: 500), vsync: this, ); animation = Tween(begin: 300.0, end: 300.0).animate( CurvedAnimation(parent: animationController, curve: Curves.easeInOut), )..addListener(() { height.value = animation.value; }); // 初始化 FocusNode kr_accountFocusNode = FocusNode(); kr_passwordFocusNode = FocusNode(); kr_confirmPasswordFocusNode = FocusNode(); kr_codeFocusNode = FocusNode(); kr_inviteCodeFocusNode = FocusNode(); // 监听 kr_loginStatus 的变化 ever(kr_loginStatus, (status) { switch (status) { case KRLoginProgressStatus.kr_check: kr_isDropdownVisible.value = true; accountController.clear(); codeController.clear(); psdController.clear(); agPsdController.clear(); inviteCodeController.clear(); break; case KRLoginProgressStatus.kr_loginByCode: kr_isDropdownVisible.value = false; break; case KRLoginProgressStatus.kr_loginByPsd: kr_isDropdownVisible.value = false; break; case KRLoginProgressStatus.kr_registerSendCode: kr_isDropdownVisible.value = false; break; case KRLoginProgressStatus.kr_registerSetPsd: kr_isDropdownVisible.value = false; break; case KRLoginProgressStatus.kr_forgetPsdSendCode: kr_isDropdownVisible.value = false; break; case KRLoginProgressStatus.kr_forgetPsdSetPsd: kr_isDropdownVisible.value = false; break; } }); // accountController 监听器(仅支持邮箱) accountController.addListener(() { String input = accountController.text.trim(); // 延迟执行状态更新 Future.microtask(() { // 始终保持邮箱类型 kr_loginType.value = KRLoginType.kr_email; // 更新邮箱建议列表 kr_emailList.value = kr_generateAndSortEmailList(input); kr_accountHasText.value = input.isNotEmpty; }); }); /// 验证码 codeController.addListener(() { kr_codeHasText.value = !codeController.text.isEmpty; }); /// 密码 psdController.addListener(() { kr_psdHasText.value = !psdController.text.isEmpty; }); /// 密码 agPsdController.addListener(() { kr_agPsdHasText.value = !agPsdController.text.isEmpty; }); // 添加邀请码输入监听 inviteCodeController.addListener(() { kr_inviteCodeHasText.value = inviteCodeController.text.isNotEmpty; }); // 语言变化时更新所有翻译文本 /*ever(KRLanguageUtils.kr_language, (_) { if (kr_canSendCode.value) { kr_countdownText.value = ""; kr_countdownText.value = AppTranslations.kr_login.sendCode; } });*/ kr_initFocus(); // 加载邮箱历史记录 KRSecureStorage() .kr_readData(key: KRSecureStorage.EMAIL_HISTORY_KEY) .then((historyString) { if (historyString != null && historyString.isNotEmpty) { try { final List decodedList = jsonDecode(historyString); kr_emailHistory.assignAll(decodedList.cast()); } catch (e) { print('Error decoding email history: $e'); } } }); } // 判断是否是手机号 bool _isNumeric(String input) { final numericRegex = RegExp(r'^\d+$'); // 匹配纯数字 return numericRegex.hasMatch(input); } /// 直接登录(不再检查是否注册) void kr_check() async { if (accountController.text.isEmpty) { KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterAccount); return; } if (!validateEmail(accountController.text.trim())) { KRCommonUtil.kr_showToast('请输入有效的邮箱地址'); return; } if (psdController.text.isEmpty) { KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterPassword); return; } // 直接调用登录 kr_login(); } /// 发送验证码(仅支持邮箱) void kr_sendCode() async { if (accountController.text.isEmpty) { KRCommonUtil.kr_showToast('请输入邮箱'); return; } if (!validateEmail(accountController.text.trim())) { KRCommonUtil.kr_showToast('请输入有效的邮箱地址'); return; } int type; final check = await KRAuthApi().kr_isRegister(accountController.text); final result = check.fold((l) { KRCommonUtil.kr_showToast(l.msg); return null; }, (isRegistered) { print('isRegistered $isRegistered'); return isRegistered ? 2 : 1; }); if (result == null) return; type = result; final either = await KRAuthApi().kr_sendCode(accountController.text, type); either.fold((l) { KRCommonUtil.kr_showToast(l.msg); }, (r) async { HIDialog.show( title: '*重要提示', message: '验证邮件已发送至邮箱,如无法找到,请检查垃圾邮件箱或营销邮件箱。', ); _startCountdown(); }); } /// 开始登录(仅支持邮箱+密码) void kr_login() async { if (psdController.text.isEmpty) { KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterPassword); return; } final either = await KRAuthApi().kr_login(accountController.text, psdController.text); either.fold((l) { KRCommonUtil.kr_showToast(l.msg); }, (r) async { _saveLoginData(r); }); } /// 开始注册(仅支持邮箱,验证码和邀请码可选) void kr_register() async { // 验证邮箱 final email = accountController.text.trim(); if (email.isEmpty) { KRCommonUtil.kr_showToast('请输入邮箱'); return; } if (!validateEmail(email)) { KRCommonUtil.kr_showToast('请输入有效的邮箱地址'); return; } // if (psdController.text.isEmpty) { // KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterPassword); // return; // } // if (agPsdController.text.isEmpty) { // KRCommonUtil.kr_showToast(AppTranslations.kr_login.reenterPassword); // return; // } // if (psdController.text != agPsdController.text) { // KRCommonUtil.kr_showToast(AppTranslations.kr_login.passwordMismatch); // return; // } // 检查是否需要验证码(基于站点配置) // final siteConfig = KRSiteConfigService(); // final needVerification = siteConfig.isEmailVerificationEnabled() || // siteConfig.isRegisterVerificationEnabled(); if (codeController.text.isEmpty) { KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterCode); return; } // 定义注册请求逻辑 Future performRegister() async { final either = await KRAuthApi().kr_register( accountController.text, psdController.text, code: codeController.text.isEmpty ? null : codeController.text, inviteCode: inviteCodeController.text.isEmpty ? null : inviteCodeController.text, ); either.fold( (l) { HIDialog.show( message: l.msg, preventBackDismiss: true, confirmText: '确定', ); return; }, (r) async { _saveLoginData(r); KRCommonUtil.kr_showToast('登录成功'); }, ); } // 检查是否已有订阅 final subscriptionResult = await KRAuthApi().kr_checkSubscription(accountController.text); subscriptionResult.fold( (error) { KRCommonUtil.kr_showToast(error.msg); }, (isFullySubscribed) async { if (isFullySubscribed) { HIDialog.show( message: '当前邮箱已有套餐,继续绑定会丢失设备当前套餐,是否继续?', confirmText: '继续', cancelText: '取消', onConfirm: performRegister, ); } else { await performRegister(); } }, ); } void kr_checkCode() { if (codeController.text.isEmpty) { KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterCode); return; } switch (kr_loginStatus.value) { case KRLoginProgressStatus.kr_check: break; case KRLoginProgressStatus.kr_loginByCode: break; case KRLoginProgressStatus.kr_loginByPsd: break; case KRLoginProgressStatus.kr_registerSendCode: kr_checkVerificationCode(KRLoginProgressStatus.kr_registerSendCode); break; case KRLoginProgressStatus.kr_registerSetPsd: break; case KRLoginProgressStatus.kr_forgetPsdSendCode: kr_checkVerificationCode(KRLoginProgressStatus.kr_forgetPsdSendCode); break; case KRLoginProgressStatus.kr_forgetPsdSetPsd: break; } } /// 验证验证码(仅支持邮箱) void kr_checkVerificationCode(KRLoginProgressStatus status) async { final either = await KRAuthApi().kr_checkVerificationCode( accountController.text, codeController.text, kr_loginStatus.value == KRLoginProgressStatus.kr_registerSendCode ? 2 // 注册验证码类型为2 : 3); // 重置密码验证码类型为3 either.fold((l) { KRCommonUtil.kr_showToast(l.msg); }, (r) async { if (status == KRLoginProgressStatus.kr_registerSendCode) { kr_loginStatus.value = KRLoginProgressStatus.kr_registerSetPsd; } else if (status == KRLoginProgressStatus.kr_forgetPsdSendCode) { kr_loginStatus.value = KRLoginProgressStatus.kr_forgetPsdSetPsd; } }); } /// 忘记密码-设置新密码(仅支持邮箱) void kr_setNewPsdByForgetPsd() async { if (psdController.text.isEmpty) { KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterPassword); return; } if (agPsdController.text.isEmpty) { KRCommonUtil.kr_showToast(AppTranslations.kr_login.reenterPassword); return; } if (psdController.text != agPsdController.text) { KRCommonUtil.kr_showToast(AppTranslations.kr_login.passwordMismatch); return; } final either = await KRAuthApi().kr_setNewPsdByForgetPsd( accountController.text, codeController.text, psdController.text); either.fold((l) { KRCommonUtil.kr_showToast(l.msg); }, (r) async { codeController.clear(); psdController.clear(); kr_loginStatus.value = KRLoginProgressStatus.kr_forgetPsdSetPsd; _saveLoginData(r); }); } /// 开始倒计时 void _startCountdown() { kr_canSendCode.value = false; _endTime = DateTime.now().add(const Duration(seconds: 60)); _timer.cancel(); _timer = Timer.periodic(const Duration(seconds: 1), (timer) { final now = DateTime.now(); if (_endTime != null && _endTime!.isAfter(now)) { final remaining = _endTime!.difference(now).inSeconds; _countdown = remaining; kr_countdownText.value = "${remaining}s"; } else { kr_canSendCode.value = true; kr_countdownText.value = AppTranslations.kr_login.sendCode; _countdown = 60; _endTime = null; timer.cancel(); _onCountdownFinished(); } }); } /// 验证邮箱格式 bool validateEmail(String str) { return RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$') .hasMatch(str); } /// 设置登录数据(仅支持邮箱) void _saveLoginData(String token) { KRAppRunData.getInstance().kr_saveUserInfo( token, accountController.text, ); // 保存邮箱到历史记录 KRSecureStorage() .kr_readData(key: KRSecureStorage.EMAIL_HISTORY_KEY) .then((historyString) async { List history = []; if (historyString != null && historyString.isNotEmpty) { try { final List decodedList = jsonDecode(historyString); history = decodedList.cast(); } catch (e) { print('Error decoding email history for saving: $e'); } } // 确保不重复添加 if (!history.contains(accountController.text)) { history.add(accountController.text); } // 限制历史记录数量,例如只保留最新的5个 if (history.length > 5) { history = history.sublist(history.length - 5); } await KRSecureStorage().kr_saveData( key: KRSecureStorage.EMAIL_HISTORY_KEY, value: jsonEncode(history)); }); kr_loginStatus.value = KRLoginProgressStatus.kr_check; // 登录/注册成功后,发送消息触发订阅服务刷新 // 延迟一小段时间确保登录状态已经完全保存 Future.delayed(Duration(milliseconds: 100), () { KREventBus().kr_sendMessage(KRMessageType.kr_payment); }); final controller = Get.find(); controller.loadDeviceList(); // 登录成功后返回到上一页 Get.back(); } /// 根据输入内容匹配邮箱 List kr_generateAndSortEmailList(String input) { // 常用邮箱域名 List _commonEmailDomains = [ "@gmail.com", "@yahoo.com", "@outlook.com", "@hotmail.com", "@icloud.com", "@aol.com", "@zoho.com", "@protonmail.com", "@qq.com", "@163.com", "@126.com", "@sina.com", "@sohu.com", "@foxmail.com", "@aliyun.com", "@189.cn", "@china.com", ]; // 判断是否是邮箱格式 bool isEmail(String input) { final emailRegex = RegExp(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"); return emailRegex.hasMatch(input); } // 输入过短或者是邮箱格式,直接返回空数组 if (input.length < 2 || isEmail(input)) { return []; } // 处理输入,确保只保留一个 '@' 后的内容 String sanitizedInput = input.contains("@") ? input.substring(0, input.indexOf("@")) : input; // 根据匹配度排序 List emailList = _commonEmailDomains.map((domain) { return "$sanitizedInput$domain"; }).toList(); // 根据用户输入的 @ 后部分对域名进行匹配和排序 String? userDomain = input.contains("@") ? input.split("@").last : null; if (userDomain != null && userDomain.isNotEmpty) { emailList.sort((a, b) { String domainA = a.split("@")[1]; String domainB = b.split("@")[1]; int matchScoreA = domainA.startsWith(userDomain) ? 1 : 0; int matchScoreB = domainB.startsWith(userDomain) ? 1 : 0; // 先比较匹配度,再按照字母顺序排序 if (matchScoreA == matchScoreB) { return domainA.compareTo(domainB); } return matchScoreB.compareTo(matchScoreA); }); } return emailList; } /// 返回 void kr_back() { kr_removeOverlay(); switch (kr_loginStatus.value) { case KRLoginProgressStatus.kr_check: break; case KRLoginProgressStatus.kr_loginByCode: kr_loginStatus.value = KRLoginProgressStatus.kr_check; _resetTimer(); break; case KRLoginProgressStatus.kr_loginByPsd: kr_loginStatus.value = KRLoginProgressStatus.kr_check; break; case KRLoginProgressStatus.kr_registerSendCode: kr_loginStatus.value = KRLoginProgressStatus.kr_check; _resetTimer(); break; case KRLoginProgressStatus.kr_registerSetPsd: kr_loginStatus.value = KRLoginProgressStatus.kr_registerSendCode; psdController.clear(); agPsdController.clear(); break; case KRLoginProgressStatus.kr_forgetPsdSendCode: kr_loginStatus.value = KRLoginProgressStatus.kr_loginByPsd; _resetTimer(); codeController.clear(); psdController.clear(); agPsdController.clear(); break; case KRLoginProgressStatus.kr_forgetPsdSetPsd: kr_loginStatus.value = KRLoginProgressStatus.kr_forgetPsdSendCode; psdController.clear(); agPsdController.clear(); break; } } /// 重置计时器 void _resetTimer() { if (_timer.isActive) { _timer.cancel(); } _countdown = 60; kr_canSendCode.value = true; kr_countdownText.value = AppTranslations.kr_login.sendCode; } void _onCountdownFinished() {} void toggleHeight() { // if (animationController.status == AnimationStatus.completed) { // animationController.reverse(); // } else { // animationController.forward(); // } } @override void onReady() { super.onReady(); // 每次打开登录页面时,重置为登录状态(而不是注册状态) // 这样确保点击"登录/注册"按钮时始终显示登录页面 kr_loginStatus.value = KRLoginProgressStatus.kr_check; } @override void onClose() { kr_removeOverlay(); if (_timer.isActive) { _timer.cancel(); } animationController.dispose(); inviteCodeController.dispose(); kr_accountFocusNode.dispose(); kr_passwordFocusNode.dispose(); kr_confirmPasswordFocusNode.dispose(); kr_codeFocusNode.dispose(); kr_inviteCodeFocusNode.dispose(); super.onClose(); } // 添加点击输入框的方法 void kr_onInputTap() { kr_accountFocusNode.requestFocus(); } // 添加焦点管理 void kr_initFocus() { kr_accountFocusNode.addListener(() { if (kr_accountFocusNode.hasFocus) { // 获得焦点时的处理 _updateInputState(); } }); } void _updateInputState() { String input = accountController.text.trim(); // 始终保持邮箱类型 kr_loginType.value = KRLoginType.kr_email; kr_emailList.value = kr_generateAndSortEmailList(input); } // 添加移除悬浮框的方法 void kr_removeOverlay() { overlayEntry?.remove(); overlayEntry = null; } }