elysia/lib/page/ForgetPasswordPage.dart
2025-11-04 09:53:47 +08:00

372 lines
11 KiB
Dart

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<ForgetPasswordPage> {
final _formKey = GlobalKey<FormState>();
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<void> _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<void> _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),
),
),
],
);
}
}