From bd062c2acdc83e8665b92eae7a262b13b3533f52 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Sat, 29 Mar 2025 17:49:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=A4=B4=E5=83=8F=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/translations/language/en_us.dart | 8 + lib/core/translations/language/zh_cn.dart | 8 + lib/modules/home/bindings/home_binding.dart | 2 +- .../home/controllers/home_controller.dart | 2 +- .../controllers/pairing_controller.dart | 2 +- .../controllers/profile_controller.dart | 142 ++++++++++++++---- lib/modules/profile/views/profile_view.dart | 10 +- .../controllers/settings_controller.dart | 92 +++++++++--- lib/modules/settings/views/settings_view.dart | 59 +++++++- .../timbre/controllers/timbre_controller.dart | 8 +- lib/modules/timbre/views/timbre_view.dart | 6 +- 11 files changed, 270 insertions(+), 69 deletions(-) diff --git a/lib/core/translations/language/en_us.dart b/lib/core/translations/language/en_us.dart index d109f357d..8d3cbab5b 100644 --- a/lib/core/translations/language/en_us.dart +++ b/lib/core/translations/language/en_us.dart @@ -43,6 +43,7 @@ const Map enUS = { "and": " and ", "userAgreement": "User Agreement", "privacyPolicy": "Privacy Policy", + 'selectCountry':'Select Country/Region', // 首页 "deviceStatus": "Device Status", @@ -85,6 +86,8 @@ const Map enUS = { "fine": "Great", "excellent": "Excellent", "unknown": "Unknown", + 'bluetoothTurnedOff': 'Bluetooth is turned off, please turn it on', + 'bluetoothOff':'bluetooth is off', // 发现页 "searchAi": "Search AI features...", @@ -148,6 +151,11 @@ const Map enUS = { "flutterVoiceService": "Local voice service", "flutterVoiceServiceInfo": "Device-local voice service with no internet required", + 'timbreService': 'Timbre Service', + 'timbreServiceInfo': 'Select voice synthesis service', + 'defaultTimbre': 'Default Timbre', + 'microsoftCloudTimbreService': 'Microsoft Cloud Timbre Service', + 'voiceClone': 'Voice Clone', // 聊天页 "clearChatHistory": "Clear Chat History", diff --git a/lib/core/translations/language/zh_cn.dart b/lib/core/translations/language/zh_cn.dart index ba7674fd9..e1e33780b 100644 --- a/lib/core/translations/language/zh_cn.dart +++ b/lib/core/translations/language/zh_cn.dart @@ -84,6 +84,8 @@ const Map zhCN = { "fine": "很好", "excellent": "极佳", "unknown": "未知", + 'bluetoothTurnedOff': '蓝牙已关闭,请打开蓝牙', + 'bluetoothOff': '蓝牙已关闭', // 发现页 "searchAi": "搜索AI功能...", @@ -143,6 +145,12 @@ const Map zhCN = { "azureVoiceServiceInfo": "微软云端语音服务,支持高质量语音合成和识别", "flutterVoiceService": "Flutter 本地语音服务", "flutterVoiceServiceInfo": "设备本地语音服务,无需联网即可使用", + 'timbreService': '音色服务', + 'timbreServiceInfo': '选择音色语音合成服务', + 'defaultTimbre': '默认音色', + 'microsoftCloudTimbreService': '微软云端音色服务', + 'voiceClone': '声音复刻', + 'selectCountry': '选择国家/地区', // 聊天页 "clearChatHistory": "清空聊天记录", diff --git a/lib/modules/home/bindings/home_binding.dart b/lib/modules/home/bindings/home_binding.dart index 9a67dfcc4..835a946fd 100644 --- a/lib/modules/home/bindings/home_binding.dart +++ b/lib/modules/home/bindings/home_binding.dart @@ -1,4 +1,4 @@ -import 'package:deep_voice/modules/explore/controllers/explore_controller.dart'; +import '../../explore/controllers/explore_controller.dart'; import 'package:get/get.dart'; import '../controllers/home_controller.dart'; diff --git a/lib/modules/home/controllers/home_controller.dart b/lib/modules/home/controllers/home_controller.dart index 1c1de79d5..cfed9c72f 100644 --- a/lib/modules/home/controllers/home_controller.dart +++ b/lib/modules/home/controllers/home_controller.dart @@ -278,7 +278,7 @@ class HomeController extends GetxController { String getConnectionStatusText() { // 如果蓝牙关闭,优先显示蓝牙状态 if (_bluetoothService.bluetoothState.value == 'off') { - return '蓝牙已关闭'; + return 'bluetoothOff'.tr; // 蓝牙已关闭 } switch (_connectionStatus.value) { diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart index b1f07dfdb..2501e26bd 100644 --- a/lib/modules/pairing/controllers/pairing_controller.dart +++ b/lib/modules/pairing/controllers/pairing_controller.dart @@ -82,7 +82,7 @@ class PairingController extends GetxController { /// 更新状态描述 void _updateStatusDescription() { if (_bluetoothService.bluetoothState.value == 'off') { - statusDescription.value = '蓝牙已关闭,请打开蓝牙'; + statusDescription.value = 'bluetoothTurnedOff'.tr;// 蓝牙已关闭, 请打开蓝牙 return; } diff --git a/lib/modules/profile/controllers/profile_controller.dart b/lib/modules/profile/controllers/profile_controller.dart index 28a45fac6..dc2863aa9 100644 --- a/lib/modules/profile/controllers/profile_controller.dart +++ b/lib/modules/profile/controllers/profile_controller.dart @@ -13,6 +13,7 @@ import 'package:path_provider/path_provider.dart'; import '../../../data/services/network/api.dart'; import 'package:dio/dio.dart'; + class ProfileController extends GetxController { static ProfileController get to => Get.find(); @@ -115,6 +116,53 @@ class ProfileController extends GetxController { // void toggleEditing() { // _isEditing.value = !_isEditing.value; + // } + + // 验证base64图片数据 + // Future _verifyBase64Image(String base64String) async { + // try { + // // 解码base64数据 + // List 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'); + // } // } // 更新用户信息到后端 @@ -123,26 +171,29 @@ class ProfileController extends GetxController { // 准备数据 Map data = { "image": "", // 默认空字符串 - "language": currentLanguage.value, - "name": userInfo.value.name, + "language": currentLanguage.value,//当前用户语言 + "name": userInfo.value.name,//当前用户名 }; - // // 如果有头像,转换为base64 - // if (localAvatar.value.isNotEmpty) { - // try{ - // File imageFile = File(localAvatar.value); - // if(await imageFile.exists()){ - // List imageBytes = await imageFile.readAsBytes(); - // String base64Image = base64Encode(imageBytes); - // data["image"] = base64Image; - // }else{ - // Logger().e('头像文件不存在'); - // } - // }catch(e){ - // Logger().e('读取头像文件失败: $e'); - // } - - // } + // 如果有头像,转换为base64 + if (localAvatar.value.isNotEmpty) { + //如果本地头像路径不为空,即代表有头像 + try { + File imageFile = File(localAvatar.value); + if (await imageFile.exists()) { + List imageBytes = await imageFile.readAsBytes(); + String base64Image = base64Encode(imageBytes); + data["image"] = base64Image; + + // // 验证base64数据 - 将base64转回图片并保存到本地 + // await _verifyBase64Image(base64Image); + } else { + Logger().e('头像文件不存在'); + } + } catch (e) { + Logger().e('读取头像文件失败: $e'); + } + } final dio = Dio(BaseOptions( baseUrl: 'https://www.deapsound.com:443', @@ -160,6 +211,8 @@ class ProfileController extends GetxController { '/api/home/user_setting', data: data, ); + + // 调用API //final response = await Api.updateUserSetting(data); @@ -167,6 +220,13 @@ class ProfileController extends GetxController { if (response.data['code'] == 0) { Logger().d('更新成功: ${response.data}'); Get.snackbar('成功', '同步到服务器成功'); + userInfo.update((u) { + if (u != null) { + u.name = response.data['data']['name'] ?? ''; + u.avatar = response.data['data']['avatar'] ?? ''; + } + }); + Logger().d('更新后的用户信息: ${userInfo.value.toJson()}'); } else { throw '更新失败:${response.data['msg'] ?? '未知错误'}'; } @@ -184,18 +244,35 @@ class ProfileController extends GetxController { final userJson = _storage.read(userKey); if (userJson != null) { userInfo.value = User.instance; + Logger().d('从本地加载用户信息: $userJson'); // 如果有远程头像地址,下载并保存到本地 if (userInfo.value.avatar.isNotEmpty) { + Logger().d('远程头像地址: ${userInfo.value.avatar}'); _downloadAndSaveAvatar(userInfo.value.avatar); } } } // 下载并保存远程头像 - Future _downloadAndSaveAvatar(String url) async { + Future _downloadAndSaveAvatar(String url) async { try { - final response = await GetConnect().get(url); - if (response.status.isOk && response.body != null) { + Logger().d('开始下载头像: $url'); + + final dio = Dio(BaseOptions( + headers: { + 'accept': '*/*', + 'Authorization': _storage.read("logintoken"), + }, + )); + + final response = await dio.get( + url, + options: Options(responseType: ResponseType.bytes), + ); + + Logger().d('下载状态: ${response.statusCode}'); + + if (response.statusCode == 200) { // 保存到应用文档目录 final appDir = await getApplicationDocumentsDirectory(); final fileName = 'avatar_${DateTime.now().millisecondsSinceEpoch}.jpg'; @@ -203,15 +280,13 @@ class ProfileController extends GetxController { // 写入文件 final file = File(localPath); - final List bytes = response.body.runtimeType == String - ? base64Decode(response.body.toString()) - : response.body; - await file.writeAsBytes(bytes); + await file.writeAsBytes(response.data); // 更新本地头像路径 localAvatar.value = localPath; await _storage.write('local_avatar', localPath); Logger().d('头像已下载到本地: $localPath'); + //Get.snackbar('头像', '头像已下载到本地'); } } catch (e) { Logger().e('下载头像失败: $e'); @@ -225,12 +300,12 @@ class ProfileController extends GetxController { // 更新用户头像 Future updateUserAvatar() async { final XFile? image = await _picker.pickImage( - source: ImageSource.gallery, + source: ImageSource.gallery,// 从相册选择 maxWidth: 800, maxHeight: 800, imageQuality: 85, ); - + if (image != null) { // 检查文件大小 final file = File(image.path); @@ -268,7 +343,7 @@ class ProfileController extends GetxController { localAvatar.value = image.path; await _storage.write('local_avatar', image.path); // await saveUserInfo(); //保存到本地 - await _syncWithBackend(); //同步到后端 + //await _syncWithBackend(); //同步到后端 // 通知 SettingsController 更新 // if (Get.isRegistered()) { @@ -278,7 +353,7 @@ class ProfileController extends GetxController { } // 更新用户名 - Future updateUserName(String name) async { + Future updateUserNameAndAvatar(String name) async { if (name.trim().isEmpty) { Logger().e('无效名称: $name'); return; @@ -298,11 +373,14 @@ class ProfileController extends GetxController { //同步更新 SettingsController if (Get.isRegistered()) { final settingsController = Get.find(); - settingsController.loadUserInfo(); + settingsController.userInfo.value.name = userInfo.value.name; + settingsController.userInfo.value.avatar = '${userInfo.value.avatar}?time=${DateTime.now().millisecondsSinceEpoch}'; + //settingsController.loadUserInfo(); + settingsController.userInfo.refresh(); } } catch (e) { - Logger().e('更新名称错误: $e'); - Get.snackbar('错误', '更新用户名失败'); + Logger().e('更新用户名和头像错误: $e'); + Get.snackbar('错误', '更新用户名和头像失败'); } } } diff --git a/lib/modules/profile/views/profile_view.dart b/lib/modules/profile/views/profile_view.dart index a74e8ae21..bb2771b08 100644 --- a/lib/modules/profile/views/profile_view.dart +++ b/lib/modules/profile/views/profile_view.dart @@ -148,6 +148,14 @@ class EditProfileView extends GetView { centerTitle: true, elevation: 0, backgroundColor: isDarkMode ? Colors.black : Colors.white, + leading: IconButton( + icon: Icon( + Icons.arrow_back, + size: 24.sp, + color: isDarkMode? Colors.white : Colors.black, + ), + onPressed: () => Get.back(), + ), title: Text( 'editProfile'.tr, style: TextStyle( @@ -231,7 +239,7 @@ class EditProfileView extends GetView { child: ElevatedButton( onPressed: () { // 保存用户名 - controller.updateUserName(nameController.text); + controller.updateUserNameAndAvatar(nameController.text); // 返回上一级页面 Get.back(); }, diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index 9fe723ee2..ce400e224 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -1,15 +1,17 @@ import 'package:firebase_auth/firebase_auth.dart' as firebase; import 'package:get/get.dart'; import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; +import 'package:dio/dio.dart'; import 'package:get_storage/get_storage.dart'; import '../../../core/themes/theme_service.dart'; import '../../../data/services/speech_factory.dart'; import '../models/language_model.dart'; -//import '../../../data/models/user_model.dart'; +import 'package:logger/logger.dart'; import '../../../data/models/user_Info.dart'; import '../../../modules/login/controllers/googleLogin.dart'; import '../../../modules/login/controllers/facebookLogin.dart'; +import 'package:path_provider/path_provider.dart'; +import 'dart:io'; class SettingsController extends GetxController { // 语言设置 @@ -61,12 +63,12 @@ class SettingsController extends GetxController { }, ]; - RxString timbreName = '默认音色'.obs; + RxString timbreName = 'defaultTimbre'.obs; //用户信息 static const String userKey = 'user_info'; //final _storage=GetStorage(); - final ImagePicker _picker = ImagePicker(); + //final ImagePicker _picker = ImagePicker(); final Rx userInfo = User.instance.obs; //使用单例实例 @override @@ -93,8 +95,20 @@ class SettingsController extends GetxController { void loadUserInfo() { final userJson = _storage.read(userKey); + Logger().d('加载用户信息: $userJson'); + if (userJson != null) { userInfo.value = User.instance; + Logger().d('用户信息已加载: ${userInfo.value.toJson()}'); + + if (userInfo.value.avatar.isNotEmpty) { + Logger().d('开始下载头像'); + _downloadAndSaveAvatar(userInfo.value.avatar); + } else { + Logger().d('用户没有头像'); + } + } else { + Logger().d('没有找到用户信息'); } } @@ -102,24 +116,64 @@ class SettingsController extends GetxController { await _storage.write(userKey, userInfo.value.toJson()); } - Future updateUserAvatar() async { - final XFile? image = await _picker.pickImage( - source: ImageSource.gallery, - maxWidth: 800, - maxHeight: 800, - imageQuality: 85, - ); - - if (image != null) { - userInfo.update((user) { - if (user != null) { - user.avatar = image.path; - } - }); - await saveUserInfo(); + // 下载并保存远程头像 + Future _downloadAndSaveAvatar(String url) async { + try { + Logger().d('开始下载头像: $url'); + + final dio = Dio(BaseOptions( + headers: { + 'accept': '*/*', + 'Authorization': _storage.read("logintoken"), + }, + )); + + final response = await dio.get( + url, + options: Options(responseType: ResponseType.bytes), + ); + + Logger().d('下载状态: ${response.statusCode}'); + + if (response.statusCode == 200) { + // 保存到应用文档目录 + final appDir = await getApplicationDocumentsDirectory(); + final fileName = 'avatar_${DateTime.now().millisecondsSinceEpoch}.jpg'; + final localPath = '${appDir.path}/$fileName'; + + // 写入文件 + final file = File(localPath); + 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 updateUserAvatar() async { + // final XFile? image = await _picker.pickImage( + // source: ImageSource.gallery, + // maxWidth: 800, + // maxHeight: 800, + // imageQuality: 85, + // ); + + // if (image != null) { + // userInfo.update((user) { + // if (user != null) { + // user.avatar = image.path; + // } + // }); + // await saveUserInfo(); + // } + // } + Future updateUserName(String name) async { if (name.trim().isNotEmpty) { userInfo.update((user) { diff --git a/lib/modules/settings/views/settings_view.dart b/lib/modules/settings/views/settings_view.dart index 01c8e0243..564cf4443 100644 --- a/lib/modules/settings/views/settings_view.dart +++ b/lib/modules/settings/views/settings_view.dart @@ -110,13 +110,55 @@ class SettingsView extends GetView { // 裁剪圆形头像 borderRadius: BorderRadius.circular(30.r), child: controller.userInfo.value.avatar.isEmpty - ? Image.network( - 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=80', - fit: BoxFit.cover, // 图片填充方式 + ? + // Image.network( + // 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=80', + // fit: BoxFit.cover, // 图片填充方式 + // ) + Container( + decoration: BoxDecoration( + color: Colors.blue[600], + borderRadius: BorderRadius.circular(30.r), + ), + child: Center( + child: Text( + controller.userInfo.value.name.isNotEmpty + ? controller.userInfo.value.name[0] + .toUpperCase() + : '?', + style: TextStyle( + color: Colors.white, + fontSize: 24.sp, + fontWeight: FontWeight.bold, + ), + ), + ), ) : Image.network( controller.userInfo.value.avatar, - fit: BoxFit.cover, // 图片填充方式 + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + // 当图片加载失败时显示用户名首字母 + return Container( + decoration: BoxDecoration( + color: Colors.blue[600], + borderRadius: BorderRadius.circular(30.r), + ), + child: Center( + child: Text( + controller.userInfo.value.name.isNotEmpty + ? controller.userInfo.value.name[0] + .toUpperCase() + : '?', + style: TextStyle( + color: Colors.white, + fontSize: 24.sp, + fontWeight: FontWeight.bold, + ), + ), + ), + ); + }, )), ), )), @@ -328,6 +370,9 @@ class SettingsView extends GetView { ), onTap: () { // 打开备份设置页面 + print('---------用户头像-${controller.userInfo.value.avatar}'); + print('---------用户名-${controller.userInfo.value.name}'); + //print('user${User.instance.value}'); }, isDarkMode: isDarkMode, ), @@ -346,15 +391,15 @@ class SettingsView extends GetView { : Colors.grey[200]), Obx( () => _buildNavigationSetting( - title: '音色服务', - subtitle: '选择音色语音合成服务', + title: 'timbreService'.tr, // 音色服务 + subtitle: 'timbreServiceInfo'.tr, // 选择音色服务 icon: Icons.photo_camera_front_outlined, iconBgColor: isDarkMode ? Colors.cyan[900]!.withOpacity(0.3) : Colors.cyan[100]!, iconColor: isDarkMode ? Colors.cyan[300]! : Colors.cyan[600]!, - value: controller.timbreName.value, + value: controller.timbreName.value.tr, valueStyle: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.white70 : Colors.grey[500], diff --git a/lib/modules/timbre/controllers/timbre_controller.dart b/lib/modules/timbre/controllers/timbre_controller.dart index 4514d2aa2..62d2cec06 100644 --- a/lib/modules/timbre/controllers/timbre_controller.dart +++ b/lib/modules/timbre/controllers/timbre_controller.dart @@ -51,15 +51,15 @@ class TimbreController extends GetxController { } else { dataList.add({ 'type': '1', - 'name': '默认音色', - 'description': '微软云端音色服务', + 'name': 'defaultTimbre', + 'description': 'microsoftCloudTimbreService', 'isUsable': true }); _storage.write('timbreList', [ { 'type': '1', - 'name': '默认音色', - 'description': '微软云端音色服务', + 'name': 'defaultTimbre', + 'description': 'microsoftCloudTimbreService', 'isUsable': true } ]); diff --git a/lib/modules/timbre/views/timbre_view.dart b/lib/modules/timbre/views/timbre_view.dart index dd82c8d55..9073b1dcd 100644 --- a/lib/modules/timbre/views/timbre_view.dart +++ b/lib/modules/timbre/views/timbre_view.dart @@ -137,7 +137,7 @@ class TimbreView extends GetView { const Icon(Icons.keyboard_voice, color: Colors.white), 5.horizontalSpace, Text( - '声音复刻', + 'voiceClone'.tr, style: TextStyle(color: Colors.white, fontSize: 16.sp), ), ], @@ -180,7 +180,7 @@ class TimbreView extends GetView { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - data['name'], + '${data['name']}'.tr, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, @@ -190,7 +190,7 @@ class TimbreView extends GetView { ), SizedBox(height: 4.h), Text( - data['description'], + '${data['description']}'.tr, style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.white70 : Colors.black54,