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

407 lines
13 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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:image_picker/image_picker.dart';
import 'dart:io';
import 'package:oktoast/oktoast.dart';
import '../plugin/AvatarPicker.dart';
import '../plugin/C.dart';
class RegistrationPage extends StatefulWidget {
@override
_RegistrationPageState createState() => _RegistrationPageState();
}
class _RegistrationPageState extends State<RegistrationPage> {
final _formKey = GlobalKey<FormState>();
// 表单字段控制器
TextEditingController _nameController = TextEditingController();
TextEditingController _nicknameController = TextEditingController();
TextEditingController _usernameController = TextEditingController();
TextEditingController _passwordController = TextEditingController();
TextEditingController _emailController = TextEditingController();
TextEditingController _seepseekAPIController = TextEditingController();
TextEditingController _verificationCodeController = TextEditingController();
// 头像相关
File? _avatarImage;
bool _isPasswordVisible = 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/register-mail").setParam({"mail":_emailController.text}).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();
}
}
// 表单验证不显示错误文本使用Toast
bool _validateForm() {
// 用户名验证
if(_avatarImage==null){
Toast.show('请选择头像',type: ToastType.Error,position: ToastPosition.center);
return false;
}
if (_usernameController.text.isEmpty) {
Toast.show('请输入用户名',type: ToastType.Error,position: ToastPosition.center);
return false;
}
if (_usernameController.text.length < 4) {
Toast.show('用户名至少4个字符',type: ToastType.Error,position: ToastPosition.center);
return false;
}
// 昵称验证
if (_nicknameController.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;
}
// API密钥验证
if (_seepseekAPIController.text.isEmpty) {
Toast.show('请输入Deepseek API Key',type: ToastType.Error,position: ToastPosition.center);
return false;
}
// 邮箱验证
if (_emailController.text.isEmpty) {
Toast.show('请输入邮箱地址',type: ToastType.Error,position: ToastPosition.center);
return false;
}
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 false;
}
// 验证码验证
if (_verificationCodeController.text.isEmpty) {
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/register")
.setHeader(C.TOKEN)
.setParam({
"avatar": _avatarImage,
"name": _nameController.text,
"nickname": _nicknameController.text,
"userName": _usernameController.text,
"password": _passwordController.text,
"email": _emailController.text,
"deepseekKey": _seepseekAPIController.text,
"emailCode": _verificationCodeController.text
})
.setRequestType(RequestType.POST)
.setContentType(ContentType("application", "multipart/form-data"))
.execute();
LoadingOverlay.hide();
if (result["code"] != 200) {
showToast(result["message"]);
} else {
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: [
// 头像选择区域
SizedBox(
width: 90,
height: 90,
child: AvatarPicker(
onImageSelected: (file) {
setState(() {
_avatarImage = file;
});
},
),
),
SizedBox(height: 15),
// 用户名
_buildTextField(
controller: _usernameController,
required: true,
label: '用户名',
hintText: '请输入用户名',
prefixIcon: Icons.account_circle,
),
SizedBox(height: 30),
// 昵称
_buildTextField(
controller: _nicknameController,
required: true,
label: '昵称',
hintText: '请输入您的昵称',
prefixIcon: Icons.face,
),
SizedBox(height: 15),
// 姓名
_buildTextField(
controller: _nameController,
label: '姓名',
hintText: '请输入您的真实姓名',
prefixIcon: Icons.person,
),
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),
// API密钥
_buildTextField(
controller: _seepseekAPIController,
required: true,
label: 'Deepseek API Key',
hintText: '请输入Deepseek API Key',
prefixIcon: Icons.vpn_key,
),
SizedBox(height: 15),
// 邮箱
_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: 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,
),
),
),
),
],
),
),
),
);
}
// 简化后的文本输入框移除validator参数
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),
),
// 移除validator参数不显示错误文本
),
],
);
}
}