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 { final _formKey = GlobalKey(); 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 _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 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('密码修改成功'); }, ), ); }