Browse Source

Merge branch 'new_dev' of https://github.com/deepcloud2048/deep_voice into new_dev

weicu
liwei1dao 1 year ago
parent
commit
f16c40fa89
  1. 4
      lib/core/bindings/initial_binding.dart
  2. 144
      lib/data/services/account_service.dart
  3. 249
      lib/modules/agent/views/agent_view.dart
  4. 55
      lib/modules/login/controllers/login_controller.dart
  5. 54
      lib/modules/login/views/login_view.dart
  6. 88
      lib/modules/meeting/controllers/meeting_controller.dart
  7. 1
      lib/modules/meeting/controllers/meeting_details_controller.dart
  8. 3
      lib/modules/meeting/controllers/meeting_record_controller.dart
  9. 8
      lib/modules/meeting/views/meeting_details_view.dart
  10. 2
      lib/modules/meeting/views/meeting_record_view.dart
  11. 106
      lib/modules/profile/controllers/profile_controller.dart
  12. 133
      lib/modules/translation/controllers/translation_list_controller.dart
  13. 274
      lib/modules/translation/views/translation_list_view.dart

4
lib/core/bindings/initial_binding.dart

@ -9,6 +9,7 @@ import '../../data/services/language_manager.dart';
import '../../core/utils/logger.dart'; import '../../core/utils/logger.dart';
import '../../data/services/bluetooth_manager.dart'; import '../../data/services/bluetooth_manager.dart';
import '../../data/services/ble_manager.dart'; import '../../data/services/ble_manager.dart';
import '../../data/services/account_service.dart';
/// 初始绑定,用于管理全局依赖 /// 初始绑定,用于管理全局依赖
class InitialBinding extends Bindings { class InitialBinding extends Bindings {
@ -42,6 +43,9 @@ class InitialBinding extends Bindings {
// 火山AI服务 // 火山AI服务
// Get.lazyPut<VolcanoAIService>(() => VolcanoAIService(), fenix: true); // Get.lazyPut<VolcanoAIService>(() => VolcanoAIService(), fenix: true);
// 账号管理服务
Get.lazyPut<AccountService>(() => AccountService(), fenix: true);
// 杰理蓝牙服务 // 杰理蓝牙服务
// // 语言管理器(需要最先初始化) // // 语言管理器(需要最先初始化)

144
lib/data/services/account_service.dart

@ -0,0 +1,144 @@
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:logger/logger.dart';
import '../../modules/login/controllers/googleLogin.dart';
import '../../modules/login/controllers/facebookLogin.dart';
import '../services/network/api.dart';
/// 账号管理服务
///
/// 负责处理所有与用户账号相关的操作,包括登出和账号删除
class AccountService extends GetxService {
final GetStorage _storage = GetStorage();
final logger = Logger();
/// 初始化服务
@override
void onInit() {
super.onInit();
logger.d('AccountService 已初始化');
}
/// 清理本地用户数据
///
/// 返回操作是否成功
Future<bool> clearUserData() async {
try {
// 清理所有存储
await _storage.erase();
// 或者逐个清理关键存储项
await _storage.write("logintoken", null);
await _storage.write("user_info", null);
await _storage.write("local_avatar", null);
logger.d('用户数据清理成功');
return true;
} catch (e) {
logger.e('用户数据清理失败: $e');
return false;
}
}
/// 退出第三方登录
///
/// 返回操作是否成功
Future<bool> logoutThirdParty() async {
try {
final google = Google();
final facebook = Facebook();
await Future.wait([
google.signOutGoogle(),
facebook.signOutFacebook(),
]);
logger.d('第三方登录退出成功');
return true;
} catch (e) {
logger.e('第三方登录退出失败: $e');
// 返回true,因为这不应阻止整体登出流程
return true;
}
}
/// 用户登出
///
/// 清理本地数据并退出第三方登录
/// 返回操作结果,包含成功状态和错误信息
Future<AccountOperationResult> signOut() async {
try {
// 清理本地数据
final dataCleaned = await clearUserData();
if (!dataCleaned) {
return AccountOperationResult(
success: false,
errorMessage: '清理用户数据失败',
);
}
// 退出第三方登录
await logoutThirdParty();
return AccountOperationResult(
success: true,
message: '已退出登录',
);
} catch (e) {
logger.e('登出过程发生错误: $e');
return AccountOperationResult(
success: false,
errorMessage: '退出登录失败,请重试',
);
}
}
/// 删除用户账号
///
/// 调用API删除账号,然后清理本地数据
/// 返回操作结果,包含成功状态和错误信息
Future<AccountOperationResult> deleteAccount() async {
try {
// 调用API删除账号
final response = await Api.deleteUser();
if (response == null) {
return AccountOperationResult(
success: false,
errorMessage: '删除用户数据失败',
);
}
// 清理本地数据
final dataCleaned = await clearUserData();
if (!dataCleaned) {
return AccountOperationResult(
success: false,
errorMessage: '清理用户数据失败',
);
}
// 退出第三方登录
await logoutThirdParty();
return AccountOperationResult(
success: true,
message: '账号已被永久删除',
);
} catch (e) {
logger.e('删除账号过程发生错误: $e');
return AccountOperationResult(
success: false,
errorMessage: '删除账号失败,请重试',
);
}
}
}
/// 账号操作结果
class AccountOperationResult {
final bool success;
final String? message;
final String? errorMessage;
AccountOperationResult({
required this.success,
this.message,
this.errorMessage,
});
}

249
lib/modules/agent/views/agent_view.dart

@ -168,34 +168,22 @@ class AgentView extends StatelessWidget {
final message = controller final message = controller
.messages[controller.messages.length - 1 - index]; .messages[controller.messages.length - 1 - index];
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.only(bottom: 8),
child: !message.isUser child: GestureDetector(
? GestureDetector( onLongPressStart: (LongPressStartDetails details) {
onLongPressStart: _onLongPressMessageContent(
(LongPressStartDetails details) { context, isDarkMode, details, message);
_onLongPressMessageContent( },
context, isDarkMode, details, message); child: MessageBubble(
}, isUser: message.isUser,
child: MessageBubble( message: message.text,
isUser: message.isUser, timestamp: message.timestamp,
message: message.text, isRecognizing: message.isRecognizing,
timestamp: message.timestamp, hasImage: message.hasImage,
isRecognizing: message.isRecognizing, imagePath: message.imagePath,
hasImage: message.hasImage, meta: message.meta,
imagePath: message.imagePath, ),
meta: message.meta, ));
),
)
: MessageBubble(
isUser: message.isUser,
message: message.text,
timestamp: message.timestamp,
isRecognizing: message.isRecognizing,
hasImage: message.hasImage,
imagePath: message.imagePath,
meta: message.meta,
),
);
}, },
), ),
), ),
@ -602,164 +590,36 @@ class AgentView extends StatelessWidget {
)); ));
}, },
), ),
// 自定义分隔线样式 if (!message.isUser) ...[
PopupMenuItem( // 自定义分隔线样式
height: 1, // 控制间距 PopupMenuItem(
enabled: false, height: 1, // 控制间距
child: Divider( enabled: false,
height: 1, child: Divider(
thickness: 0.8, height: 1,
indent: 0, thickness: 0.8,
endIndent: 0, indent: 0,
color: isDarkMode ? Colors.grey[700] : Colors.grey[300], endIndent: 0,
color: isDarkMode ? Colors.grey[700] : Colors.grey[300],
),
), ),
), _popupMenuEntry(
_popupMenuEntry( Icon(
Icon( Icons.report,
Icons.report, size: 20.w,
size: 20.w, color: isDarkMode ? Colors.red[700] : Colors.red,
color: isDarkMode ? Colors.red[700] : Colors.red, ),
'举报'.tr, // 举报
isDarkMode ? Colors.red[700]! : Colors.red,
() {
// 实现举报功能
final reportController = Get.find<ReportController>();
reportController.initReport(message);
_showReportForm(context, isDarkMode);
},
), ),
'举报'.tr, // 举报 ]
isDarkMode ? Colors.red[700]! : Colors.red,
() {
// 实现举报功能
final reportController = Get.find<ReportController>();
reportController.initReport(message);
_showReportForm(context, isDarkMode);
// showModalBottomSheet(
// context: context,
// isScrollControlled: true, // 关键:允许弹窗随键盘移动
// backgroundColor:
// isDarkMode ? AppColors.cardBackground : Colors.white,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.vertical(top: Radius.circular(16.r)),
// ),
// builder: (BuildContext context) {
// return Padding(
// // 添加底部内边距以容纳键盘
// padding: EdgeInsets.only(
// bottom: MediaQuery.of(context).viewInsets.bottom,
// ),
// child: Container(
// padding: EdgeInsets.all(16.w),
// child: SingleChildScrollView(
// // 添加滚动视图
// child: Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.stretch,
// children: [
// Stack(
// alignment: Alignment.center,
// children: [
// Center(
// child: Text(
// '举报',
// style: TextStyle(
// fontSize: 18.sp,
// fontWeight: FontWeight.bold,
// color: isDarkMode
// ? Colors.white
// : Colors.black,
// ),
// ),
// ),
// SizedBox(
// height: 20.w,
// ),
// Positioned(
// right: 0,
// child: IconButton(
// icon: Icon(
// Icons.close,
// color: isDarkMode
// ? Colors.grey[400]
// : Colors.black54,
// ),
// onPressed: () => Navigator.pop(context),
// ),
// ),
// ],
// ),
// Container(
// padding: EdgeInsets.symmetric(horizontal: 12.w),
// margin: EdgeInsets.only(bottom: 10.w),
// child: Divider(
// height: 1,
// color: Colors.grey[300],
// ),
// ),
// Wrap(
// spacing: 8.w,
// runSpacing: 8.h,
// children: [
// _buildReportChip('有害/不安全', isDarkMode),
// _buildReportChip('色情低俗', isDarkMode),
// _buildReportChip('误导攻击', isDarkMode),
// _buildReportChip('违法犯罪', isDarkMode),
// _buildReportChip('隐私相关', isDarkMode),
// _buildReportChip('侵犯我的著作权、名誉权等', isDarkMode),
// _buildReportChip('其他', isDarkMode),
// ],
// ),
// SizedBox(height: 16.h),
// Container(
// padding: EdgeInsets.all(12.w),
// decoration: BoxDecoration(
// color: isDarkMode
// ? Colors.grey[800]
// : Colors.grey[200],
// borderRadius: BorderRadius.circular(8.r),
// ),
// child: TextField(
// maxLines: 3,
// style: TextStyle(
// color: isDarkMode ? Colors.white : Colors.black,
// ),
// decoration: InputDecoration(
// hintText: '我们想知道你举报的原因,你可以描述你遇到的问题。',
// hintStyle: TextStyle(
// color: isDarkMode
// ? Colors.white70
// : Colors.black54,
// fontSize: 14.sp,
// ),
// border: InputBorder.none,
// ),
// ),
// ),
// SizedBox(height: 14.h),
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: Colors.blue,
// foregroundColor: Colors.white,
// padding: EdgeInsets.symmetric(vertical: 12.h),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(8.r),
// ),
// ),
// child: Text('提交'),
// onPressed: () {
// Navigator.pop(context);
// Get.snackbar(
// '提示',
// '举报已提交,我们会尽快处理',
// backgroundColor: Colors.green.withOpacity(0.8),
// colorText: Colors.white,
// );
// },
// ),
// SizedBox(height: 8.h),
// ],
// ),
// ),
// ),
// );
// },
// );
},
),
], ],
); );
} }
@ -784,27 +644,6 @@ class AgentView extends StatelessWidget {
); );
} }
// // 添加这个辅助方法来创建举报选项
// Widget _buildReportChip(String label, bool isDarkMode) {
// return ChoiceChip(
// label: Text(
// label,
// style: TextStyle(
// color: isDarkMode ? Colors.white : Colors.black87,
// fontSize: 14.sp,
// ),
// ),
// selected: false,
// backgroundColor: isDarkMode ? Colors.grey[800] : Colors.grey[200],
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(16.r),
// ),
// onSelected: (bool selected) {
// // 可以在这里处理选择逻辑
// },
// );
// }
PopupMenuEntry _popupMenuEntry( PopupMenuEntry _popupMenuEntry(
Icon icon, Icon icon,
String title, String title,

55
lib/modules/login/controllers/login_controller.dart

@ -201,6 +201,9 @@ class LoginController extends GetxController {
// 显示隐私政策弹窗 // 显示隐私政策弹窗
void _showPrivacyDialog() { void _showPrivacyDialog() {
// 获取当前主题模式
final bool isDarkMode = Get.theme.brightness == Brightness.dark;
Get.dialog( Get.dialog(
WillPopScope( WillPopScope(
onWillPop: () async => false, // 禁止通过返回键关闭弹窗 onWillPop: () async => false, // 禁止通过返回键关闭弹窗
@ -208,16 +211,20 @@ class LoginController extends GetxController {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
backgroundColor:
isDarkMode ? Colors.grey[800] : Colors.white, // 暗黑模式使用深灰色背景
child: Container( child: Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const Text( Text(
'DeapSound 声明', 'DeapSound 声明',
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color:
isDarkMode ? Colors.white : Colors.black, // 暗黑模式使用白色文字
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@ -226,9 +233,14 @@ class LoginController extends GetxController {
child: Text.rich( child: Text.rich(
TextSpan( TextSpan(
children: [ children: [
const TextSpan( TextSpan(
text: '欢迎使用DeapSound,请充分阅读', text: '欢迎使用DeapSound,请充分阅读',
style: TextStyle(fontSize: 14), style: TextStyle(
fontSize: 14,
color: isDarkMode
? Colors.white70
: Colors.black87, // 暗黑模式使用浅白色
),
), ),
WidgetSpan( WidgetSpan(
alignment: PlaceholderAlignment.middle, alignment: PlaceholderAlignment.middle,
@ -244,18 +256,21 @@ class LoginController extends GetxController {
title: 'privacyPolicy'.tr, title: 'privacyPolicy'.tr,
)); ));
}, },
child: Text( child: const Text(
'《隐私政策》', '《隐私政策》',
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: const Color(0xFF3B82F6), color: Color(0xFF3B82F6),
), ),
), ),
), ),
), ),
const TextSpan( TextSpan(
text: '和', text: '和',
style: TextStyle(fontSize: 14), style: TextStyle(
fontSize: 14,
color: isDarkMode ? Colors.white70 : Colors.black87,
),
), ),
WidgetSpan( WidgetSpan(
alignment: PlaceholderAlignment.middle, alignment: PlaceholderAlignment.middle,
@ -271,18 +286,21 @@ class LoginController extends GetxController {
title: 'userAgreement'.tr, title: 'userAgreement'.tr,
)); ));
}, },
child: Text( child: const Text(
'《用户协议》', '《用户协议》',
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: const Color(0xFF3B82F6), color: Color(0xFF3B82F6),
), ),
), ),
), ),
), ),
const TextSpan( TextSpan(
text: ',点击“同意并进入”表示您已充分阅读并同意上述协议。点击“不同意”将退出应用。', text: ',点击“同意并进入”表示您已充分阅读并同意上述协议。点击“不同意”将退出应用。',
style: TextStyle(fontSize: 14), style: TextStyle(
fontSize: 14,
color: isDarkMode ? Colors.white70 : Colors.black87,
),
), ),
], ],
), ),
@ -299,9 +317,17 @@ class LoginController extends GetxController {
exit(0); exit(0);
}, },
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: Colors.black54, foregroundColor: isDarkMode
? Colors.grey[400]
: Colors.black54, // 暗黑模式使用浅灰色
),
child: Text(
'不同意',
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.black54,
),
), ),
child: const Text('不同意'), //child: const Text('不同意'),
), ),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
@ -317,6 +343,9 @@ class LoginController extends GetxController {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
elevation: isDarkMode ? 4 : 2, // 暗黑模式下增加阴影效果提高辨识度
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 10), // 统一的内边距
), ),
child: const Text('同意并进入'), child: const Text('同意并进入'),
), ),

54
lib/modules/login/views/login_view.dart

@ -177,9 +177,23 @@ class LoginView extends GetView<LoginController> {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode color: isDarkMode
? Colors.grey[900]!.withOpacity(0.4) ? Colors.grey[850]!.withOpacity(0.6) // 更深的背景色,增加不透明度
: Colors.grey.withOpacity(0.1), : Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
border: Border.all(
color:
isDarkMode ? Colors.grey[700]! : Colors.transparent, // 暗黑模式添加边框
width: 1,
),
boxShadow: isDarkMode
? [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 4,
offset: const Offset(0, 2),
)
]
: [], // 暗黑模式添加阴影
), ),
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 4.h), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 4.h),
@ -213,32 +227,27 @@ class LoginView extends GetView<LoginController> {
Container( Container(
height: 20.h, height: 20.h,
width: 1, width: 1,
color: isDarkMode ? Colors.grey[800] : Colors.grey[300], color:
isDarkMode ? Colors.grey[600] : Colors.grey[300], // 调整分隔线颜色
margin: EdgeInsets.symmetric(horizontal: 8.w), margin: EdgeInsets.symmetric(horizontal: 8.w),
), ),
// Icon(
// Icons.person_outline,
// color: isDarkMode ? Colors.grey[400] : Colors.black54,
// size: 20.sp,
// ),
//SizedBox(width: 12.w),
Expanded( Expanded(
child: TextField( child: TextField(
controller: controller.contactController, controller: controller.contactController,
// keyboardType: controller.selectedCountryFlag == '✉️'
// ? TextInputType.emailAddress
// : TextInputType.phone,
decoration: InputDecoration( decoration: InputDecoration(
hintText: controller.selectedCountryFlag == '✉️' hintText: controller.selectedCountryFlag == '✉️'
? 'email'.tr // 需要在翻译文件中添加 ? 'email'.tr // 需要在翻译文件中添加
: 'phoneNumber'.tr, // 需要在翻译文件中添加 : 'phoneNumber'.tr, // 需要在翻译文件中添加
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.grey[600] : Colors.black38, color: isDarkMode
? Colors.grey[500]
: Colors.black38, // 调整提示文字颜色
), ),
border: InputBorder.none, border: InputBorder.none,
fillColor: Colors.transparent, fillColor: Colors.transparent,
filled: true, filled: true,
focusedBorder: InputBorder.none,
), ),
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
@ -322,9 +331,23 @@ class LoginView extends GetView<LoginController> {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode color: isDarkMode
? Colors.grey[900]!.withOpacity(0.4) ? Colors.grey[850]!.withOpacity(0.6) // 更深的背景色,增加不透明度
: Colors.grey.withOpacity(0.1), : Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
border: Border.all(
color:
isDarkMode ? Colors.grey[700]! : Colors.transparent, // 暗黑模式添加边框
width: 1,
),
boxShadow: isDarkMode
? [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 4,
offset: const Offset(0, 2),
)
]
: [], // 暗黑模式添加阴影
), ),
child: Padding( child: Padding(
padding: EdgeInsets.only(left: 16.w, right: 4.w, top: 4.h, bottom: 4.h), padding: EdgeInsets.only(left: 16.w, right: 4.w, top: 4.h, bottom: 4.h),
@ -345,12 +368,15 @@ class LoginView extends GetView<LoginController> {
hintText: 'verificationCode'.tr, // 验证码 hintText: 'verificationCode'.tr, // 验证码
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.grey[600] : Colors.black38, color: isDarkMode
? Colors.grey[500]
: Colors.black38, // 调整提示文字颜色
), ),
border: InputBorder.none, border: InputBorder.none,
counterText: '', counterText: '',
fillColor: Colors.transparent, fillColor: Colors.transparent,
filled: true, filled: true,
focusedBorder: InputBorder.none,
), ),
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,

88
lib/modules/meeting/controllers/meeting_controller.dart

@ -7,6 +7,7 @@ import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
@ -17,6 +18,8 @@ import '../../../core/utils/permission_util.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import '../../../routes/app_routes.dart';
class MeetingController extends GetxController { class MeetingController extends GetxController {
final ScrollController listController = ScrollController(); final ScrollController listController = ScrollController();
final AudioPlayer _audioPlayer = AudioPlayer(); final AudioPlayer _audioPlayer = AudioPlayer();
@ -38,6 +41,7 @@ class MeetingController extends GetxController {
List dataListTask = []; List dataListTask = [];
bool isDisposed = false; bool isDisposed = false;
String filePath = '';
final String _appid = '2316081933'; final String _appid = '2316081933';
final String _token = 'mcJTh6OelBqU_L78lgGd77Nyrc_-5-En'; final String _token = 'mcJTh6OelBqU_L78lgGd77Nyrc_-5-En';
@ -57,6 +61,15 @@ class MeetingController extends GetxController {
}; };
_getDataList(); _getDataList();
addMeetingAudio(); addMeetingAudio();
final Map<String, dynamic> args = Get.arguments ?? {};
// 检查参数是否存在且包含有效路径
if (args != null &&
args.containsKey('filePath') &&
args['filePath'] is String &&
args['filePath'].isNotEmpty) {
final String path = args['filePath'];
addAudio(path);
}
} }
@override @override
@ -251,6 +264,7 @@ class MeetingController extends GetxController {
// 移动文件 // 移动文件
await file.copy(newPath); await file.copy(newPath);
await file.delete(); await file.delete();
addQuantity++; addQuantity++;
} }
} catch (e) { } catch (e) {
@ -304,39 +318,53 @@ class MeetingController extends GetxController {
EasyLoading.dismiss(); EasyLoading.dismiss();
} }
void addAudio() async { void addAudio([String? filePath]) async {
EasyLoading.show(status: '正在导入...'); EasyLoading.show(status: '正在导入...');
FilePickerResult? result = await FilePicker.platform.pickFiles( String fileName = "";
withData: true, bool isParamProvided = filePath != null; // 更清晰的变量名
); if (filePath != null) {
if (result != null) { // 正确截取文件名(跨平台安全方式)
PlatformFile file = result.files.first; fileName = path.basename(filePath);
String filePath = await _saveFile(file); } else {
String fileName = file.name; FilePickerResult? result = await FilePicker.platform.pickFiles(
await _audioPlayer.setAudioSource(AudioSource.file(filePath)); withData: true,
var audiourl = );
await UploadOss.upload(filepath: filePath, rootDir: 'ExternalAudio'); if (result != null) {
int seconds = _audioPlayer.duration?.inSeconds ?? 0; PlatformFile file = result.files.first;
int creationTime = DateTime.now().millisecondsSinceEpoch; filePath = await _saveFile(file);
Map<String, Object?> data = { fileName = file.name;
'title': fileName,
'type': 'IMPORT',
'seconds': seconds,
'filepath': filePath,
'audiourl': audiourl,
'tasktype': 0,
'creationtime': creationTime,
};
final meetingId = await SqfliteApi.insertMeeting(data);
if (meetingId != null) {
dataList.insert(0, {
'id': meetingId,
'taskid': '',
...data,
});
addQuantity++;
} }
} }
await _audioPlayer.setAudioSource(AudioSource.file(filePath!));
var audiourl =
await UploadOss.upload(filepath: filePath, rootDir: 'ExternalAudio');
int seconds = _audioPlayer.duration?.inSeconds ?? 0;
int creationTime = DateTime.now().millisecondsSinceEpoch;
Map<String, Object?> data = {
'title': fileName,
'type': 'IMPORT',
'seconds': seconds,
'filepath': filePath,
'audiourl': audiourl,
'tasktype': 0,
'creationtime': creationTime,
};
final meetingId = await SqfliteApi.insertMeeting(data);
if (meetingId != null) {
dataList.insert(0, {
'id': meetingId,
'taskid': '',
...data,
});
addQuantity++;
}
if (isParamProvided) {
Get.toNamed(Routes.meetingDetails, arguments: {
'id': meetingId,
});
}
EasyLoading.dismiss(); EasyLoading.dismiss();
} }

1
lib/modules/meeting/controllers/meeting_details_controller.dart

@ -145,6 +145,7 @@ class MeetingDetailsController extends GetxController
} }
await playerController.preparePlayer( await playerController.preparePlayer(
path: meetingData.value.filepath, path: meetingData.value.filepath,
shouldExtractWaveform: false,
noOfSamples: 88, noOfSamples: 88,
); );
durationMax.value = await playerController.getDuration(DurationType.max); durationMax.value = await playerController.getDuration(DurationType.max);

3
lib/modules/meeting/controllers/meeting_record_controller.dart

@ -153,7 +153,8 @@ class MeetingRecordController extends GetxController
} }
/// Saves new title and exits editing mode /// Saves new title and exits editing mode
void saveTitleChange() { Future<void> saveTitleChange() async {
isEditingTitle.value = true;
if (isEditingTitle.value) { if (isEditingTitle.value) {
newName.value = titleEditingController.text; newName.value = titleEditingController.text;
isEditingTitle.value = false; isEditingTitle.value = false;

8
lib/modules/meeting/views/meeting_details_view.dart

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:common_utils/common_utils.dart'; import 'package:common_utils/common_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -108,6 +110,9 @@ class MeetingDetailsView extends GetView<MeetingDetailsController> {
child: AudioFileWaveforms( child: AudioFileWaveforms(
size: Size(350.w, 30.w), size: Size(350.w, 30.w),
playerController: controller.playerController, playerController: controller.playerController,
waveformData: List.generate(88, (int index) {
return 0.01 + Random().nextDouble() * 0.09;
}),
waveformType: WaveformType.fitWidth, waveformType: WaveformType.fitWidth,
enableSeekGesture: false, enableSeekGesture: false,
playerWaveStyle: PlayerWaveStyle( playerWaveStyle: PlayerWaveStyle(
@ -350,6 +355,9 @@ class MeetingDetailsView extends GetView<MeetingDetailsController> {
child: AudioFileWaveforms( child: AudioFileWaveforms(
size: Size(350.w, 120.w), size: Size(350.w, 120.w),
playerController: controller.playerController, playerController: controller.playerController,
waveformData: List.generate(88, (int index) {
return 0.01 + Random().nextDouble() * 0.09;
}),
waveformType: WaveformType.fitWidth, waveformType: WaveformType.fitWidth,
playerWaveStyle: PlayerWaveStyle( playerWaveStyle: PlayerWaveStyle(
spacing: 4.w, spacing: 4.w,

2
lib/modules/meeting/views/meeting_record_view.dart

@ -348,7 +348,7 @@ class MeetingRecordView extends GetView<MeetingRecordController> {
Expanded( Expanded(
child: TextButton( child: TextButton(
onPressed: () async { onPressed: () async {
controller.saveTitleChange(); await controller.saveTitleChange();
await controller.saveRecording(); await controller.saveRecording();
Get.back(); Get.back();
}, },

106
lib/modules/profile/controllers/profile_controller.dart

@ -7,14 +7,15 @@ import 'package:logger/logger.dart';
import '../../../data/models/user_Info.dart'; import '../../../data/models/user_Info.dart';
import '../../settings/controllers/settings_controller.dart'; import '../../settings/controllers/settings_controller.dart';
import 'dart:io'; import 'dart:io';
import 'dart:convert'; //import 'dart:convert';
import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import '../../../data/services/network/api.dart'; import '../../../data/services/network/api.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import '../../../routes/app_routes.dart'; import '../../../routes/app_routes.dart';
import '../../../modules/login/controllers/googleLogin.dart'; //import '../../../modules/login/controllers/googleLogin.dart';
import '../../../modules/login/controllers/facebookLogin.dart'; //import '../../../modules/login/controllers/facebookLogin.dart';
import '../../../data/services/account_service.dart';
//import 'package:http_parser/http_parser.dart'; // 新增此导入 //import 'package:http_parser/http_parser.dart'; // 新增此导入
class ProfileController extends GetxController { class ProfileController extends GetxController {
@ -31,9 +32,14 @@ class ProfileController extends GetxController {
// 使用响应式变量 // 使用响应式变量
final currentLanguage = 'zh'.obs; final currentLanguage = 'zh'.obs;
// 账号服务
late final AccountService _accountService;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// 获取或注册账号服务
_accountService = Get.find<AccountService>();
loadUserInfo(); loadUserInfo();
loadLanguagePreference(); loadLanguagePreference();
} }
@ -358,49 +364,41 @@ class ProfileController extends GetxController {
} }
} }
// 退出登录
Future<void> signOut() async { Future<void> signOut() async {
try { try {
//显示加载提示 // 显示加载提示
// Get.dialog( Get.dialog(
// const Center(child: CircularProgressIndicator()), const Center(child: CircularProgressIndicator()),
// barrierDismissible: false, barrierDismissible: false,
// ); );
// 清理所有存储 // 调用账号服务执行登出
await _storage.erase(); final result = await _accountService.signOut();
// 或者逐个清理关键存储项
await _storage.write("logintoken", null);
await _storage.write("user_info", null);
await _storage.write("local_avatar", null);
print('清理存储成功');
// 第三方登录退出
try {
final google = Google();
final facebook = Facebook();
await Future.wait([
google.signOutGoogle(),
facebook.signOutFacebook(),
]);
} catch (e) {
print('第三方登录退出错误: $e');
// 继续执行,不中断退出流程
}
// 关闭加载提示 // 关闭加载提示
//Get.back(); Get.back();
// 确保导航到登录页面 if (result.success) {
if (Get.currentRoute != Routes.login) { // 先显示成功提示
await Get.offAllNamed(Routes.login); Get.snackbar('成功', result.message ?? '已退出登录');
} Logger().d('退出登录成功');
Get.snackbar('成功', '已退出登录'); // 然后导航到登录页面
print('退出登录成功'); if (Get.currentRoute != Routes.login) {
await Get.offAllNamed(Routes.login);
}
} else {
Get.snackbar('错误', result.errorMessage ?? '退出登录失败,请重试');
Logger().e('退出登录失败: ${result.errorMessage}');
}
} catch (e) { } catch (e) {
//Get.back(); // 关闭加载提示 // 确保加载提示被关闭
if (Get.isDialogOpen ?? false) {
Get.back();
}
Get.snackbar('错误', '退出登录失败,请重试'); Get.snackbar('错误', '退出登录失败,请重试');
print('退出登录错误: $e'); Logger().e('退出登录错误: $e');
} }
} }
@ -413,24 +411,32 @@ class ProfileController extends GetxController {
barrierDismissible: false, barrierDismissible: false,
); );
// 删除后台用户数据 // 调用账号服务执行账号删除
final responce = await Api.deleteUser(); final result = await _accountService.deleteAccount();
if (responce == null) {
Get.back(); // 关闭加载提示
Get.snackbar('错误', '删除用户数据失败');
return;
}
// 关闭加载提示 // 关闭加载提示
Get.back(); Get.back();
signOut(); // 调用 signOut 方法 if (result.success) {
// 先显示成功提示
Get.snackbar('成功', result.message ?? '账号已被永久删除');
Logger().d('账号删除成功');
Get.snackbar('Success', 'Account has been permanently deleted'); // 然后导航到登录页面
Logger().d('账号删除成功'); if (Get.currentRoute != Routes.login) {
await Get.offAllNamed(Routes.login);
}
} else {
Get.snackbar('错误', result.errorMessage ?? '删除账号失败,请重试');
Logger().e('删除账号失败: ${result.errorMessage}');
}
} catch (e) { } catch (e) {
Get.back(); // 关闭加载提示 // 确保加载提示被关闭
if (Get.isDialogOpen ?? false) {
Get.back();
}
Get.snackbar('错误', '删除账号失败,请重试'); Get.snackbar('错误', '删除账号失败,请重试');
Logger().d('账号删除失败: $e'); Logger().e('删除账号失败: $e');
} }
} }
} }

133
lib/modules/translation/controllers/translation_list_controller.dart

@ -1,18 +1,11 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import '../../../core/utils/logger.dart'; import '../../../core/utils/logger.dart';
import '../../../core/utils/upload_oss.dart';
import '../../../data/services/db/sqflite_api.dart';
import '../../../core/utils/permission_util.dart'; import '../../../core/utils/permission_util.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
@ -28,14 +21,21 @@ class TranslationListController extends GetxController {
final appDocDir = getApplicationDocumentsDirectory(); final appDocDir = getApplicationDocumentsDirectory();
// final directory = Directory( // final directory = Directory(
// '/storage/emulated/0/Android/data/com.yunqiinnovation.deepsound/files'); // '/storage/emulated/0/Android/data/com.yunqiinnovation.deepsound/files');
List<FileSystemEntity> files = []; final RxList<FileSystemEntity> files = <FileSystemEntity>[].obs;
TextEditingController titleController = TextEditingController(); // 录音标题
RxList dataList = [].obs; RxList dataList = [].obs;
List dataListTask = []; List dataListTask = [];
bool isDisposed = false; bool isDisposed = false;
// 添加音频播放器
// 添加音频时长缓存
final Map<String, Duration> audioDurations = {};
// 播放状态
final RxBool isPlaying = false.obs;
// 当前播放的文件路径
final RxString currentPlayingPath = ''.obs;
final AudioPlayer audioPlayer = AudioPlayer();
final PlayerController playerController = PlayerController();
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@ -49,16 +49,47 @@ class TranslationListController extends GetxController {
// _getDataList(); // _getDataList();
// addMeetingAudio(); // addMeetingAudio();
loadTranslatAudio(); loadTranslatAudio();
// 监听播放状态变化
audioPlayer.playerStateStream.listen((playerState) {
isPlaying.value = playerState.playing;
if (playerState.processingState == ProcessingState.completed) {
// 播放完成后重置状态
currentPlayingPath.value = '';
}
});
} }
@override @override
void onClose() { void onClose() {
titleController.dispose(); audioPlayer.dispose(); // 释放播放器资源
isDisposed = true;
super.onClose(); super.onClose();
} }
// 播放/暂停音频
void togglePlay(FileSystemEntity file) async {
final filePath = file.path;
if (currentPlayingPath.value == filePath) {
// 同一个文件:切换播放/暂停
if (isPlaying.value) {
await audioPlayer.pause();
} else {
await audioPlayer.play();
}
} else {
// 新文件:停止当前播放,开始新文件
currentPlayingPath.value = filePath;
try {
await audioPlayer.stop();
await audioPlayer.setFilePath(filePath);
await audioPlayer.play();
} catch (e) {
print("播放失败: $e");
currentPlayingPath.value = '';
}
}
}
// 请求存储权限 // 请求存储权限
void _requestStoragePermission() async { void _requestStoragePermission() async {
// final BuildContext context = Get.overlayContext!; // final BuildContext context = Get.overlayContext!;
@ -81,16 +112,22 @@ class TranslationListController extends GetxController {
}); });
} }
void loadTranslatopus() async { // 添加方法:获取音频时长
final appDir = await appDocDir; // 先 await 获取目录 Future<Duration> getAudioDuration(String filePath) async {
final dir = Directory("${appDir.path}/OpusAudio"); if (audioDurations.containsKey(filePath)) {
return audioDurations[filePath]!;
print("appDocDir=${dir?.path}"); }
files = dir try {
.listSync() final audioPlayer = AudioPlayer();
.where((f) => f is File && (f.path.endsWith('.opus'))) await audioPlayer.setFilePath(filePath);
.toList(); final duration = await audioPlayer.duration ?? Duration.zero;
audioDurations[filePath] = duration;
return duration;
} catch (e) {
print("获取音频时长失败: $e");
return Duration.zero;
}
} }
void loadTranslatAudio() async { void loadTranslatAudio() async {
@ -102,12 +139,12 @@ class TranslationListController extends GetxController {
// Create directory if it doesn't exist // Create directory if it doesn't exist
if (!await dir.exists()) { if (!await dir.exists()) {
await dir.create(recursive: true); // Create directory tree if needed await dir.create(recursive: true); // Create directory tree if needed
files = []; // No files exist in new directory files.value = []; // No files exist in new directory
return; return;
} }
// Directory exis // Directory exis
files = dir files.value = dir
.listSync() .listSync()
.where((f) => .where((f) =>
f is File && f is File &&
@ -120,7 +157,7 @@ class TranslationListController extends GetxController {
.toList(); .toList();
} catch (e) { } catch (e) {
print("Error loading files: $e"); print("Error loading files: $e");
files = []; // Return empty list on error files.value = []; // Return empty list on error
} }
} }
// 导入音频文件 // 导入音频文件
@ -144,48 +181,4 @@ class TranslationListController extends GetxController {
EasyLoading.showToast('分享失败: ${e.toString()}'); EasyLoading.showToast('分享失败: ${e.toString()}');
} }
} }
Future<String> _saveFile(PlatformFile file) async {
final directory = await getApplicationDocumentsDirectory();
final directoryDir = Directory('${directory.path}/ExternalAudio');
if (!await directoryDir.exists()) {
await directoryDir.create(recursive: true);
}
final String filePath = '${directory.path}/ExternalAudio/${file.name}';
final File localFile = File(filePath);
await localFile.writeAsBytes(file.bytes!);
return filePath;
}
// 修改标题
void editMeetingTitle(int id, String title) async {
if (title.isNotEmpty) {
SqfliteApi.editMeetingTitle(id, {'title': title});
int index = dataList.indexWhere((item) => item['id'] == id);
if (index != -1) {
final dataMap = Map<String, dynamic>.from(dataList[index]);
dataMap['title'] = title;
dataList[index] = dataMap;
}
}
Get.back();
}
// 删除数据
void deleteMeeting(int id, String filepath) async {
dataList.removeWhere((item) => item['id'] == id);
// 删除数据库中的记录
SqfliteApi.deleteMeeting(id);
_deleteFile(filepath);
addQuantity--;
}
// 删除文件
Future<void> _deleteFile(String filepath) async {
File file = File(filepath);
bool fileExists = await file.exists();
if (fileExists) {
await file.delete();
}
}
} }

274
lib/modules/translation/views/translation_list_view.dart

@ -1,12 +1,10 @@
import 'dart:io'; import 'dart:io';
import 'package:common_utils/common_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:haptic_feedback/haptic_feedback.dart'; import 'package:haptic_feedback/haptic_feedback.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_colors.dart';
import '../../../routes/app_routes.dart'; import '../../../routes/app_routes.dart';
import '../controllers/translation_list_controller.dart'; import '../controllers/translation_list_controller.dart';
@ -39,7 +37,7 @@ class TranslationListView extends GetView<TranslationListController> {
onPressed: () => Get.back(), onPressed: () => Get.back(),
), ),
title: Text( title: Text(
'conferenceAssistant'.tr, // 商务会议助手 "翻译录音文件", //
style: TextStyle( style: TextStyle(
fontSize: 17.sp, fontSize: 17.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -52,7 +50,8 @@ class TranslationListView extends GetView<TranslationListController> {
Widget _buildAudioList(BuildContext context, bool isDarkMode) { Widget _buildAudioList(BuildContext context, bool isDarkMode) {
return Obx(() { return Obx(() {
if (controller.files.isEmpty) { final fileList = controller.files; // 不要 toList()
if (fileList.isEmpty) {
return Center(child: Text('暂无音频文件')); return Center(child: Text('暂无音频文件'));
} }
@ -72,115 +71,168 @@ class TranslationListView extends GetView<TranslationListController> {
final fileName = file.path.split('/').last; final fileName = file.path.split('/').last;
final fileStat = file.statSync(); // 获取文件信息 final fileStat = file.statSync(); // 获取文件信息
final modifiedTime = fileStat.modified; final modifiedTime = fileStat.modified;
// 添加 FutureBuilder 来异步获取音频时长
return GestureDetector( return FutureBuilder<Duration>(
onTap: () { future: controller.getAudioDuration(file.path),
// TODO: 播放音频 builder: (context, snapshot) {
print("播放音频: $fileName"); String durationText = "计算中...";
}, if (snapshot.hasData) {
onLongPressStart: (details) { final duration = snapshot.data!;
_onLongPressAudioItem(context, isDarkMode, details, file); if (duration.inSeconds > 0) {
print("长按操作: $fileName"); durationText =
}, "${duration.inMinutes}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}";
child: Container( } else {
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 10.w), durationText = "未知";
margin: EdgeInsets.symmetric(vertical: 5.w), }
decoration: BoxDecoration( } else if (snapshot.hasError) {
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, durationText = "错误";
borderRadius: BorderRadius.circular(12.w), }
boxShadow: [ return GestureDetector(
BoxShadow( onLongPressStart: (details) {
color: isDarkMode _onLongPressAudioItem(context, isDarkMode, details, file);
? Colors.black.withOpacity(0.2) print("长按操作: $fileName");
: Colors.black.withOpacity(0.05), },
blurRadius: 10, onTap: () {
offset: const Offset(0, 2), // 添加点击播放功能
), controller.togglePlay(file);
], },
), child: Container(
child: Column( padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 10.w),
crossAxisAlignment: CrossAxisAlignment.start, margin: EdgeInsets.symmetric(vertical: 5.w),
children: [ decoration: BoxDecoration(
/// 文件名 color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white,
Text( borderRadius: BorderRadius.circular(12.w),
fileName, boxShadow: [
style: TextStyle( BoxShadow(
color: isDarkMode ? Colors.white : Colors.black87, color: isDarkMode
fontSize: 18.sp, ? Colors.black.withOpacity(0.2)
), : Colors.black.withOpacity(0.05),
overflow: TextOverflow.ellipsis, blurRadius: 10,
), offset: const Offset(0, 2),
),
/// 修改时间 & 大小 ],
Padding(
padding: EdgeInsets.symmetric(vertical: 5.w),
child: Row(
children: [
Icon(
Icons.access_time,
size: 14.w,
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
),
4.horizontalSpace,
Text(
DateFormat('yyyy-MM-dd HH:mm').format(modifiedTime),
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 12.sp,
),
),
20.horizontalSpace,
Icon(
Icons.sd_storage,
size: 14.w,
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
),
4.horizontalSpace,
Text(
'${(fileStat.size / 1024).toStringAsFixed(1)} KB',
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 12.sp,
),
),
],
),
), ),
child: Column(
/// 文件类型标签 crossAxisAlignment: CrossAxisAlignment.start,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Container( /// 文件名
padding: EdgeInsets.symmetric(horizontal: 5.w, vertical: 2.w), Text(
margin: EdgeInsets.only(top: 5.w), fileName,
decoration: BoxDecoration( style: TextStyle(
color: isDarkMode ? Colors.grey[900] : Colors.grey[100], color: isDarkMode ? Colors.white : Colors.black87,
borderRadius: BorderRadius.circular(4.w), fontSize: 18.sp,
),
child: Text(
file.path.split('.').last.toUpperCase(),
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
fontSize: 10.sp,
),
), ),
overflow: TextOverflow.ellipsis,
), ),
/// 修改时间 & 大小
Padding( Padding(
padding: EdgeInsets.only(top: 5.w), padding: EdgeInsets.symmetric(vertical: 5.w),
child: Text( child: Row(
'点击播放', children: [
style: TextStyle( Icon(
color: Colors.blue, Icons.access_time,
fontSize: 10.sp, size: 14.w,
), color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
),
4.horizontalSpace,
Text(
DateFormat('yyyy-MM-dd HH:mm').format(modifiedTime),
style: TextStyle(
color:
isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 12.sp,
),
),
20.horizontalSpace,
Icon(
Icons.sd_storage,
size: 14.w,
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
),
4.horizontalSpace,
Text(
'${(fileStat.size / 1024).toStringAsFixed(1)} KB',
style: TextStyle(
color:
isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 12.sp,
),
),
20.horizontalSpace,
Icon(Icons.music_note,
size: 14.w,
color:
isDarkMode ? Colors.grey[400] : Colors.grey[500]),
4.horizontalSpace,
Text(
durationText, // 使用计算出的时长文本
style: TextStyle(
color:
isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 12.sp,
),
),
],
), ),
), ),
/// 文件类型标签
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding:
EdgeInsets.symmetric(horizontal: 5.w, vertical: 2.w),
margin: EdgeInsets.only(top: 5.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.grey[100],
borderRadius: BorderRadius.circular(4.w),
),
child: Text(
file.path.split('.').last.toUpperCase(),
style: TextStyle(
color:
isDarkMode ? Colors.grey[400] : Colors.grey[600],
fontSize: 10.sp,
),
),
),
Obx(() {
final isCurrentFile =
controller.currentPlayingPath.value == file.path;
final isPlaying = controller.isPlaying.value;
return Row(
children: [
// 播放/暂停图标
if (isCurrentFile)
Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
size: 20.w,
color: Colors.blue,
),
// 播放状态文本
Text(
isCurrentFile
? (isPlaying ? '播放中...' : '已暂停')
: '点击播放',
style: TextStyle(
color: Colors.blue,
fontSize: 10.sp,
),
),
],
);
}),
],
),
], ],
), ),
], ),
), );
), },
); );
} }
@ -237,6 +289,20 @@ class TranslationListView extends GetView<TranslationListController> {
controller.loadTranslatAudio(); // 刷新列表 controller.loadTranslatAudio(); // 刷新列表
}, },
), ),
_popupMenuEntry(
Icon(
Icons.delete_outline,
size: 20.w,
color: isDarkMode ? Colors.red[700] : Colors.red,
),
'导入',
isDarkMode ? Colors.red[700]! : Colors.red,
() {
Get.toNamed(Routes.meeting, arguments: {
'filePath': file.path,
});
},
),
], ],
); );
} }

Loading…
Cancel
Save