372 lines
11 KiB
Dart
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),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|