QinGang_interested/lib/pages/user/register_page.dart

329 lines
11 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/pages/user/CustomInput.dart';
import 'package:qhd_prevention/pages/user/login_page.dart';
import 'package:qhd_prevention/tools/tools.dart';
import '../../http/ApiService.dart'; // 假设你的 API 在这里
class RegisterPage extends StatefulWidget {
const RegisterPage({super.key});
@override
State<RegisterPage> createState() => _RegisterPageState();
}
class _RegisterPageState extends State<RegisterPage> {
final _formKey = GlobalKey<FormState>();
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _codeController = TextEditingController();
final TextEditingController _pwdController = TextEditingController();
final TextEditingController _confirmPwdController = TextEditingController();
bool _obscurePwd = true;
bool _obscureConfirm = true;
// 验证码发送状态和倒计时
bool _isSendingCode = false;
int _secondsLeft = 0;
Timer? _timer;
String textString =
"*密码长度8-18位必须包含大小写字母+数字+特殊字母例如Qa@123456";
@override
void dispose() {
_timer?.cancel();
_phoneController.dispose();
_codeController.dispose();
_pwdController.dispose();
_confirmPwdController.dispose();
super.dispose();
}
// 验证密码复杂度
bool isPasswordValid(String password) {
final hasUpperCase = RegExp(r'[A-Z]');
final hasLowerCase = RegExp(r'[a-z]');
final hasNumber = RegExp(r'[0-9]');
final hasSpecialChar = RegExp(r'[!@#\$%\^&\*\(\)_\+\-=\[\]\{\};:"\\|,.<>\/\?~`]');
return hasUpperCase.hasMatch(password) &&
hasLowerCase.hasMatch(password) &&
hasNumber.hasMatch(password) &&
hasSpecialChar.hasMatch(password);
}
// 手机号简单校验11 位数字)
bool _isPhoneValid(String phone) {
final RegExp phoneReg = RegExp(r'^\d{11}$');
return phoneReg.hasMatch(phone);
}
void _startCountdown(int seconds) {
_timer?.cancel();
setState(() {
_secondsLeft = seconds;
});
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (!mounted) return;
setState(() {
_secondsLeft--;
if (_secondsLeft <= 0) {
_timer?.cancel();
_secondsLeft = 0;
}
});
});
}
Future<void> _sendCode() async {
final phone = _phoneController.text.trim();
if (phone.isEmpty) {
ToastUtil.showNormal(context, '请输入手机号');
return;
}
if (!_isPhoneValid(phone)) {
ToastUtil.showNormal(context, '请输入有效的手机号11位');
return;
}
if (_isSendingCode || _secondsLeft > 0) return;
setState(() {
_isSendingCode = true;
});
LoadingDialogHelper.show();
try {
final resp = await BasicInfoApi.sendRegisterSms({'phone': phone});
LoadingDialogHelper.hide();
if (resp != null && resp['success'] == true) {
ToastUtil.showNormal(context, '验证码已发送');
_startCountdown(60);
} else {
ToastUtil.showNormal(context, resp?['message'] ?? '发送验证码失败');
}
} catch (e) {
LoadingDialogHelper.hide();
ToastUtil.showNormal(context, '发送验证码失败,请稍后重试');
} finally {
setState(() {
_isSendingCode = false;
});
}
}
Future<void> _handleRegister() async {
if (!(_formKey.currentState?.validate() ?? false)) return;
final phone = _phoneController.text.trim();
final code = _codeController.text.trim();
final pwd = _pwdController.text.trim();
final confirm = _confirmPwdController.text.trim();
if (!_isPhoneValid(phone)) {
ToastUtil.showNormal(context, '请输入有效的手机号11位');
return;
}
if (code.isEmpty) {
ToastUtil.showNormal(context, '请输入验证码');
return;
}
if (pwd.isEmpty) {
ToastUtil.showNormal(context, '请输入密码');
return;
}
if (confirm.isEmpty) {
ToastUtil.showNormal(context, '请确认密码');
return;
}
if (pwd != confirm) {
ToastUtil.showNormal(context, '两次输入的密码不一致');
return;
}
if (pwd.length < 8) {
ToastUtil.showNormal(context, '密码长度需至少8位');
return;
}
if (pwd.length > 32) {
ToastUtil.showNormal(context, '密码长度需小于32位');
return;
}
if (!isPasswordValid(pwd)) {
ToastUtil.showNormal(context, '密码必须包含大小写字母、数字和特殊符号');
return;
}
try {
final data = {
'phone': phone,
'phoneCode': code,
'newPassword': pwd,
'confirmPassword': pwd,
};
LoadingDialogHelper.show();
final resp = await BasicInfoApi.register(data);
LoadingDialogHelper.hide();
if (resp != null && resp['success'] == true) {
ToastUtil.showNormal(context, '注册成功,请登录');
// 跳转到登录页并移除当前页面栈
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const LoginPage()),
(route) => false,
);
} else {
ToastUtil.showNormal(context, resp?['message'] ?? '注册失败,请重试');
}
} catch (e) {
LoadingDialogHelper.hide();
ToastUtil.showNormal(context, '注册失败,请稍后重试');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(title: '注册账号'),
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20),
child: Form(
key: _formKey,
child: Column(
children: [
const SizedBox(height: 8),
// 手机号(使用 CustomInput
CustomInput.buildInput(
_phoneController,
title: '手机号',
hint: '请输入手机号',
keyboardType: TextInputType.phone,
validator: (v) {
if (v == null || v.isEmpty) return '请输入手机号';
if (!_isPhoneValid(v.trim())) return '请输入有效的手机号';
return null;
},
),
const SizedBox(height: 16),
// 验证码 + 发送按钮 行(验证码输入使用 CustomInput
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: CustomInput.buildInput(
_codeController,
title: '验证码',
hint: '请输入验证码',
keyboardType: TextInputType.number,
validator: (v) {
if (v == null || v.isEmpty) return '请输入验证码';
return null;
},
),
),
const SizedBox(width: 12),
Column(
children: [
const SizedBox(height: 40),
SizedBox(
height: 40,
child: ElevatedButton(
onPressed: (_secondsLeft > 0 || _isSendingCode) ? null : _sendCode,
style: ElevatedButton.styleFrom(
backgroundColor: (_secondsLeft > 0) ? Colors.grey.shade400 : const Color(0xFF2A75F8),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.symmetric(horizontal: 12),
),
child: Text(
_secondsLeft > 0 ? '$_secondsLeft s后可重发' : '发送验证码',
style: const TextStyle(fontSize: 14, color: Colors.white),
),
),
),
],
)
],
),
const SizedBox(height: 16),
// 密码
CustomInput.buildInput(
_pwdController,
title: '密码',
hint: '请输入密码',
obscure: _obscurePwd,
suffix: IconButton(
icon: Icon(_obscurePwd ? Icons.visibility_off : Icons.visibility, color: Colors.grey),
onPressed: () => setState(() => _obscurePwd = !_obscurePwd),
),
validator: (v) {
if (v == null || v.isEmpty) return '请输入密码';
if (v.length < 8) return '密码长度至少 8 位';
return null;
},
),
const SizedBox(height: 16),
// 确认密码
CustomInput.buildInput(
_confirmPwdController,
title: '确认密码',
hint: '请再次输入密码',
obscure: _obscureConfirm,
suffix: IconButton(
icon: Icon(_obscureConfirm ? Icons.visibility_off : Icons.visibility, color: Colors.grey),
onPressed: () => setState(() => _obscureConfirm = !_obscureConfirm),
),
validator: (v) {
if (v == null || v.isEmpty) return '请确认密码';
return null;
},
),
const SizedBox(height: 12),
// 密码提示语
Align(
alignment: Alignment.centerLeft,
child: Text(
textString,
style: const TextStyle(color: Colors.red, fontSize: 13),
),
),
const SizedBox(height: 30),
// 注册确认按钮
SizedBox(
width: double.infinity,
height: 45,
child: CustomButton(
onPressed: _handleRegister,
text: '确认',
backgroundColor: Colors.blue,
),
),
],
),
),
),
),
);
}
}