diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index aeb0fa019..bd1fef2f5 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -57,6 +57,15 @@ android { enableV1Signing = true enableV2Signing = true } + getByName("debug") { + // debug 使用与 release 相同的签名配置 + keyAlias = keystoreProperties.getProperty("keyAlias") + keyPassword = keystoreProperties.getProperty("keyPassword") + storeFile = file(keystoreProperties.getProperty("storeFile")) + storePassword = keystoreProperties.getProperty("storePassword") + enableV1Signing = true + enableV2Signing = true + } } buildTypes { @@ -68,6 +77,9 @@ android { signingConfig = signingConfigs.getByName("release") // signingConfig = signingConfigs.getByName("debug") } + debug { + signingConfig = signingConfigs.getByName("debug") + } } } diff --git a/lib/core/bindings/initial_binding.dart b/lib/core/bindings/initial_binding.dart index 1dc794dfd..42752c7d6 100644 --- a/lib/core/bindings/initial_binding.dart +++ b/lib/core/bindings/initial_binding.dart @@ -13,10 +13,10 @@ class InitialBinding extends Bindings { @override void dependencies() { Logger.warning('InitialBinding dependencies'); - + // 语言管理器(需要最先初始化) Get.put(LanguageManager(), permanent: true); - + //初始化语音工厂 Get.put(SpeechFactory(), permanent: true); Get.find().initialize(initialType: SpeechServiceType.azure); @@ -25,17 +25,25 @@ class InitialBinding extends Bindings { Get.lazyPut(() => ChatHistoryService(), fenix: true); // 火山翻译服务 - Get.lazyPut(() => VolcanoTranslationService(), fenix: true); - + Get.lazyPut(() => VolcanoTranslationService(), + fenix: true); + // 火山AI服务 Get.lazyPut(() => VolcanoAIService(), fenix: true); // 注意:VoiceInteractionService 在 HomeController 中初始化 - Get.lazyPut(() => VoiceInteractionService(), fenix: true); - - // 经典蓝牙服务 - Get.lazyPut(() => ClassicBluetoothService(), fenix: true); + Get.lazyPut(() => VoiceInteractionService(), + fenix: true); + // 经典蓝牙服务 + Get.lazyPut(() => ClassicBluetoothService(), + fenix: true); + // // 语言管理器(需要最先初始化) + // Get.put(LoginService(), permanent: true); + // await LoginService().FirebaseInit(); + // print('资源FirebaseInit加载错误: 完成'); + // await LoginService().WeChatInit(); + // print('资源FWeChatInit加载错误: 完成'); } -} \ No newline at end of file +} diff --git a/lib/data/models/user_Info.dart b/lib/data/models/user_Info.dart index 3f369b0c0..13132bbf6 100644 --- a/lib/data/models/user_Info.dart +++ b/lib/data/models/user_Info.dart @@ -53,11 +53,13 @@ class User { factory User.fromJson(Map json) { // 修复1:安全获取嵌套的 user 字段 - final userData = json['data']['user'] as Map?; // 允许空值 - final token = json['data']['token'] as String? ?? ''; + final userData = json['user'] as Map?; // 允许空值 + //final token = json['data']['token'] as String? ?? ''; + //final token = ''; // 更新单例实例 return _instance = User._( - token: token as String? ?? '', + token: '', + //token: token as String? ?? '', name: (userData?['name'] as String?) ?? '默认名称', // 提供默认值 uid: (userData?['uid'] as String?) ?? '', avatar: (userData?['avatar'] as String?) ?? '', @@ -79,10 +81,10 @@ class User { factory User.fronTokenJson(Map json) { // 修复1:安全获取嵌套的 user 字段 final userData = json['user'] as Map?; // 允许空值 - final token = ''; + print("数据获取fronTokenJson${userData}"); // 更新单例实例 return _instance = User._( - token: token as String? ?? '', + token: '', name: (userData?['name'] as String?) ?? '默认名称', // 提供默认值 uid: (userData?['uid'] as String?) ?? '', avatar: (userData?['avatar'] as String?) ?? '', @@ -122,11 +124,4 @@ class User { // 'facebookopenid': facebookopenid, } }; - - // 获取用户名的快捷方法 - static String getname() => instance.name; - static String getdeviceInfo() => instance.deviceid; - static String getuserId() => instance.uid; - static String getstanding() => ""; - static String getnavatar() => instance.avatar; } diff --git a/lib/data/services/login_service.dart b/lib/data/services/login_service.dart index 705a37353..d09ce9672 100644 --- a/lib/data/services/login_service.dart +++ b/lib/data/services/login_service.dart @@ -12,14 +12,25 @@ class LoginService { return ''; })); - if (Firebase.apps.isEmpty) { - Firebase.initializeApp( - name: 'DeepSound', - options: DefaultFirebaseOptions.currentPlatform, - //demoProjectId:'1:907754492821:android:2deffc5aa1037ae2145c9c' - ).then((value) { - print('Firebase Initialized:${value.options}'); - }); + _initializeFirebase(); + } + + Future _initializeFirebase() async { + try { + // 尝试获取已存在的实例 + Firebase.app('DeepSound'); + print('已存在的 Firebase 实例被重用'); + } catch (e) { + try { + // 如果不存在则初始化新实例 + await Firebase.initializeApp( + name: 'DeepSound', + options: DefaultFirebaseOptions.currentPlatform, + ); + print('Firebase 初始化成功'); + } catch (e) { + print('Firebase 初始化失败: $e'); + } } } } diff --git a/lib/data/services/network/api.dart b/lib/data/services/network/api.dart index 48e3758c1..11c361580 100644 --- a/lib/data/services/network/api.dart +++ b/lib/data/services/network/api.dart @@ -4,7 +4,7 @@ import 'nw_method.dart'; class Api { // 登录 static login([params]) { - print("请求后台数据params=${params}"); + print("请求后台数据login params=${params}"); return DioManager().request( NWMethod.post, '/api/home/user_sgin', @@ -22,7 +22,6 @@ class Api { } static tokenlogin([params]) { - print("请求后台数据params=${params}"); return DioManager().request( NWMethod.post, '/api/home/user_getinfo', @@ -38,4 +37,13 @@ class Api { params: params, ); } + + //更新用户设置 + static updateUserSetting([params]) { + return DioManager().request( + NWMethod.post, + '/api/home/user_setting', + params: params, + ); + } } diff --git a/lib/data/services/network/dio_manager.dart b/lib/data/services/network/dio_manager.dart index 10952e905..0872436ec 100644 --- a/lib/data/services/network/dio_manager.dart +++ b/lib/data/services/network/dio_manager.dart @@ -21,7 +21,7 @@ class DioManager { dio = Dio(options); //fdp - // dio.interceptors.add(AuthInterceptor()); + dio.interceptors.add(AuthInterceptor()); } // 请求,返回参数为 T diff --git a/lib/main.dart b/lib/main.dart index dc5373757..e08049e9d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -24,7 +24,7 @@ void main() async { // 初始化环境变量 await dotenv.load(); LoginService(); - + //LoginService.FirebaseInit(); // 设置窗口大小(仅在 macOS 下生效) if (Platform.isMacOS) { const double width = 375.0; diff --git a/lib/modules/login/controllers/login_controller.dart b/lib/modules/login/controllers/login_controller.dart index 2f8720cc7..ae5a585bb 100644 --- a/lib/modules/login/controllers/login_controller.dart +++ b/lib/modules/login/controllers/login_controller.dart @@ -67,7 +67,6 @@ class LoginController extends GetxController { _contactValue.value = contactController.text; _formatDisplayContact(); - _startCountdown(); _sendVerificationCode(); } @@ -133,7 +132,7 @@ class LoginController extends GetxController { _contactValue.value = contactController.text; _formatDisplayContact(); _viewState.value = LoginViewState.verification; - _startCountdown(); + _sendVerificationCode(); } @@ -185,14 +184,26 @@ class LoginController extends GetxController { Future _sendVerificationCode() async { // 这里应该是实际发送验证码的逻辑 // 为了演示,我们只是模拟发送 - var response = await Api.getCode({ - "addr": contactController.text, - "vtype": 0, - }); - if (response != null) { - print("请求后台数据验证码params=${response}"); - //短信已发送 - //EasyLoading.showInfo('codeSend'.tr); + if (isEmialContactController() || isPhoneContactController()) { + _startCountdown(); + var response = await Api.getCode({ + "addr": contactController.text, + "vtype": 0, + }); + if (response != null) { + print("请求后台数据验证码params=${response}"); + //短信已发送 + //EasyLoading.showInfo('codeSend'.tr); + } + } else { + Get.snackbar( + '提示', + '请正确的手机号码或邮箱', + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, + ); + return; } } @@ -205,6 +216,7 @@ class LoginController extends GetxController { // 更新验证码输入 void updateVerificationCode(int index, String value) { + print("updateVerificationCode=${index}${value}"); if (index >= 0 && index < 4) { var newList = [..._verificationCode]; newList[index] = value; @@ -217,40 +229,32 @@ class LoginController extends GetxController { _verificationCode.value = ['', '', '', '']; } - // 检查验证码是否完整 - bool isVerificationCodeComplete() { - return !_verificationCode.contains(''); + // 检查邮箱格式 + bool isEmialContactController() { + // 检查每个元素是否为单个字母或数字字符 + final text = contactController.text; + return (text.length > 5 && + RegExp(r'^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') + .hasMatch(text)); } - Future getLoginToken() async { - // 使用正确语法创建请求头 - var response = await Api.tokenlogin(); - if (response != null) { - if (response?["code"] == null) { - final user = User.fronTokenJson(response); - await _storage.write( - 'user', jsonEncode(user.toJson())); // 使用 user.toJson() - Get.offAllNamed(Routes.pairing); - } - } else { - login(); - } +// 检查手机号码格式 + bool isPhoneContactController() { + // 检查每个元素是否为单个字母或数字字符 + final text = contactController.text; + return RegExp(r'^[0-9]+$').hasMatch(text); + } + + // 检查验证码是否完整 + bool isVerificationCodeComplete() { + // 检查每个元素是否为单个字母或数字字符 + final text = verificationCodeController.text; + print("数据获取Facebook${text.length}"); + return text.length == 4 && RegExp(r'^[0-9]+$').hasMatch(text); } // 登录 Future login() async { - // 暂时关闭验证码验证 - // if (!isVerificationCodeComplete()) { - // Get.snackbar( - // '提示', - // '请输入完整的验证码', - // snackPosition: SnackPosition.TOP, - // backgroundColor: Colors.red.withOpacity(0.1), - // colorText: Colors.red, - // ); - // return; - // } - _isLoading.value = true; try { @@ -260,16 +264,16 @@ class LoginController extends GetxController { switch (loginType) { case 0 || 1: { - // if (!isVerificationCodeComplete()) { - // Get.snackbar( - // '提示', - // '请输入完整的验证码', - // snackPosition: SnackPosition.TOP, - // backgroundColor: Colors.red.withOpacity(0.1), - // colorText: Colors.red, - // ); - // break; - // } + if (!isVerificationCodeComplete()) { + Get.snackbar( + '提示', + '请输入完整的验证码', + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, + ); + break; + } requestLogin({ "mail": contactController.text, "phone": "", @@ -425,21 +429,21 @@ class LoginController extends GetxController { try { print('响应数据: ${response.toString()}'); // 调试输出原始数据 - if (response['code'].toString() == '0') { + if (response['token'].toString() != '') { // 新增:检查 user 字段是否存在且类型正确 - if (response.containsKey('data') && response['data'] is Map) { - final user = User.fromJson(response); - print('用户初始化成功: ${user.token}'); - await _storage.write("logintoken", user.token); - // 存储到 SharedPreferences (仅存储用户数据) - //final prefs = await GetStorage(); - await _storage.write( - 'user', jsonEncode(user.toJson())); // 使用 user.toJson() - - Get.offAllNamed(Routes.pairing); - } else { - throw FormatException('API 响应缺少 user 字段'); - } + // if (response.containsKey('data') && response['data'] is Map) { + final user = User.fromJson(response); + print('用户初始化成功: ${response['token'].toString()}'); + await _storage.write("logintoken", response['token'].toString()); + // 存储到 SharedPreferences (仅存储用户数据) + //final prefs = await GetStorage(); + await _storage.write( + 'user', jsonEncode(user.toJson())); // 使用 user.toJson() + + Get.offAllNamed(Routes.pairing); + // } else { + // throw FormatException('API 响应缺少 user 字段'); + // } } else { throw Exception('接口返回错误: ${response['msg']}'); } diff --git a/lib/modules/profile/controllers/profile_controller.dart b/lib/modules/profile/controllers/profile_controller.dart index 5f6e09933..d2cb75f27 100644 --- a/lib/modules/profile/controllers/profile_controller.dart +++ b/lib/modules/profile/controllers/profile_controller.dart @@ -1,7 +1,7 @@ import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:flutter/material.dart'; -import 'package:http/http.dart'; +//import 'package:http/http.dart'; import 'package:image_picker/image_picker.dart'; import 'package:logger/logger.dart'; import '../../../data/models/user_Info.dart'; @@ -9,6 +9,9 @@ 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'; class ProfileController extends GetxController { static ProfileController get to => Get.find(); @@ -19,11 +22,11 @@ class ProfileController extends GetxController { // final _isEditing = false.obs; // final _storage = GetStorage(); //用户信息 - static const String userKey = 'user_info'; - final _storage = GetStorage(); - final ImagePicker _picker = ImagePicker(); - final Rx userInfo = User.instance.obs; - //final RxString localAvatar = ''.obs; + static const String userKey = 'user_info'; //用户信息存储键 + final _storage = GetStorage(); //本地存储 + final ImagePicker _picker = ImagePicker(); //图片选择器 + final Rx userInfo = User.instance.obs; // 响应式用户信息对象,使用单例模式确保全局唯一性 + final RxString localAvatar = ''.obs; // 本地头像路径,便于响应式更新UI // 使用响应式变量 final currentLanguage = 'zh'.obs; @@ -119,35 +122,60 @@ class ProfileController extends GetxController { try { // 准备数据 Map data = { - "image": "", // 默认空字符串 - "name": userInfo.value.name, + "image": "", // 默认空字符串 "language": currentLanguage.value, + "name": userInfo.value.name, }; - // 如果有头像,转换为base64 - if (userInfo.value.avatar.isNotEmpty) { - File imageFile = File(userInfo.value.avatar); - List imageBytes = await imageFile.readAsBytes(); - String base64Image = base64Encode(imageBytes); - data["image"] = base64Image; - } + // // 如果有头像,转换为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'); + // } - // 调用API - final response = await GetConnect().post( - 'https://www.deapsound.com:443/api/home/user_setting', + // } + + final dio = Dio(BaseOptions( + baseUrl: 'https://www.deapsound.com:443', headers: { 'accept': 'application/json', + 'Authorization': _storage.read("logintoken"), 'Content-Type': 'application/json', }, - data, + )); + + // 调用API前打印token + Logger().d('当前用户token: ${userInfo.value.token}'); + Logger().d('请求数据: ${{"user": data}}'); + final response = await dio.post( + '/api/home/user_setting', + data: data, ); + // 调用API + //final response = await Api.updateUserSetting(data); - if (response.statusCode != 200) { - throw '更新失败:${response.body}'; + if (response.statusCode == 200) { + if (response.data['code'] == 0) { + Logger().d('更新成功: ${response.data}'); + Get.snackbar('成功', '同步到服务器成功'); + } else { + throw '更新失败:${response.data['msg'] ?? '未知错误'}'; + } + } else { + throw '服务器响应错误: ${response.statusCode}'; } } catch (e) { Logger().e('同步到后端失败: $e'); - Get.snackbar('错误','同步到服务器失败,请重试'); + Get.snackbar('错误', '同步到服务器失败,请重试'); throw e; } } @@ -156,6 +184,37 @@ class ProfileController extends GetxController { final userJson = _storage.read(userKey); if (userJson != null) { userInfo.value = User.instance; + // 如果有远程头像地址,下载并保存到本地 + if (userInfo.value.avatar.isNotEmpty) { + _downloadAndSaveAvatar(userInfo.value.avatar); + } + } + } + + // 下载并保存远程头像 + Future _downloadAndSaveAvatar(String url) async { + try { + final response = await GetConnect().get(url); + if (response.status.isOk && response.body != null) { + // 保存到应用文档目录 + final appDir = await getApplicationDocumentsDirectory(); + final fileName = 'avatar_${DateTime.now().millisecondsSinceEpoch}.jpg'; + final localPath = '${appDir.path}/$fileName'; + + // 写入文件 + final file = File(localPath); + final List bytes = response.body.runtimeType == String + ? base64Decode(response.body.toString()) + : response.body; + await file.writeAsBytes(bytes); + + // 更新本地头像路径 + localAvatar.value = localPath; + await _storage.write('local_avatar', localPath); + Logger().d('头像已下载到本地: $localPath'); + } + } catch (e) { + Logger().e('下载头像失败: $e'); } } @@ -163,6 +222,7 @@ class ProfileController extends GetxController { await _storage.write(userKey, userInfo.value.toJson()); } + // 更新用户头像 Future updateUserAvatar() async { final XFile? image = await _picker.pickImage( source: ImageSource.gallery, @@ -203,19 +263,24 @@ class ProfileController extends GetxController { user.avatar = image.path; } }); - await saveUserInfo();//保存到本地 - await _syncWithBackend();//同步到后端 + + // 更新本地头像路径 + localAvatar.value = image.path; + await _storage.write('local_avatar', image.path); + // await saveUserInfo(); //保存到本地 + await _syncWithBackend(); //同步到后端 // 通知 SettingsController 更新 - if (Get.isRegistered()) { - Get.find().loadUserInfo(); - } + // if (Get.isRegistered()) { + // Get.find().loadUserInfo(); + // } } } + // 更新用户名 Future updateUserName(String name) async { if (name.trim().isEmpty) { - Logger().e('Invalid name: $name'); + Logger().e('无效名称: $name'); return; } @@ -223,21 +288,21 @@ class ProfileController extends GetxController { userInfo.update((user) { if (user != null) { user.name = name.trim(); - Logger().d('Name updated to: ${user.name}'); + Logger().d('名称更新为: ${user.name}'); } }); - await saveUserInfo();//保存到本地 - await _syncWithBackend();//同步到后端 + //await saveUserInfo(); //保存到本地 + await _syncWithBackend(); //同步到后端 - // 同步更新 SettingsController + //同步更新 SettingsController if (Get.isRegistered()) { - final settingsController = Get.find(); + final settingsController = Get.find(); settingsController.loadUserInfo(); } } catch (e) { - Logger().e('Error updating name: $e'); - Get.snackbar('错误', 'Failed to update user name'); + Logger().e('更新名称错误: $e'); + Get.snackbar('错误', '更新用户名失败'); } } } diff --git a/lib/modules/profile/views/profile_view.dart b/lib/modules/profile/views/profile_view.dart index e66e0bdcf..a74e8ae21 100644 --- a/lib/modules/profile/views/profile_view.dart +++ b/lib/modules/profile/views/profile_view.dart @@ -169,10 +169,12 @@ class EditProfileView extends GetView { children: [ Obx(() => CircleAvatar( radius: 50.r, - backgroundImage: controller - .userInfo.value.avatar.isNotEmpty - ? NetworkImage(controller.userInfo.value.avatar) - : null, + backgroundImage: controller.localAvatar.value.isNotEmpty + ? FileImage(File(controller.localAvatar.value)) + : (controller.userInfo.value.avatar.isNotEmpty + ? NetworkImage( + controller.userInfo.value.avatar) + : null) as ImageProvider?, child: controller.userInfo.value.avatar.isEmpty ? Icon(Icons.person, size: 50.r) : null, @@ -205,10 +207,10 @@ class EditProfileView extends GetView { controller: nameController, decoration: InputDecoration( labelText: 'nickname'.tr, - border: OutlineInputBorder(), + border: const OutlineInputBorder(), suffixIcon: nameController.text.isNotEmpty ? IconButton( - icon: Icon(Icons.clear), + icon: const Icon(Icons.clear), onPressed: () { nameController.clear(); }, diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index 6730e3aba..a34b590f3 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -60,11 +60,11 @@ class SettingsController extends GetxController { ]; //用户信息 - static const String userKey='user_info'; + static const String userKey = 'user_info'; //final _storage=GetStorage(); - final ImagePicker _picker=ImagePicker(); - final Rx userInfo=User.instance.obs;//使用单例实例 - + final ImagePicker _picker = ImagePicker(); + final Rx userInfo = User.instance.obs; //使用单例实例 + @override void onInit() { super.onInit(); @@ -103,7 +103,7 @@ class SettingsController extends GetxController { maxHeight: 800, imageQuality: 85, ); - + if (image != null) { userInfo.update((user) { if (user != null) { @@ -205,6 +205,7 @@ class SettingsController extends GetxController { void signOut() { // 在此实现退出登录逻辑 + _storage.write("logintoken", ""); Get.offAllNamed('/login'); } } diff --git a/lib/modules/splash/controllers/splash_controller.dart b/lib/modules/splash/controllers/splash_controller.dart index 6883c6487..c52535c3d 100644 --- a/lib/modules/splash/controllers/splash_controller.dart +++ b/lib/modules/splash/controllers/splash_controller.dart @@ -1,50 +1,49 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:get_storage/get_storage.dart'; +import '../../../data/models/user_Info.dart'; import '../../../routes/app_routes.dart'; import '../../../core/themes/theme_service.dart'; +import '../../../data/services/network/api.dart'; class SplashController extends GetxController { + final GetStorage _storage = GetStorage(); // 进度条动画控制 final _loadingProgress = 0.0.obs; double get loadingProgress => _loadingProgress.value; - // 版本号 - final String version = 'v1.0.0'; - // 资源加载状态 final _isResourceLoaded = false.obs; bool get isResourceLoaded => _isResourceLoaded.value; + // 版本号 + final String version = 'v1.0.0'; + // 主题服务 final ThemeService _themeService = ThemeService(); @override void onInit() { super.onInit(); - - // 确保页面先渲染,再执行其他操作 WidgetsBinding.instance.addPostFrameCallback((_) { - // 启动进度条动画 _startLoadingAnimation(); - - // 异步加载应用资源 _loadAppResources(); }); } - // 进度条动画 + // 进度条动画(持续2秒) void _startLoadingAnimation() { - // 模拟0.5秒的加载进度 - Timer.periodic(const Duration(milliseconds: 5), (timer) { + const totalDuration = 2000; // 动画总时长2秒 + const steps = totalDuration ~/ 50; // 40次更新(50ms间隔) + final stepValue = 1.0 / steps; + + Timer.periodic(const Duration(milliseconds: 50), (timer) { if (_loadingProgress.value < 1.0) { - _loadingProgress.value += 0.01; + _loadingProgress.value += stepValue; } else { timer.cancel(); - // 当进度条动画完成且资源加载完毕时,跳转到登录页面 - if (_isResourceLoaded.value) { - _navigateToLogin(); - } + _checkNavigateCondition(); } }); } @@ -52,37 +51,63 @@ class SplashController extends GetxController { // 异步加载应用资源 Future _loadAppResources() async { try { - // 在这里可以添加应用初始化需要的资源加载 - // 例如:预加载图片、初始化数据库、加载配置等 - - // 模拟耗时操作 - await Future.delayed(const Duration(milliseconds: 100)); + // 模拟资源加载(如API配置、本地缓存等) + await Future.delayed(const Duration(milliseconds: 1500)); - // TODO: 在此处添加实际的资源加载代码 + // 实际资源加载代码 + // await _loadImages(); + // await _initDatabase(); - // 标记资源加载完成 _isResourceLoaded.value = true; - - // 如果进度条已经走完,则跳转到登录页面 - if (_loadingProgress.value >= 1.0) { - _navigateToLogin(); - } + _checkNavigateCondition(); } catch (e) { - // 处理资源加载错误 - // 即使加载失败,也应该允许用户继续使用应用 - _isResourceLoaded.value = true; - - // 记录错误日志 + Get.snackbar('错误', '资源加载失败,请检查网络连接'); + _isResourceLoaded.value = true; // 允许用户继续使用 print('资源加载错误: $e'); } } - // 跳转到登录页面 - void _navigateToLogin() { - // 添加一个小延迟,确保UI完全渲染 - Future.delayed(const Duration(milliseconds: 200), () { - Get.offAllNamed(Routes.login); - }); + // 检查跳转条件 + void _checkNavigateCondition() { + if (_loadingProgress.value >= 1.0 && _isResourceLoaded.value) { + _navigateToLogin(); + } + } + + // 获取登录Token并跳转 + Future _navigateToLogin() async { + final isLoggedIn = await getLoginToken(); + if (isLoggedIn) { + // Get.offAllNamed(Routes.login); // 跳转到登录页 + // Get.offAllNamed(Routes.home); // 跳转到主页 + Get.offAllNamed(Routes.pairing); + } else { + Get.offAllNamed(Routes.login); // 跳转到登录页 + } + } + + Future getLoginToken() async { + try { + print('getLoginToken:进来了'); + //if (User.getname() != "") { + final response = await Api.tokenlogin(); + print('getLoginToken: $response'); + if (response == null) return false; + + if (response['user'] != '') { + print('getLoginToken:进来了'); + // 根据API设计调整 + final user = User.fromJson(response); + // 存储用户信息(示例) + await _storage.write('user_info', user.toJson()); + return true; + } + //return true; + //} + return false; + } catch (e) { + print('Token获取失败: $e'); + return false; + } } - // fdp挑转登陆 } diff --git a/lib/modules/timbre/bindings/timbre_binding.dart b/lib/modules/timbre/bindings/timbre_binding.dart new file mode 100644 index 000000000..6883836f7 --- /dev/null +++ b/lib/modules/timbre/bindings/timbre_binding.dart @@ -0,0 +1,9 @@ +import 'package:get/get.dart'; +import '../controllers/timbre_controller.dart'; + +class TimbreBinding extends Bindings { + @override + void dependencies() { + Get.put(TimbreController()); + } +} diff --git a/lib/modules/timbre/controllers/timbre_controller.dart b/lib/modules/timbre/controllers/timbre_controller.dart new file mode 100644 index 000000000..3fd12cf53 --- /dev/null +++ b/lib/modules/timbre/controllers/timbre_controller.dart @@ -0,0 +1,14 @@ +import 'package:get/get.dart'; + +class TimbreController extends GetxController { + RxList dataList = [ + {'type': '1', 'name': '默认音色', 'description': '微软云端音色服务'}, + {'type': '2', 'name': '声音复刻', 'description': '复刻本人音色服务'} + ].obs; + RxString type = '1'.obs; + + @override + void onInit() { + super.onInit(); + } +} diff --git a/lib/modules/timbre/views/timbre_view.dart b/lib/modules/timbre/views/timbre_view.dart new file mode 100644 index 000000000..37bdd8d45 --- /dev/null +++ b/lib/modules/timbre/views/timbre_view.dart @@ -0,0 +1,168 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import '../../../core/theme/app_colors.dart'; +import '../controllers/timbre_controller.dart'; + +class TimbreView extends GetView { + const TimbreView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + + return Scaffold( + backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], + appBar: _buildAppBar(isDarkMode), + body: _buildBody(isDarkMode), + ); + } + + // 构建应用栏 + PreferredSizeWidget _buildAppBar(bool isDarkMode) { + return AppBar( + backgroundColor: isDarkMode ? Colors.black : Colors.white, + elevation: 0.5, + leading: IconButton( + icon: Icon( + Icons.arrow_back_ios, + color: isDarkMode ? Colors.white : Colors.black, + size: 20.sp, + ), + onPressed: () => Get.back(), + ), + title: Text( + '选择音色', + style: TextStyle( + fontSize: 17.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black, + ), + ), + centerTitle: true, + ); + } + + // 构建主体内容 + Widget _buildBody(bool isDarkMode) { + return Column( + children: [ + Expanded( + child: Obx( + () => ListView.builder( + itemCount: controller.dataList.length, + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 5.w), + itemBuilder: (BuildContext context, int index) { + Map data = controller.dataList[index]; + return _item(isDarkMode, data); + }, + ), + ), + ), + GestureDetector( + onTap: () {}, + child: Container( + width: 130.w, + height: 48.w, + margin: EdgeInsets.symmetric(vertical: 20.w), + decoration: BoxDecoration( + color: isDarkMode ? AppColors.primary : Colors.blue, + borderRadius: BorderRadius.circular(12.w), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.keyboard_voice, color: Colors.white), + 5.horizontalSpace, + Text( + '声音复刻', + style: TextStyle(color: Colors.white, fontSize: 16.sp), + ), + ], + ), + ), + ), + ], + ); + } + + Widget _item(bool isDarkMode, Map data) { + return InkWell( + onTap: () { + controller.type.value = data['type']; + }, + child: Obx( + () => Container( + margin: EdgeInsets.symmetric(vertical: 6.h), + padding: EdgeInsets.all(12.w), + decoration: BoxDecoration( + color: controller.type.value == data['type'] + ? (isDarkMode + ? Colors.teal[900]!.withOpacity(0.3) + : Colors.teal[50]!) + : (isDarkMode + ? Colors.white.withOpacity(0.05) + : Colors.grey[100]!), + borderRadius: BorderRadius.circular(12.r), + border: Border.all( + color: controller.type.value == data['type'] + ? (isDarkMode ? Colors.teal[300]! : Colors.teal[600]!) + : Colors.transparent, + width: 1, + ), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + data['name'], + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w500, + color: isDarkMode ? Colors.white : Colors.black87, + ), + overflow: TextOverflow.ellipsis, + ), + SizedBox(height: 4.h), + Text( + data['description'], + style: TextStyle( + fontSize: 12.sp, + color: isDarkMode ? Colors.white70 : Colors.black54, + ), + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), + ], + ), + ), + Container( + width: 24.w, + height: 24.w, + margin: EdgeInsets.only(left: 12.w), + decoration: BoxDecoration( + color: controller.type.value == data['type'] + ? (isDarkMode ? Colors.teal[300] : Colors.teal[600]) + : (isDarkMode + ? Colors.white.withOpacity(0.1) + : Colors.grey[300]), + shape: BoxShape.circle, + ), + child: controller.type.value == data['type'] + ? Icon( + Icons.check, + size: 16.w, + color: Colors.white, + ) + : null, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/routes/app_pages.dart b/lib/routes/app_pages.dart index 0114183d9..1e9374b4b 100644 --- a/lib/routes/app_pages.dart +++ b/lib/routes/app_pages.dart @@ -12,6 +12,8 @@ import '../modules/profile/bindings/profile_binding.dart'; import '../modules/chat/bindings/chat_binding.dart'; import '../modules/chat/views/chat_view.dart'; +import '../modules/timbre/bindings/timbre_binding.dart'; +import '../modules/timbre/views/timbre_view.dart'; import '../modules/translation/views/translation_view.dart'; import '../modules/translation/bindings/translation_binding.dart'; import '../modules/translation/views/translation_history_view.dart'; @@ -93,6 +95,11 @@ abstract class AppPages { page: () => const MeetingRecordView(), binding: MeetingRecordBinding(), ), + GetPage( + name: Routes.timbre, + page: () => const TimbreView(), + binding: TimbreBinding(), + ), GetPage( name: Routes.settings, page: () => const SettingsView(), diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index 0504f2c71..2dcd625d8 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -9,6 +9,7 @@ abstract class Routes { static const profile = '/profile'; static const meeting = '/meeting'; static const meetingRecord = '/meetingRecord'; + static const timbre = '/timbre'; static const settings = '/settings'; static const speechDemo = '/speech_demo'; static const TTS_TEST = '/tts_test';