/// 登录 / 注册页的表单状态与提交逻辑。 /// /// 校验规则与后端一致(邮箱格式、密码 8-64 位且含字母数字),本地先拦一次是为了少一次网络往返、 /// 也让用户立刻看到哪个字段不合格;后端仍会再校验一遍(前端校验不作为安全边界)。 class AuthLogic { bool signUpMode; String email = ''; String password = ''; bool busy = false; String? errorMessage; AuthLogic({this.signUpMode = false, this.errorMessage}); void toggleMode() { signUpMode = !signUpMode; errorMessage = null; } /// 返回 null 表示校验通过,否则返回错误文案(由视图用 l10n 映射) AuthFieldError? validate() { final mail = email.trim(); if (mail.isEmpty) return AuthFieldError.emailRequired; if (!_emailPattern.hasMatch(mail)) return AuthFieldError.emailInvalid; if (password.isEmpty) return AuthFieldError.passwordRequired; if (password.length < 8 || password.length > 64) { return AuthFieldError.passwordLength; } if (!_hasLetter.hasMatch(password) || !_hasDigit.hasMatch(password)) { return AuthFieldError.passwordWeak; } return null; } String get normalizedEmail => email.trim().toLowerCase(); static final _emailPattern = RegExp( r'^[A-Za-z0-9._%+\-]{1,64}@[A-Za-z0-9]([A-Za-z0-9\-]{0,61}[A-Za-z0-9])?' r'(\.[A-Za-z0-9]([A-Za-z0-9\-]{0,61}[A-Za-z0-9])?)*\.[A-Za-z]{2,}$', ); static final _hasLetter = RegExp('[A-Za-z]'); static final _hasDigit = RegExp('[0-9]'); } enum AuthFieldError { emailRequired, emailInvalid, passwordRequired, passwordLength, passwordWeak, }