diff --git a/lib/modules/translation/controllers/translation_controller.dart b/lib/modules/translation/controllers/translation_controller.dart index a964585fc..cff3a3247 100644 --- a/lib/modules/translation/controllers/translation_controller.dart +++ b/lib/modules/translation/controllers/translation_controller.dart @@ -944,6 +944,84 @@ class TranslationController extends GetxController { _storage.remove(_historyKey); } + // 删除指定会话的所有翻译记录 + void deleteSessionHistory(String sessionId) { + try { + // 1. 从内存中删除该会话的所有记录 + final originalCount = translationHistory.length; + translationHistory.removeWhere((item) => item.sessionId == sessionId); + final removedFromMemory = originalCount - translationHistory.length; + + // 2. 从本地存储中删除该会话的记录 + final historyJson = _storage.read(_historyKey); + int removedFromStorage = 0; + + if (historyJson != null) { + final List historyList = jsonDecode(historyJson); + final List storedHistory = + historyList.map((item) => TranslationItem.fromJson(item)).toList(); + + final originalStoredCount = storedHistory.length; + storedHistory.removeWhere((item) => item.sessionId == sessionId); + removedFromStorage = originalStoredCount - storedHistory.length; + + // 重新保存更新后的存储数据 + final List> updatedHistoryJson = + storedHistory.map((item) => item.toJson()).toList(); + _storage.write(_historyKey, jsonEncode(updatedHistoryJson)); + } + + // 3. 刷新UI显示 + translationHistory.refresh(); + + Logger.info('删除会话历史成功: sessionId=$sessionId, 内存删除${removedFromMemory}条, 存储删除${removedFromStorage}条'); + } catch (e) { + Logger.error('删除会话历史失败: ${e.toString()}'); + rethrow; // 重新抛出异常,让调用方处理 + } + } + + // 删除指定的单条翻译记录 + void deleteTranslationItem(String sessionId, DateTime timestamp) { + try { + // 1. 从内存中删除指定的翻译记录 + final originalCount = translationHistory.length; + translationHistory.removeWhere((item) => + item.sessionId == sessionId && + item.timestamp.millisecondsSinceEpoch == timestamp.millisecondsSinceEpoch); + final removedFromMemory = originalCount - translationHistory.length; + + // 2. 从本地存储中删除指定的翻译记录 + final historyJson = _storage.read(_historyKey); + int removedFromStorage = 0; + + if (historyJson != null) { + final List historyList = jsonDecode(historyJson); + final List storedHistory = + historyList.map((item) => TranslationItem.fromJson(item)).toList(); + + final originalStoredCount = storedHistory.length; + storedHistory.removeWhere((item) => + item.sessionId == sessionId && + item.timestamp.millisecondsSinceEpoch == timestamp.millisecondsSinceEpoch); + removedFromStorage = originalStoredCount - storedHistory.length; + + // 重新保存更新后的存储数据 + final List> updatedHistoryJson = + storedHistory.map((item) => item.toJson()).toList(); + _storage.write(_historyKey, jsonEncode(updatedHistoryJson)); + } + + // 3. 刷新UI显示 + translationHistory.refresh(); + + Logger.info('删除单条翻译记录成功: sessionId=$sessionId, timestamp=${timestamp.toIso8601String()}, 内存删除${removedFromMemory}条, 存储删除${removedFromStorage}条'); + } catch (e) { + Logger.error('删除单条翻译记录失败: ${e.toString()}'); + rethrow; // 重新抛出异常,让调用方处理 + } + } + // 滚动到底部 (公共方法,用于向后兼容) void scrollToBottom({bool animate = true}) { _scrollToBottom(); diff --git a/lib/modules/translation/controllers/translation_detail_controller.dart b/lib/modules/translation/controllers/translation_detail_controller.dart index f2f469041..a33bf50ac 100644 --- a/lib/modules/translation/controllers/translation_detail_controller.dart +++ b/lib/modules/translation/controllers/translation_detail_controller.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../data/services/volcano_ai_service.dart'; import '../controllers/translation_controller.dart'; @@ -182,10 +183,63 @@ class TranslationDetailController extends GetxController { } } + // 删除单条翻译记录 + Future deleteTranslationItem(TranslationItem item) async { + try { + // 调用主控制器的删除方法 + historyController.translationController.deleteTranslationItem( + item.sessionId, + item.timestamp + ); + + // 从当前会话项目列表中移除该项目 + sessionItems.removeWhere((sessionItem) => + sessionItem.sessionId == item.sessionId && + sessionItem.timestamp.millisecondsSinceEpoch == item.timestamp.millisecondsSinceEpoch); + + // 重新加载历史控制器的数据 + await historyController.loadHistoryData(); + + // 检查是否删除了会话中的最后一条记录 + if (sessionItems.isEmpty) { + // 如果会话为空,返回上级页面 + Get.back(); + Get.snackbar( + '删除成功', + '已删除该翻译记录,会话已清空', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Get.isDarkMode ? Colors.grey[800] : Colors.grey[200], + colorText: Get.isDarkMode ? Colors.white : Colors.black, + duration: const Duration(seconds: 2), + ); + } else { + // 显示删除成功提示 + Get.snackbar( + '删除成功', + '已删除该翻译记录', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Get.isDarkMode ? Colors.grey[800] : Colors.grey[200], + colorText: Get.isDarkMode ? Colors.white : Colors.black, + duration: const Duration(seconds: 2), + ); + } + } catch (e) { + // 显示删除失败提示 + Get.snackbar( + '删除失败', + '删除翻译记录时发生错误: ${e.toString()}', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red[400], + colorText: Colors.white, + duration: const Duration(seconds: 3), + ); + } + } + @override void onClose() { // 确保资源被正确释放 historyController.translationController.stopAll(); super.onClose(); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/lib/modules/translation/controllers/translation_history_controller.dart b/lib/modules/translation/controllers/translation_history_controller.dart index 04fb34c50..3c2184c43 100644 --- a/lib/modules/translation/controllers/translation_history_controller.dart +++ b/lib/modules/translation/controllers/translation_history_controller.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import '../controllers/translation_controller.dart'; @@ -139,4 +140,35 @@ class TranslationHistoryController extends GetxController { // 检查是否有历史记录 bool get hasHistory => translationController.translationHistory.isNotEmpty; -} \ No newline at end of file + + // 删除指定会话的历史记录 + Future deleteSessionHistory(String sessionId) async { + try { + // 调用主控制器的删除方法 + translationController.deleteSessionHistory(sessionId); + + // 重新加载和分组历史数据 + await loadHistoryData(); + + // 显示删除成功提示 + Get.snackbar( + '删除成功', + '已删除该会话的所有翻译记录', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Get.isDarkMode ? Colors.grey[800] : Colors.grey[200], + colorText: Get.isDarkMode ? Colors.white : Colors.black, + duration: const Duration(seconds: 2), + ); + } catch (e) { + // 显示删除失败提示 + Get.snackbar( + '删除失败', + '删除会话记录时发生错误: ${e.toString()}', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red[400], + colorText: Colors.white, + duration: const Duration(seconds: 3), + ); + } + } +} \ No newline at end of file diff --git a/lib/modules/translation/views/translation_detail_view.dart b/lib/modules/translation/views/translation_detail_view.dart index c31db5552..52ad53397 100644 --- a/lib/modules/translation/views/translation_detail_view.dart +++ b/lib/modules/translation/views/translation_detail_view.dart @@ -258,7 +258,7 @@ class TranslationDetailView extends GetView { ), ), ], - // 语言信息和播放按钮 + // 语言信息和操作按钮 const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -270,15 +270,39 @@ class TranslationDetailView extends GetView { color: Theme.of(context).textTheme.bodySmall?.color, ), ), - if (!item.isIntermediate) - GestureDetector( - onTap: () => controller.playTranslationItem(item), - child: Icon( - Icons.volume_up, - size: 20, - color: Theme.of(context).colorScheme.primary, - ), - ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + // 删除按钮 + if (!item.isIntermediate) + InkWell( + onTap: () => _showDeleteItemConfirmDialog(item), + borderRadius: BorderRadius.circular(6.r), + splashColor: Colors.red.withValues(alpha: 0.1), + highlightColor: Colors.red.withValues(alpha: 0.05), + child: Container( + padding: EdgeInsets.all(6.w), + child: Icon( + Icons.delete_outline, + size: 16.sp, + color: isDarkMode ? Colors.grey[500] : Colors.grey[400], + ), + ), + ), + // 播放按钮 + if (!item.isIntermediate) ...[ + SizedBox(width: 8.w), + GestureDetector( + onTap: () => controller.playTranslationItem(item), + child: Icon( + Icons.volume_up, + size: 20.sp, + color: Theme.of(context).colorScheme.primary, + ), + ), + ], + ], + ), ], ), ], @@ -592,6 +616,102 @@ class TranslationDetailView extends GetView { ); } + // 显示删除单条记录确认对话框 + void _showDeleteItemConfirmDialog(TranslationItem item) { + final isDarkMode = Get.isDarkMode; + + Get.dialog( + AlertDialog( + backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, + title: Text( + '删除翻译记录', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.bold, + color: isDarkMode ? Colors.white : Colors.black, + ), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '确定要删除这条翻译记录吗?此操作无法撤销。', + style: TextStyle( + fontSize: 16.sp, + color: isDarkMode ? Colors.grey[300] : Colors.black87, + ), + ), + SizedBox(height: 12.h), + Container( + padding: EdgeInsets.all(12.w), + decoration: BoxDecoration( + color: isDarkMode ? Colors.grey[800] : Colors.grey[100], + borderRadius: BorderRadius.circular(8.r), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.sourceText, + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.grey[300] : Colors.black87, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + if (item.translatedText.isNotEmpty) ...[ + SizedBox(height: 4.h), + Text( + item.translatedText, + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.grey[400] : Colors.black54, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ], + ], + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: Text( + 'cancel'.tr, + style: TextStyle( + fontSize: 16.sp, + color: isDarkMode ? Colors.grey[400] : Colors.grey[700], + ), + ), + ), + TextButton( + onPressed: () { + Get.back(); + // 删除指定的翻译记录 + controller.deleteTranslationItem(item); + }, + child: Text( + 'confirm'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.r), + ), + ), + ); + } + // 分享内容方法 void _shareContent() { String shareText = ''; diff --git a/lib/modules/translation/views/translation_history_view.dart b/lib/modules/translation/views/translation_history_view.dart index 4aeb6ef38..44733c576 100644 --- a/lib/modules/translation/views/translation_history_view.dart +++ b/lib/modules/translation/views/translation_history_view.dart @@ -176,6 +176,22 @@ class TranslationHistoryView extends GetView { ), ), SizedBox(width: 12.w), + // 删除会话按钮 + InkWell( + onTap: () => _showDeleteSessionConfirmDialog(item.sessionId), + borderRadius: BorderRadius.circular(6.r), + splashColor: Colors.red.withOpacity(0.1), + highlightColor: Colors.red.withOpacity(0.05), + child: Container( + padding: EdgeInsets.all(8.w), + child: Icon( + Icons.delete_outline, + color: isDarkMode ? Colors.grey[500] : Colors.grey[400], + size: 18.sp, + ), + ), + ), + SizedBox(width: 8.w), Icon( Icons.chevron_right, color: isDarkMode ? Colors.grey[600] : Colors.grey[350], @@ -229,9 +245,65 @@ class TranslationHistoryView extends GetView { return '$sourceName ⇄ $targetName'; } + // 显示删除会话确认对话框 + void _showDeleteSessionConfirmDialog(String sessionId) { + final isDarkMode = Get.isDarkMode; + + Get.dialog( + AlertDialog( + backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, + title: Text( + '删除会话记录', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.bold, + color: isDarkMode ? Colors.white : Colors.black, + ), + ), + content: Text( + '确定要删除该会话的所有翻译记录吗?此操作无法撤销。', + style: TextStyle( + fontSize: 16.sp, + color: isDarkMode ? Colors.grey[300] : Colors.black87, + ), + ), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: Text( + 'cancel'.tr, + style: TextStyle( + fontSize: 16.sp, + color: isDarkMode ? Colors.grey[400] : Colors.grey[700], + ), + ), + ), + TextButton( + onPressed: () { + Get.back(); + // 删除指定会话的历史记录 + controller.deleteSessionHistory(sessionId); + }, + child: Text( + 'confirm'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.r), + ), + ), + ); + } + void _showClearHistoryConfirmDialog() { final isDarkMode = Get.isDarkMode; - + Get.dialog( AlertDialog( backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white,