import 'dart:async'; import 'package:elysia/plugin/HTTP.dart'; import 'package:elysia/plugin/LoadingOverlay.dart'; import 'package:elysia/plugin/Toast.dart'; import 'package:flutter/material.dart'; import 'package:oktoast/oktoast.dart'; import '../plugin/C.dart'; class ForgetPasswordPage extends StatefulWidget { @override _ForgetPasswordPageState createState() => _ForgetPasswordPageState(); } class _ForgetPasswordPageState extends State { final _formKey = GlobalKey(); TextEditingController _emailController = TextEditingController(); TextEditingController _verificationCodeController = TextEditingController(); TextEditingController _passwordController = TextEditingController(); TextEditingController _confirmPasswordController = TextEditingController(); bool _isPasswordVisible = false; bool _isConfirmPasswordVisible = false; int _countdown = 0; Timer? _timer; @override void dispose() { _timer?.cancel(); super.dispose(); } // 发送验证码 Future _sendVerificationCode() async { if (_emailController.text.isEmpty) { Toast.show( '请输入邮箱地址', type: ToastType.Error, position: ToastPosition.center, ); return; } if (!RegExp( r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$', ).hasMatch(_emailController.text)) { Toast.show( '邮箱格式错误', type: ToastType.Error, position: ToastPosition.center, ); return; } LoadingOverlay.show(context: context); try { dynamic result = await HTTP .create("${C.BASE_URL}/authorization/forgot-password-mail") .setParam({"mail": _emailController.text}) .setRequestType(RequestType.GET) .execute(); LoadingOverlay.hide(); if (result["code"] != 200) { Toast.show( result["message"], type: ToastType.Error, position: ToastPosition.center, ); } else { setState(() { _countdown = 60 * 5; }); Toast.show( '验证码已发送至 ${_emailController.text}', type: ToastType.Normal, position: ToastPosition.center, ); _timer = Timer.periodic(Duration(seconds: 1), (timer) { setState(() { if (_countdown > 0) { _countdown--; } else { _timer?.cancel(); } }); }); } } catch (e) { LoadingOverlay.hide(); } } // 表单验证 bool _validateForm() { if (_emailController.text.isEmpty) { Toast.show( '请输入邮箱地址', type: ToastType.Error, position: ToastPosition.center, ); return false; } if (_verificationCodeController.text.isEmpty) { Toast.show( '请输入验证码', type: ToastType.Error, position: ToastPosition.center, ); return false; } if (_passwordController.text.isEmpty) { Toast.show( '请输入新密码', type: ToastType.Error, position: ToastPosition.center, ); return false; } if (_passwordController.text.length < 6) { Toast.show( '密码至少6个字符', type: ToastType.Error, position: ToastPosition.center, ); return false; } if (_confirmPasswordController.text.isEmpty) { Toast.show( '请再次输入密码', type: ToastType.Error, position: ToastPosition.center, ); return false; } if (_confirmPasswordController.text != _passwordController.text) { Toast.show( '两次输入的密码不一致', type: ToastType.Error, position: ToastPosition.center, ); return false; } return true; } // 提交表单 Future _submitForm() async { if (!_validateForm()) return; LoadingOverlay.show(context: context, barrierColor: Colors.black54); try { dynamic result = await HTTP .create("${C.BASE_URL}/authorization/forgot-password-mail") .setParam({ "email": _emailController.text, "emailCode": _verificationCodeController.text, "password": _passwordController.text, }) .setRequestType(RequestType.POST) .execute(); LoadingOverlay.hide(); if (result["code"] != 200) { showToast(result["message"]); } else { Toast.show( "密码重置成功,请重新登录", type: ToastType.Normal, position: ToastPosition.center, ); Navigator.pop(context); } } catch (e) { showToast(e.toString()); LoadingOverlay.hide(); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("忘记密码", style: const TextStyle(color: Colors.black)), centerTitle: true, backgroundColor: Colors.grey[100], leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.black), onPressed: () => Navigator.pop(context), ), elevation: 0, ), body: SingleChildScrollView( padding: EdgeInsets.all(20), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ _buildTextField( controller: _emailController, label: '邮箱', required: true, hintText: '请输入邮箱地址', prefixIcon: Icons.email, keyboardType: TextInputType.emailAddress, ), SizedBox(height: 15), Row( children: [ Expanded( flex: 2, child: _buildTextField( controller: _verificationCodeController, required: true, label: '邮箱验证码', hintText: '请输入验证码', prefixIcon: Icons.verified_user, ), ), SizedBox(width: 10), Expanded( flex: 1, child: Container( margin: EdgeInsets.only(top: 25), child: ElevatedButton( onPressed: _countdown > 0 ? null : _sendVerificationCode, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue[700], foregroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 15), ), child: Text( _countdown > 0 ? '${(_countdown / 60).floor()}分${_countdown % 60}秒' : '获取验证码', style: TextStyle(fontSize: 14), ), ), ), ), ], ), SizedBox(height: 15), _buildTextField( controller: _passwordController, required: true, label: '新密码', hintText: '请输入新密码', prefixIcon: Icons.lock, obscureText: !_isPasswordVisible, suffixIcon: IconButton( icon: Icon( _isPasswordVisible ? Icons.visibility : Icons.visibility_off, color: Colors.grey, ), onPressed: () { setState(() { _isPasswordVisible = !_isPasswordVisible; }); }, ), ), SizedBox(height: 15), _buildTextField( controller: _confirmPasswordController, required: true, label: '确认密码', hintText: '请再次输入密码', prefixIcon: Icons.lock_outline, obscureText: !_isConfirmPasswordVisible, suffixIcon: IconButton( icon: Icon( _isConfirmPasswordVisible ? Icons.visibility : Icons.visibility_off, color: Colors.grey, ), onPressed: () { setState(() { _isConfirmPasswordVisible = !_isConfirmPasswordVisible; }); }, ), ), SizedBox(height: 30), Container( width: double.infinity, height: 50, child: ElevatedButton( onPressed: _submitForm, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue[700], foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), child: Text( '确认重置', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), ), ], ), ), ), ); } // 公用输入框 Widget _buildTextField({ required TextEditingController controller, required String label, required String hintText, required IconData prefixIcon, Widget? suffixIcon, bool obscureText = false, bool required = false, TextInputType keyboardType = TextInputType.text, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( label, style: TextStyle( fontWeight: FontWeight.bold, color: Colors.grey[700], ), ), SizedBox(width: 3), if (required) Text("*", style: TextStyle(color: Colors.red, fontSize: 16)), ], ), SizedBox(height: 5), TextFormField( controller: controller, obscureText: obscureText, keyboardType: keyboardType, decoration: InputDecoration( hintText: hintText, prefixIcon: Icon(prefixIcon, color: Colors.blue[700]), suffixIcon: suffixIcon, border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.blue, width: 2), ), contentPadding: EdgeInsets.symmetric(horizontal: 15, vertical: 15), ), ), ], ); } }