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

379 lines
13 KiB
Dart

import 'dart:developer';
import 'dart:io';
import 'package:elysia/plugin/HTTP.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:typed_data';
import 'package:oktoast/oktoast.dart';
import '../../plugin/C.dart';
import '../../plugin/Toast.dart';
class ChangePasswordDialog extends StatefulWidget {
final VoidCallback onSuccess;
const ChangePasswordDialog({Key? key, required this.onSuccess}) : super(key: key);
@override
_ChangePasswordDialogState createState() => _ChangePasswordDialogState();
}
class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
final _formKey = GlobalKey<FormState>();
final TextEditingController _oldPasswordController = TextEditingController();
final TextEditingController _newPasswordController = TextEditingController();
final TextEditingController _confirmPasswordController = TextEditingController();
final TextEditingController _captchaController = TextEditingController();
Uint8List? _captchaImageBytes;
String? _captchaUUID;
bool _loadingCaptcha = false;
bool _isLoading = false;
bool _obscureOldPassword = true;
bool _obscureNewPassword = true;
bool _obscureConfirmPassword = true;
@override
void initState() {
super.initState();
_fetchCaptcha();
}
Future<void> _fetchCaptcha() async {
setState(() {
_loadingCaptcha = true;
});
final url = Uri.parse('${C.BASE_URL}/authorization/captcha');
try {
final response = await http.get(url);
if (response.statusCode == 200) {
setState(() {
_captchaImageBytes = response.bodyBytes;
_captchaUUID = response.headers['captcha-uuid'];
_loadingCaptcha = false;
log(response.headers['captcha-uuid'] ?? '');
});
} else {
setState(() {
_loadingCaptcha = false;
});
print("获取验证码失败: ${response.statusCode}");
}
} catch (e) {
setState(() {
_loadingCaptcha = false;
});
print("获取验证码异常: $e");
}
}
bool _validateForm() {
if (_oldPasswordController.text.isEmpty) {
Toast.show('请输入原密码', type: ToastType.Error, position: ToastPosition.center);
return false;
}
if (_oldPasswordController.text.length < 6) {
Toast.show('密码长度至少6位', type: ToastType.Error, position: ToastPosition.center);
return false;
}
if (_newPasswordController.text.isEmpty) {
Toast.show('请输入新密码', type: ToastType.Error, position: ToastPosition.center);
return false;
}
if (_newPasswordController.text.length < 8) {
Toast.show('密码长度至少8位', type: ToastType.Error, position: ToastPosition.center);
return false;
}
if (_confirmPasswordController.text.isEmpty) {
Toast.show('请确认新密码', type: ToastType.Error, position: ToastPosition.center);
return false;
}
if (_newPasswordController.text != _confirmPasswordController.text) {
Toast.show('两次输入的密码不一致', type: ToastType.Error, position: ToastPosition.center);
return false;
}
if (_captchaController.text.isEmpty) {
Toast.show('请输入验证码', type: ToastType.Error, position: ToastPosition.center);
return false;
}
return true;
}
void _submitForm() async {
if (!_validateForm()) {
return;
}
setState(() {
_isLoading = true;
});
try {
// 调用修改密码API
Map<String, String> header = {
"captcha": _captchaController.text,
"captcha-code": _captchaUUID!,
"Authorization": C.TOKEN["Authorization"]!
};
dynamic result = await HTTP.create('${C.BASE_URL}/authorization/modify-password')
.setHeader(header)
.setContentType(ContentType.json)
.setRequestType(RequestType.PUT)
.setBody({
"oldPassword": _oldPasswordController.text,
"newPassword": _newPasswordController.text
}).execute();
print(result.toString());
setState(() {
_isLoading = false;
});
if (result['code'] != 200) {
Toast.show(result["message"], type: ToastType.Error, position: ToastPosition.center);
_fetchCaptcha(); // 刷新验证码
return;
}
// 成功处理
Toast.show('密码修改成功', type: ToastType.Normal, position: ToastPosition.center);
Navigator.of(context).pop();
widget.onSuccess();
} catch (e) {
setState(() {
_isLoading = false;
});
log(e.toString());
Toast.show('修改密码失败,请重试', type: ToastType.Error, position: ToastPosition.center);
_fetchCaptcha(); // 刷新验证码
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final activeColor = theme.colorScheme.primary;
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
backgroundColor: Colors.white,
child: Padding(
padding: const EdgeInsets.all(24),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'修改密码',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: activeColor,
),
),
IconButton(
icon: Icon(Icons.close, color: Colors.grey),
onPressed: () => Navigator.of(context).pop(),
),
],
),
SizedBox(height: 20),
// 原密码
TextField(
controller: _oldPasswordController,
obscureText: _obscureOldPassword,
decoration: InputDecoration(
labelText: '原密码',
prefixIcon: Icon(Icons.lock_outline),
suffixIcon: IconButton(
icon: Icon(
_obscureOldPassword ? Icons.visibility : Icons.visibility_off,
color: Colors.grey,
),
onPressed: () {
setState(() {
_obscureOldPassword = !_obscureOldPassword;
});
},
),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
filled: true,
fillColor: Colors.blue.shade50,
),
),
SizedBox(height: 16),
// 新密码
TextField(
controller: _newPasswordController,
obscureText: _obscureNewPassword,
decoration: InputDecoration(
labelText: '新密码',
prefixIcon: Icon(Icons.lock_reset),
suffixIcon: IconButton(
icon: Icon(
_obscureNewPassword ? Icons.visibility : Icons.visibility_off,
color: Colors.grey,
),
onPressed: () {
setState(() {
_obscureNewPassword = !_obscureNewPassword;
});
},
),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
filled: true,
fillColor: Colors.blue.shade50,
),
),
SizedBox(height: 16),
// 确认新密码
TextField(
controller: _confirmPasswordController,
obscureText: _obscureConfirmPassword,
decoration: InputDecoration(
labelText: '确认新密码',
prefixIcon: Icon(Icons.lock_reset),
suffixIcon: IconButton(
icon: Icon(
_obscureConfirmPassword ? Icons.visibility : Icons.visibility_off,
color: Colors.grey,
),
onPressed: () {
setState(() {
_obscureConfirmPassword = !_obscureConfirmPassword;
});
},
),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
filled: true,
fillColor: Colors.blue.shade50,
),
),
SizedBox(height: 16),
// 图形验证码
Row(
children: [
Expanded(
child: TextField(
controller: _captchaController,
decoration: InputDecoration(
labelText: '验证码',
prefixIcon: Icon(Icons.verified),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
filled: true,
fillColor: Colors.blue.shade50,
),
),
),
SizedBox(width: 8),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade200),
borderRadius: BorderRadius.circular(12)
),
height: 55,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: GestureDetector(
onTap: _fetchCaptcha,
child: Container(
width: 90,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.transparent,
),
child: _loadingCaptcha
? Center(child: CircularProgressIndicator(strokeWidth: 2))
: _captchaImageBytes != null
? ClipRRect(
borderRadius: BorderRadius.circular(8),
child: FittedBox(
fit: BoxFit.contain,
child: Image.memory(_captchaImageBytes!),
),
)
: Center(child: Icon(Icons.image_not_supported)),
),
),
),
),
],
),
SizedBox(height: 24),
// 按钮
Row(
children: [
Expanded(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
padding: EdgeInsets.symmetric(vertical: 12),
side: BorderSide(color: activeColor),
),
onPressed: _isLoading ? null : () => Navigator.of(context).pop(),
child: Text('取消', style: TextStyle(color: activeColor)),
),
),
SizedBox(width: 12),
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
padding: EdgeInsets.symmetric(vertical: 12),
backgroundColor: activeColor,
),
onPressed: _isLoading ? null : _submitForm,
child: _isLoading
? SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: Text('保存', style: TextStyle(fontSize: 16)),
),
),
],
),
],
),
),
),
);
}
@override
void dispose() {
_oldPasswordController.dispose();
_newPasswordController.dispose();
_confirmPasswordController.dispose();
_captchaController.dispose();
super.dispose();
}
}
// 使用方法:
void showChangePasswordDialog(BuildContext context) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => ChangePasswordDialog(
onSuccess: () {
// 密码修改成功后的回调
print('密码修改成功');
},
),
);
}