379 lines
13 KiB
Dart
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('密码修改成功');
|
|
},
|
|
),
|
|
);
|
|
} |