You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
754 lines
25 KiB
754 lines
25 KiB
import '/data/services/user_portrait.dart';
|
|
import 'package:dio/dio.dart' as dio;
|
|
import 'package:get/get.dart';
|
|
import 'package:get_storage/get_storage.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:image_picker/image_picker.dart';
|
|
import 'package:logger/logger.dart';
|
|
import '../../../data/models/user_Info.dart';
|
|
import '../../settings/controllers/settings_controller.dart';
|
|
import 'dart:io';
|
|
//import 'dart:convert';
|
|
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import '../../../data/services/network/api.dart';
|
|
import 'package:dio/dio.dart';
|
|
import '../../../routes/app_routes.dart';
|
|
//import '../../../modules/login/controllers/googleLogin.dart';
|
|
//import '../../../modules/login/controllers/facebookLogin.dart';
|
|
import '../../../data/services/account_service.dart';
|
|
//import 'package:http_parser/http_parser.dart'; // 新增此导入
|
|
|
|
class ProfileController extends GetxController {
|
|
static ProfileController get to => Get.find();
|
|
|
|
//用户信息
|
|
static const String userKey = 'user_info'; //用户信息存储键
|
|
final _storage = GetStorage(); //本地存储
|
|
final ImagePicker _picker = ImagePicker(); //图片选择器
|
|
final Rx<User> userInfo = User.instance.obs; // 响应式用户信息对象,使用单例模式确保全局唯一性
|
|
final RxString localAvatar = ''.obs; // 本地头像路径,便于响应式更新UI
|
|
final RxBool hasEdits = false.obs; // 是否有编辑
|
|
final RxBool isUpdating = false.obs; // 是否正在更新用户信息
|
|
final RxBool isEditingNickname = false.obs; // 是否正在编辑昵称(内联编辑状态)
|
|
final RxBool isAvatarReady = false.obs; // 头像数据是否已准备好,用于防止闪烁
|
|
//音乐播放管理
|
|
final _userportraitManager = Get.find<UserportraitManager>();
|
|
// 用于乐观更新的原始状态备份
|
|
String _originalName = '';
|
|
String _originalAvatar = '';
|
|
String _originalLocalAvatar = '';
|
|
|
|
// 使用响应式变量
|
|
final currentLanguage = 'zh'.obs;
|
|
|
|
// 账号服务
|
|
late final AccountService _accountService;
|
|
|
|
// 错误处理工具方法
|
|
String _getErrorMessage(dynamic error) {
|
|
final errorStr = error.toString().toLowerCase();
|
|
|
|
if (errorStr.contains('network') ||
|
|
errorStr.contains('socket') ||
|
|
errorStr.contains('timeout') ||
|
|
errorStr.contains('connection')) {
|
|
return 'networkConnectionFailed'.tr; //网络连接失败
|
|
} else if (errorStr.contains('server') ||
|
|
errorStr.contains('500') ||
|
|
errorStr.contains('502') ||
|
|
errorStr.contains('503')) {
|
|
return 'serverTemporarilyUnavailable'.tr; //服务器暂时不可用
|
|
} else if (errorStr.contains('401') || errorStr.contains('unauthorized')) {
|
|
return 'loginExpiredPleaseRelogin'.tr; //登录过期,请重新登录
|
|
} else if (errorStr.contains('403') || errorStr.contains('forbidden')) {
|
|
return 'noPermissionForThisOperation'.tr; //没有权限执行此操作
|
|
} else if (errorStr.contains('404')) {
|
|
return 'requestedResourceNotFound'.tr; //请求的资源不存在
|
|
} else if (errorStr.contains('file') || errorStr.contains('image')) {
|
|
return 'imageProcessingFailedPleaseReselect'.tr; //图片处理失败,请重新选择图片
|
|
} else {
|
|
return 'operationFailedPleaseRetry'.tr; //操作失败,请重试
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
// 获取或注册账号服务
|
|
_accountService = Get.find<AccountService>();
|
|
loadUserInfo();
|
|
loadLanguagePreference();
|
|
}
|
|
|
|
// 加载用户语言偏好设置
|
|
void loadLanguagePreference() {
|
|
try {
|
|
final String? languageCode = _storage.read('languageCode');
|
|
final String? countryCode = _storage.read('countryCode');
|
|
|
|
if (languageCode != null && countryCode != null) {
|
|
Get.updateLocale(Locale(languageCode, countryCode));
|
|
currentLanguage.value = languageCode;
|
|
}
|
|
} catch (e) {
|
|
Logger().e('Failed to load language preference: $e');
|
|
}
|
|
}
|
|
|
|
// 保存用户语言偏好设置
|
|
Future<void> saveLanguagePreference(Locale locale) async {
|
|
try {
|
|
await _storage.write('languageCode', locale.languageCode);
|
|
await _storage.write('countryCode', locale.countryCode);
|
|
} catch (e) {
|
|
Logger().e('Failed to save language preference: $e');
|
|
}
|
|
}
|
|
|
|
// 切换应用语言
|
|
void changeLanguage(String languageCode, String countryCode) {
|
|
// 使用Future.delayed来避免在构建过程中更新UI
|
|
Future.delayed(Duration.zero, () async {
|
|
final locale = Locale(languageCode, countryCode);
|
|
await saveLanguagePreference(locale);
|
|
Get.updateLocale(locale);
|
|
currentLanguage.value = languageCode;
|
|
});
|
|
}
|
|
|
|
// Future<void> updateUserData({
|
|
// String? userName,
|
|
// String? email,
|
|
// String? avatar,
|
|
// }) async {
|
|
// try {
|
|
// if (userName != null) {
|
|
// await _storage.write('userName', userName);
|
|
// _userName.value = userName;
|
|
// }
|
|
// if (email != null) {
|
|
// await _storage.write('userEmail', email);
|
|
// _email.value = email;
|
|
// }
|
|
// if (avatar != null) {
|
|
// await _storage.write('userAvatar', avatar);
|
|
// _avatar.value = avatar;
|
|
// }
|
|
// Get.snackbar('Success', 'Profile updated successfully');
|
|
// } catch (e) {
|
|
// Get.snackbar('Error', 'Failed to update profile');
|
|
// }
|
|
// }
|
|
|
|
// void toggleEditing() {
|
|
// _isEditing.value = !_isEditing.value;
|
|
// }
|
|
|
|
// 验证base64图片数据
|
|
// Future<void> _verifyBase64Image(String base64String) async {
|
|
// try {
|
|
// // 解码base64数据
|
|
// List<int> imageBytes = base64Decode(base64String);
|
|
|
|
// // 获取应用文档目录
|
|
// final appDir = await getApplicationDocumentsDirectory();
|
|
// final verifyPath = '${appDir.path}/verify_base64_${DateTime.now().millisecondsSinceEpoch}.jpg';
|
|
|
|
// // 将解码后的数据写入文件
|
|
// final file = File(verifyPath);
|
|
// await file.writeAsBytes(imageBytes);
|
|
|
|
// // 记录验证文件的路径
|
|
// Logger().d('Base64验证图片已保存到: $verifyPath');
|
|
|
|
// // 显示图片预览对话框
|
|
// Get.dialog(
|
|
// Dialog(
|
|
// child: Column(
|
|
// mainAxisSize: MainAxisSize.min,
|
|
// children: [
|
|
// Padding(
|
|
// padding: const EdgeInsets.all(16.0),
|
|
// child: Text('Base64 图片验证', style: TextStyle(fontWeight: FontWeight.bold)),
|
|
// ),
|
|
// Image.file(File(verifyPath)),
|
|
// Padding(
|
|
// padding: const EdgeInsets.all(16.0),
|
|
// child: ElevatedButton(
|
|
// onPressed: () => Get.back(),
|
|
// child: Text('关闭'),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// );
|
|
|
|
// Get.snackbar('验证', '已将Base64转换回图片,请查看预览');
|
|
// } catch (e) {
|
|
// Logger().e('验证Base64图片失败: $e');
|
|
// Get.snackbar('错误', 'Base64验证失败: $e');
|
|
// }
|
|
// }
|
|
|
|
// 强制回滚未保存的更改(在页面退出时调用)
|
|
void forceRollbackIfUnsaved() {
|
|
if (hasEdits.value) {
|
|
Logger().d('检测到未保存的编辑,执行强制回滚');
|
|
_rollbackToOriginalState(); // 复用现有的回滚方法
|
|
}
|
|
isEditingNickname.value = false;
|
|
}
|
|
|
|
// 更新用户信息到后端
|
|
Future<void> _syncWithBackend() async {
|
|
try {
|
|
Logger().d('开始同步用户信息到后端');
|
|
|
|
// 创建FormData对象用于上传
|
|
dio.FormData formData = dio.FormData();
|
|
|
|
// 添加基本用户信息
|
|
formData.fields.add(MapEntry('language', currentLanguage.value));
|
|
formData.fields.add(MapEntry('name', userInfo.value.name));
|
|
|
|
// 如果有头像,添加到FormData
|
|
if (localAvatar.value.isNotEmpty) {
|
|
try {
|
|
File imageFile = File(localAvatar.value);
|
|
if (await imageFile.exists()) {
|
|
// 添加文件到FormData
|
|
formData.files.add(
|
|
MapEntry(
|
|
'file',
|
|
await dio.MultipartFile.fromFile(
|
|
imageFile.path,
|
|
filename: 'avatar.jpg',
|
|
),
|
|
),
|
|
);
|
|
Logger().d('已添加头像文件到请求');
|
|
} else {
|
|
Logger().e('头像文件不存在: ${localAvatar.value}');
|
|
throw Exception('头像文件不存在');
|
|
}
|
|
} catch (e) {
|
|
Logger().e('读取头像文件失败: $e');
|
|
throw Exception('读取头像文件失败: $e');
|
|
}
|
|
}
|
|
|
|
Logger().d('准备更新用户信息: ${formData.fields}');
|
|
|
|
// 调用API,使用FormData
|
|
final response = await Api.updateUserSetting(formData);
|
|
Logger().d('--更新用户信息响应: $response');
|
|
|
|
if (response != null && response is Map<String, dynamic>) {
|
|
// 检查响应是否包含错误
|
|
if (response.containsKey('error') || response.containsKey('msg')) {
|
|
final errorMsg = response['error'] ??
|
|
response['msg'] ??
|
|
'serverReturnedError'.tr; //服务器返回错误
|
|
Logger().e('服务器返回错误: $errorMsg');
|
|
throw Exception('${'serverError'.tr}: $errorMsg'); //服务器错误
|
|
}
|
|
|
|
Logger().d('更新成功: $response');
|
|
|
|
// 更新用户信息为服务器返回的最新数据
|
|
userInfo.update((u) {
|
|
if (u != null) {
|
|
u.name = response['name'] ?? u.name;
|
|
u.avatar = response['avatar'] ?? u.avatar;
|
|
}
|
|
});
|
|
|
|
// 如果服务器返回了新的头像URL,下载并保存到本地
|
|
if (response['avatar'] != null &&
|
|
response['avatar'].toString().isNotEmpty) {
|
|
final serverAvatar = response['avatar'].toString();
|
|
if (serverAvatar.startsWith('http')) {
|
|
// 异步下载新头像,不阻塞当前流程
|
|
_downloadAndSaveAvatar(serverAvatar);
|
|
}
|
|
}
|
|
|
|
Logger().d('更新后的用户信息: ${userInfo.value.toJson()}');
|
|
} else {
|
|
Logger().e('服务器响应为空或格式错误: $response');
|
|
throw Exception('服务器响应异常');
|
|
}
|
|
} catch (e) {
|
|
Logger().e('同步到后端失败: $e');
|
|
|
|
// 根据错误类型抛出更具体的异常
|
|
if (e.toString().contains('SocketException') ||
|
|
e.toString().contains('TimeoutException') ||
|
|
e.toString().contains('network')) {
|
|
throw Exception(
|
|
'network: ${'networkConnectionFailedCheckSettings'.tr}'); //网络连接失败,请检查网络设置
|
|
} else if (e.toString().contains('500') ||
|
|
e.toString().contains('server')) {
|
|
throw Exception(
|
|
'server: ${'serverInternalErrorRetryLater'.tr}'); //服务器内部错误,请稍后重试
|
|
} else {
|
|
throw Exception('unknown: ${e.toString()}');
|
|
}
|
|
}
|
|
}
|
|
|
|
void loadUserInfo() async {
|
|
final userJson = _storage.read(userKey);
|
|
if (userJson != null) {
|
|
userInfo.value = User.instance;
|
|
Logger().d('从本地加载用户信息: $userJson');
|
|
|
|
// 重置头像准备状态
|
|
isAvatarReady.value = false;
|
|
|
|
// 同步加载本地头像路径并验证文件有效性 - 修复闪烁问题
|
|
final savedLocalAvatar = _storage.read('local_avatar');
|
|
if (savedLocalAvatar != null && savedLocalAvatar.toString().isNotEmpty) {
|
|
final avatarPath = savedLocalAvatar.toString();
|
|
// 同步验证文件是否存在,避免异步导致的闪烁
|
|
final file = File(avatarPath);
|
|
try {
|
|
final exists = await file.exists();
|
|
if (exists) {
|
|
localAvatar.value = avatarPath;
|
|
Logger().d('加载本地头像路径: ${localAvatar.value}');
|
|
} else {
|
|
// 文件不存在,清除无效路径
|
|
localAvatar.value = '';
|
|
_storage.remove('local_avatar');
|
|
Logger().w('本地头像文件不存在,已清除路径: $avatarPath');
|
|
}
|
|
} catch (e) {
|
|
// 文件访问异常,清除无效路径
|
|
localAvatar.value = '';
|
|
_storage.remove('local_avatar');
|
|
Logger().e('验证本地头像文件时发生异常: $e');
|
|
}
|
|
} else {
|
|
// 确保本地头像路径为空
|
|
localAvatar.value = '';
|
|
Logger().d('没有本地头像路径');
|
|
}
|
|
|
|
// 头像数据准备完成
|
|
isAvatarReady.value = true;
|
|
|
|
// 保存原始状态用于回滚
|
|
_saveOriginalState();
|
|
|
|
// 如果有远程头像地址且本地头像不存在,才下载并保存到本地
|
|
if (userInfo.value.avatar.isNotEmpty && localAvatar.value.isEmpty) {
|
|
Logger().d('远程头像地址: ${userInfo.value.avatar}');
|
|
_downloadAndSaveAvatar(userInfo.value.avatar);
|
|
}
|
|
|
|
// 确保状态一致性
|
|
_ensureStateConsistency();
|
|
}
|
|
}
|
|
|
|
// 确保本地状态与服务器状态的一致性
|
|
void _ensureStateConsistency() async {
|
|
// 同步检查本地头像文件是否存在 - 修复闪烁问题
|
|
if (localAvatar.value.isNotEmpty) {
|
|
final file = File(localAvatar.value);
|
|
try {
|
|
final exists = await file.exists();
|
|
if (!exists) {
|
|
Logger().w('本地头像文件不存在,清除本地头像路径: ${localAvatar.value}');
|
|
localAvatar.value = '';
|
|
_storage.remove('local_avatar');
|
|
|
|
// 如果有远程头像,重新下载
|
|
if (userInfo.value.avatar.isNotEmpty) {
|
|
_downloadAndSaveAvatar(userInfo.value.avatar);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
Logger().e('检查本地头像文件一致性时发生异常: $e');
|
|
localAvatar.value = '';
|
|
_storage.remove('local_avatar');
|
|
}
|
|
}
|
|
}
|
|
|
|
// 保存原始状态用于乐观更新回滚
|
|
void _saveOriginalState() {
|
|
_originalName = userInfo.value.name;
|
|
_originalAvatar = userInfo.value.avatar;
|
|
_originalLocalAvatar = localAvatar.value;
|
|
Logger().d(
|
|
'已保存原始状态 - 名称: $_originalName, 头像: $_originalAvatar, 本地头像: $_originalLocalAvatar');
|
|
}
|
|
|
|
// 回滚到原始状态
|
|
Future<void> _rollbackToOriginalState() async {
|
|
Logger().d('开始回滚到原始状态');
|
|
|
|
// 回滚内存中的状态
|
|
userInfo.update((user) {
|
|
if (user != null) {
|
|
user.name = _originalName;
|
|
user.avatar = _originalAvatar;
|
|
}
|
|
});
|
|
localAvatar.value = _originalLocalAvatar;
|
|
|
|
// 重置编辑状态
|
|
hasEdits.value = false;
|
|
|
|
// 关键修复:回滚后立即同步到本地存储
|
|
try {
|
|
await _syncLocalStorage();
|
|
Logger().d('回滚状态已同步到本地存储');
|
|
} catch (e) {
|
|
Logger().e('回滚状态同步到本地存储失败: $e');
|
|
}
|
|
|
|
Logger().d(
|
|
'已回滚到原始状态 - 名称: $_originalName, 头像: $_originalAvatar, 本地头像: $_originalLocalAvatar');
|
|
}
|
|
|
|
// 开始编辑昵称(内联编辑)
|
|
void startEditingNickname() {
|
|
isEditingNickname.value = true;
|
|
Logger().d('开始内联编辑昵称');
|
|
}
|
|
|
|
// 完成编辑昵称(内联编辑)
|
|
void finishEditingNickname(String newName) {
|
|
isEditingNickname.value = false;
|
|
|
|
if (newName.trim().isNotEmpty && newName.trim() != _originalName) {
|
|
// 乐观更新
|
|
userInfo.update((user) {
|
|
if (user != null) {
|
|
user.name = newName.trim();
|
|
}
|
|
});
|
|
hasEdits.value = true;
|
|
Logger().d('昵称内联编辑完成,新名称: ${newName.trim()}');
|
|
} else {
|
|
Logger().d('昵称内联编辑取消或无变化');
|
|
}
|
|
}
|
|
|
|
// 取消编辑昵称(内联编辑)
|
|
void cancelEditingNickname() {
|
|
isEditingNickname.value = false;
|
|
Logger().d('取消内联编辑昵称');
|
|
}
|
|
|
|
// 下载并保存远程头像
|
|
Future<void> _downloadAndSaveAvatar(String url) async {
|
|
try {
|
|
Logger().d('开始下载头像: $url');
|
|
|
|
final dio = Dio(BaseOptions(
|
|
headers: {
|
|
'accept': '*/*',
|
|
'Authorization': _storage.read<String>("logintoken"),
|
|
},
|
|
));
|
|
|
|
final response = await dio.get(
|
|
url,
|
|
options: Options(responseType: ResponseType.bytes),
|
|
);
|
|
|
|
Logger().d('下载状态: ${response.statusCode}');
|
|
|
|
if (response.statusCode == 200) {
|
|
// 保存到应用文档目录
|
|
final appDir = await getApplicationSupportDirectory();
|
|
final fileName =
|
|
'/UserAvatar/avatar_${DateTime.now().millisecondsSinceEpoch}.jpg';
|
|
final localPath = '${appDir.path}/$fileName';
|
|
|
|
// 写入文件
|
|
final file = File(localPath);
|
|
await file.parent.create(recursive: true);
|
|
await file.writeAsBytes(response.data);
|
|
|
|
// 更新本地头像路径
|
|
localAvatar.value = localPath;
|
|
await _storage.write('local_avatar', localPath);
|
|
Logger().d('头像已下载到本地: $localPath');
|
|
//Get.snackbar('头像', '头像已下载到本地');
|
|
}
|
|
} catch (e) {
|
|
Logger().e('下载头像失败: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> saveUserInfo() async {
|
|
await _storage.write(userKey, userInfo.value.toJson());
|
|
Logger().d('用户信息已保存到本地存储');
|
|
}
|
|
|
|
// 同步所有相关状态到本地存储
|
|
Future<void> _syncLocalStorage() async {
|
|
try {
|
|
// 保存用户信息
|
|
await saveUserInfo();
|
|
|
|
// 同步本地头像路径
|
|
if (localAvatar.value.isNotEmpty) {
|
|
await _storage.write('local_avatar', localAvatar.value);
|
|
Logger().d('本地头像路径已保存: ${localAvatar.value}');
|
|
} else {
|
|
// 如果本地头像为空,清除存储中的路径
|
|
await _storage.remove('local_avatar');
|
|
Logger().d('本地头像路径已清除');
|
|
}
|
|
|
|
Logger().d('所有状态已同步到本地存储');
|
|
} catch (e) {
|
|
Logger().e('同步本地存储失败: $e');
|
|
}
|
|
}
|
|
|
|
// 更新用户头像
|
|
Future<void> updateUserAvatar() async {
|
|
final XFile? image = await _picker.pickImage(
|
|
source: ImageSource.gallery, // 从相册选择
|
|
maxWidth: 800,
|
|
maxHeight: 800,
|
|
imageQuality: 85,
|
|
);
|
|
|
|
if (image != null) {
|
|
try {
|
|
// 保存当前状态用于可能的回滚
|
|
_saveOriginalState();
|
|
|
|
// 检查文件大小
|
|
final file = File(image.path);
|
|
var fileSize = await file.length();
|
|
var quality = 85;
|
|
|
|
// 如果文件大于1MB,进行压缩
|
|
while (fileSize > 1024 * 1024 && quality > 30) {
|
|
final compressedImage = await FlutterImageCompress.compressAndGetFile(
|
|
file.absolute.path,
|
|
file.absolute.path, // 覆盖原文件
|
|
quality: quality,
|
|
minWidth: 800,
|
|
minHeight: 800,
|
|
);
|
|
|
|
if (compressedImage == null) {
|
|
Get.snackbar('error'.tr, 'imageCompressionFailed'.tr); //图片压缩失败
|
|
return;
|
|
}
|
|
|
|
//更新文件大小和质量
|
|
fileSize = await compressedImage.length();
|
|
quality -= 5;
|
|
Logger().d('压缩后文件大小:$fileSize');
|
|
}
|
|
|
|
// 乐观更新:立即更新UI
|
|
userInfo.update((user) {
|
|
if (user != null) {
|
|
user.avatar = image.path;
|
|
}
|
|
});
|
|
|
|
// 更新本地头像路径
|
|
localAvatar.value = image.path;
|
|
await _storage.write('local_avatar', image.path);
|
|
hasEdits.value = true; //表示有编辑
|
|
|
|
Logger().d('头像已乐观更新,等待用户保存');
|
|
} catch (e) {
|
|
Logger().e('更新头像失败: $e');
|
|
final errorMessage = _getErrorMessage(e);
|
|
Get.snackbar(
|
|
'avatarUpdateFailed'.tr,
|
|
errorMessage,
|
|
duration: const Duration(seconds: 3),
|
|
snackPosition: SnackPosition.TOP,
|
|
); //头像更新失败
|
|
// 发生错误时回滚状态
|
|
await _rollbackToOriginalState();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 更新用户名和头像(保存操作)
|
|
Future<void> updateUserNameAndAvatar(String name) async {
|
|
if (name.trim().isEmpty) {
|
|
Logger().e('无效名称: $name');
|
|
Get.snackbar('error'.tr, 'usernameCannotBeEmpty'.tr); //用户名不能为空
|
|
return;
|
|
}
|
|
|
|
// 显示加载状态
|
|
isUpdating.value = true;
|
|
|
|
try {
|
|
Logger().d('开始保存用户信息 - 名称: ${name.trim()}, 本地头像: ${localAvatar.value}');
|
|
|
|
// 同步到后端
|
|
await _syncWithBackend();
|
|
|
|
// 同步成功后更新原始状态和本地存储
|
|
_saveOriginalState();
|
|
await _syncLocalStorage();
|
|
|
|
// 重置编辑状态
|
|
hasEdits.value = false;
|
|
|
|
Logger().d('用户信息保存成功');
|
|
|
|
// 显示成功提示
|
|
Get.snackbar(
|
|
'saveSuccess'.tr,
|
|
'personalInfoUpdated'.tr,
|
|
duration: const Duration(seconds: 3),
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.green.withOpacity(0.1),
|
|
colorText: Colors.green.shade700,
|
|
icon: Icon(
|
|
Icons.check_circle_outline,
|
|
color: Colors.green.shade700,
|
|
),
|
|
margin: const EdgeInsets.all(16),
|
|
borderRadius: 8,
|
|
isDismissible: true,
|
|
forwardAnimationCurve: Curves.easeOutBack,
|
|
); //个人信息更新成功
|
|
|
|
Logger().d('成功提示已显示');
|
|
|
|
// 备用成功提示机制 - 使用ScaffoldMessenger确保用户看到
|
|
Future.delayed(const Duration(milliseconds: 100), () {
|
|
if (Get.context != null) {
|
|
ScaffoldMessenger.of(Get.context!).showSnackBar(
|
|
SnackBar(
|
|
content: Row(
|
|
children: [
|
|
Icon(Icons.check_circle, color: Colors.white),
|
|
SizedBox(width: 8),
|
|
Text('personalInfoUpdated'.tr),
|
|
],
|
|
),
|
|
backgroundColor: Colors.green,
|
|
duration: Duration(seconds: 2),
|
|
behavior: SnackBarBehavior.floating,
|
|
),
|
|
);
|
|
}
|
|
});
|
|
} catch (e) {
|
|
Logger().e('更新用户名和头像错误: $e');
|
|
|
|
// 同步失败时回滚到原始状态
|
|
await _rollbackToOriginalState();
|
|
|
|
// 显示错误提示
|
|
final errorMessage = _getErrorMessage(e);
|
|
Get.snackbar(
|
|
'saveFailed'.tr,
|
|
errorMessage,
|
|
duration: const Duration(seconds: 4),
|
|
snackPosition: SnackPosition.TOP,
|
|
); //保存失败
|
|
} finally {
|
|
// 隐藏加载状态
|
|
isUpdating.value = false;
|
|
}
|
|
}
|
|
|
|
// 清理用户缓存
|
|
Future<void> clean() async {
|
|
await _userportraitManager.clean();
|
|
}
|
|
|
|
// 退出登录
|
|
Future<void> signOut() async {
|
|
try {
|
|
// 显示加载提示
|
|
Get.dialog(
|
|
const Center(child: CircularProgressIndicator()),
|
|
barrierDismissible: false,
|
|
);
|
|
|
|
// 调用账号服务执行登出
|
|
final result = await _accountService.signOut();
|
|
|
|
// 关闭加载提示
|
|
Get.back();
|
|
|
|
if (result.success) {
|
|
// 先显示成功提示
|
|
Get.snackbar('success'.tr, result.message ?? 'loggedOut'.tr); //已退出登录
|
|
Logger().d('退出登录成功');
|
|
|
|
// 然后导航到登录页面
|
|
if (Get.currentRoute != Routes.login) {
|
|
await Get.offAllNamed(Routes.login);
|
|
}
|
|
} else {
|
|
Get.snackbar('error'.tr,
|
|
result.errorMessage ?? 'logoutFailedRetry'.tr); //退出登录失败,请重试
|
|
Logger().e('退出登录失败: ${result.errorMessage}');
|
|
}
|
|
} catch (e) {
|
|
// 确保加载提示被关闭
|
|
if (Get.isDialogOpen ?? false) {
|
|
Get.back();
|
|
}
|
|
Get.snackbar('error'.tr, 'logoutFailedRetry'.tr); //退出登录失败,请重试
|
|
Logger().e('退出登录错误: $e');
|
|
}
|
|
}
|
|
|
|
// 注销账号
|
|
Future<void> deleteAccount() async {
|
|
try {
|
|
// 显示加载提示
|
|
Get.dialog(
|
|
const Center(child: CircularProgressIndicator()),
|
|
barrierDismissible: false,
|
|
);
|
|
|
|
// 调用账号服务执行账号删除
|
|
final result = await _accountService.deleteAccount();
|
|
|
|
// 关闭加载提示
|
|
Get.back();
|
|
|
|
if (result.success) {
|
|
// 先显示成功提示
|
|
Get.snackbar('success'.tr,
|
|
result.message ?? 'accountPermanentlyDeleted'.tr); //账号已被永久删除
|
|
Logger().d('账号删除成功');
|
|
|
|
// 然后导航到登录页面
|
|
if (Get.currentRoute != Routes.login) {
|
|
await Get.offAllNamed(Routes.login);
|
|
}
|
|
} else {
|
|
Get.snackbar('error'.tr,
|
|
result.errorMessage ?? 'deleteAccountFailedRetry'.tr); //删除账号失败,请重试
|
|
Logger().e('删除账号失败: ${result.errorMessage}');
|
|
}
|
|
} catch (e) {
|
|
// 确保加载提示被关闭
|
|
if (Get.isDialogOpen ?? false) {
|
|
Get.back();
|
|
}
|
|
Get.snackbar('error'.tr, 'deleteAccountFailedRetry'.tr); //删除账号失败,请重试
|
|
Logger().e('删除账号失败: $e');
|
|
}
|
|
}
|
|
}
|
|
|