import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../controllers/translation_controller.dart'; import '../controllers/translation_audiohistory_controller.dart'; import '../controllers/translation_detail_controller.dart'; import 'package:intl/intl.dart'; import 'package:flutter/services.dart'; import 'package:share_plus/share_plus.dart'; import '../../../core/theme/app_colors.dart'; import '../models/translation_models.dart'; class TranslationDetailView extends GetView { TranslationDetailView({Key? key}) : super(key: key); // 缓存文本,避免重复计算 final _sourceTextCache = Rx(null); final _translatedTextCache = Rx(null); // 获取源文本(带缓存) String _getCombinedSourceText() { if (_sourceTextCache.value == null) { _sourceTextCache.value = controller.getCombinedSourceText(); } return _sourceTextCache.value!; } // 获取翻译文本(带缓存) String _getCombinedTranslatedText() { if (_translatedTextCache.value == null) { _translatedTextCache.value = controller.getCombinedTranslatedText(); } return _translatedTextCache.value!; } @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; return Scaffold( backgroundColor: isDarkMode ? AppColors.darkBackground : Color(0xFFF5F5F8), appBar: _buildAppBar(isDarkMode), body: Obx(() { // 显示加载状态 if (controller.isLoading.value) { return Center( child: CircularProgressIndicator( color: isDarkMode ? AppColors.primary : Colors.blue, ), ); } return Column( children: [ _buildTabButtons(isDarkMode), _buildContentArea(isDarkMode), ], ); }), ); } // 构建应用栏 PreferredSizeWidget _buildAppBar(bool isDarkMode) { return AppBar( backgroundColor: isDarkMode ? AppColors.cardBackground : Color(0xFFF5F5F8), elevation: 0, leading: IconButton( icon: Icon( Icons.arrow_back_ios, color: isDarkMode ? Colors.white : Colors.black, size: 20.sp, ), onPressed: () => Get.back(), ), // title: Text( // 'Detail', // style: TextStyle( // color: isDarkMode ? Colors.white : Colors.black, // fontSize: 24.sp, // fontWeight: FontWeight.bold, // ), // ), centerTitle: true, actions: [ IconButton( icon: Icon( Icons.share, color: isDarkMode ? Colors.white : Colors.black, size: 22.sp, ), onPressed: _shareContent, ), ], ); } // 构建选项卡按钮 Widget _buildTabButtons(bool isDarkMode) { return Padding( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h), child: Row( children: [ Expanded( child: Obx(() => _buildTabButton( title: 'originalRecord'.tr, // 原始记录 isSelected: controller.selectedTabIndex.value == 0, onTap: () => controller.switchTab(0), isDarkMode: isDarkMode, )), ), //屏蔽“记录摘要”,需要时开启 // SizedBox(width: 12.w), // Expanded( // child: Obx(() => _buildTabButton( // title: 'recordSummary'.tr, // 记录摘要 // isSelected: controller.selectedTabIndex.value == 1, // onTap: () => controller.switchTab(1), // isDarkMode: isDarkMode, // )), // ), ], ), ); } // 构建内容区域 Widget _buildContentArea(bool isDarkMode) { return Expanded( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 16.w), child: Obx(() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildTimestamp(isDarkMode), SizedBox(height: 8.h), // 根据选中的选项卡显示不同内容 if (controller.selectedTabIndex.value == 0) ..._buildOriginalRecords(isDarkMode) else _buildSummaryWidget(isDarkMode), SizedBox(height: 24.h), ], ); }), ), ); } // 构建时间戳 Widget _buildTimestamp(bool isDarkMode) { return Padding( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 8.h), child: Text( DateFormat('yyyy-MM-dd HH:mm').format(controller.currentItem.timestamp), style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.grey[400] : Colors.grey[600], letterSpacing: 1.0, ), ), ); } // 构建原始记录列表 List _buildOriginalRecords(bool isDarkMode) { return controller.sessionItems .map((sessionItem) => _buildSessionItemWidget(sessionItem, isDarkMode)) .toList(); } // 构建选项卡按钮 Widget _buildTabButton({ required String title, required bool isSelected, required VoidCallback onTap, required bool isDarkMode, }) { return GestureDetector( onTap: onTap, child: Container( height: 40.h, decoration: BoxDecoration( color: isSelected ? (isDarkMode ? AppColors.primary : Colors.blue) : (isDarkMode ? AppColors.cardBackground : Colors.white), borderRadius: BorderRadius.circular(20.r), boxShadow: [ BoxShadow( color: Colors.black.withAlpha(isDarkMode ? 30 : 8), blurRadius: 4, offset: Offset(0, 1), ), ], ), alignment: Alignment.center, child: Text( title, style: TextStyle( fontSize: 15.sp, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, color: isSelected ? Colors.white : (isDarkMode ? Colors.white : Colors.black87), ), ), ), ); } // 构建会话详情中的翻译项 Widget _buildSessionItemWidget(TranslationItem item, bool isDarkMode) { final context = Get.context!; return Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: item.isIntermediate ? Colors.grey.withOpacity(0.1) // 中间结果使用浅灰色底色 : (item.sourceLanguageCode == controller.currentItem.sourceLanguageCode ? Theme.of(context) .colorScheme .primary .withOpacity(0.08) // 源语言匹配时使用主题色底色 : Theme.of(context) .colorScheme .secondary .withOpacity(0.08)), // 否则使用次要色底色 borderRadius: BorderRadius.circular(12), border: Border.all( color: item.isIntermediate ? Colors.grey.withOpacity(0.2) : Colors.transparent, width: 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 源文本 Expanded( child: Text( item.sourceText, style: TextStyle( fontSize: 16, color: Theme.of(context).textTheme.bodyLarge?.color, ), )), // 删除按钮 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.translatedText.isNotEmpty) ...[ const SizedBox(height: 8), // 翻译文本 Text( item.translatedText, style: TextStyle( fontSize: 16, color: Theme.of(context).textTheme.bodyMedium?.color, fontWeight: FontWeight.w500, ), ), ], // 语言信息和操作按钮 const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '${item.sourceLanguageName.tr} → ${item.targetLanguageName.tr}', style: TextStyle( fontSize: 12, color: Theme.of(context).textTheme.bodySmall?.color, ), ), Row( mainAxisSize: MainAxisSize.min, children: [ // 播放按钮 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, ), ), ], ], ), ], ), ], ), ); } // 构建总结视图 Widget _buildSummaryWidget(bool isDarkMode) { final String combinedSourceText = _getCombinedSourceText(); return Column( children: [ // 摘要内容容器 _buildSummaryContainer(isDarkMode), // 提取摘要按钮 Obx(() => _buildExtractSummaryButton(combinedSourceText, isDarkMode)), // 重新生成按钮 Obx(() => _buildRegenerateSummaryButton(combinedSourceText, isDarkMode)), ], ); } // 构建摘要容器 Widget _buildSummaryContainer(bool isDarkMode) { return Container( width: double.infinity, margin: EdgeInsets.only(bottom: 16.h), decoration: BoxDecoration( color: isDarkMode ? AppColors.cardBackground : Colors.white, borderRadius: BorderRadius.circular(16.r), boxShadow: [ BoxShadow( color: Colors.black.withAlpha(isDarkMode ? 30 : 8), blurRadius: 8, offset: Offset(0, 2), ), ], ), child: Obx(() { if (controller.summaryResult.value.isEmpty) { return _buildEmptySummaryPrompt(isDarkMode); } else { return _buildSummaryContent(isDarkMode); } }), ); } // 构建空摘要提示 Widget _buildEmptySummaryPrompt(bool isDarkMode) { return Padding( padding: EdgeInsets.all(24.r), child: Center( child: Text( 'clickToGenerateSummary'.tr, // 点击下方按钮生成会话摘要 style: TextStyle( fontSize: 15.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[600], fontStyle: FontStyle.italic, ), textAlign: TextAlign.center, ), ), ); } // 构建摘要内容 Widget _buildSummaryContent(bool isDarkMode) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.all(20.r), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'summaryContent'.tr, // 摘要内容 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.black, ), ), SizedBox(height: 12.h), Text( controller.summaryResult.value, style: TextStyle( fontSize: 15.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[800], height: 1.5, ), ), ], ), ), Divider( height: 1, thickness: 0.5, color: isDarkMode ? Colors.grey[700] : Colors.grey[200], ), Padding( padding: EdgeInsets.symmetric(horizontal: 12.r, vertical: 8.r), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ // 播放按钮 Material( color: Colors.transparent, child: InkWell( onTap: () => controller.playText(controller.summaryResult.value), borderRadius: BorderRadius.circular(20.r), splashColor: isDarkMode ? AppColors.primary.withOpacity(0.2) : Colors.blue.withOpacity(0.2), child: Padding( padding: EdgeInsets.all(8.r), child: Icon( Icons.volume_up, color: isDarkMode ? Colors.white : Colors.blue, size: 18.sp, ), ), ), ), SizedBox(width: 12.w), // 复制按钮 Material( color: Colors.transparent, child: InkWell( onTap: _copySummaryToClipboard, borderRadius: BorderRadius.circular(20.r), splashColor: isDarkMode ? AppColors.primary.withOpacity(0.2) : Colors.blue.withOpacity(0.2), child: Padding( padding: EdgeInsets.all(8.r), child: Icon( Icons.copy, color: isDarkMode ? Colors.white : Colors.blue, size: 18.sp, ), ), ), ), ], ), ), ], ); } // 复制摘要到剪贴板 void _copySummaryToClipboard() { Clipboard.setData(ClipboardData(text: controller.summaryResult.value)); Get.snackbar( 'copied'.tr, // 已复制 'summaryCopiledToClipboard'.tr, // 摘要已复制到剪贴板 snackPosition: SnackPosition.BOTTOM, duration: Duration(seconds: 2), ); } // 构建提取摘要按钮 Widget _buildExtractSummaryButton( String combinedSourceText, bool isDarkMode) { final bool isDisabled = controller.isGeneratingSummary.value || controller.summaryResult.value.isNotEmpty; return Container( width: double.infinity, margin: EdgeInsets.only(bottom: 16.h), child: ElevatedButton( onPressed: isDisabled ? null : () => _extractSummary(combinedSourceText), style: ElevatedButton.styleFrom( backgroundColor: isDarkMode ? AppColors.primary : Colors.blue, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 12.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.r), ), elevation: 0, disabledBackgroundColor: controller.isGeneratingSummary.value ? isDarkMode ? AppColors.primary.withAlpha(128) : Colors.blue.withAlpha(128) : Colors.grey.withAlpha(128), ), child: controller.isGeneratingSummary.value ? _buildGeneratingIndicator(isDarkMode) : Text( controller.summaryResult.value.isEmpty ? 'extractSummary'.tr : 'summaryGenerated'.tr, // 提取摘要 : 已生成摘要 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, ), ), ), ); } // 构建生成中指示器 Widget _buildGeneratingIndicator(bool isDarkMode) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: 20.w, height: 20.h, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2.0, ), ), SizedBox(width: 12.w), Text( 'generating'.tr, // 生成中... style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, ), ), ], ); } // 构建重新生成按钮 Widget _buildRegenerateSummaryButton( String combinedSourceText, bool isDarkMode) { if (controller.summaryResult.value.isEmpty || controller.isGeneratingSummary.value) { return SizedBox.shrink(); } return Container( width: double.infinity, margin: EdgeInsets.only(bottom: 16.h), child: OutlinedButton( onPressed: () => _extractSummary(combinedSourceText), style: OutlinedButton.styleFrom( foregroundColor: isDarkMode ? AppColors.primary : Colors.blue, side: BorderSide(color: isDarkMode ? AppColors.primary : Colors.blue), padding: EdgeInsets.symmetric(vertical: 12.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.r), ), ), child: Text( 'regenerate'.tr, // 重新生成 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, ), ), ), ); } // 提取摘要方法 void _extractSummary(String text) async { final result = await controller.extractSummary(text); // 检查是否超时,如果超时则显示重试选项 if (result.contains('超时')) { _showTimeoutDialog(text); } } // 显示超时对话框 void _showTimeoutDialog(String text) { Get.dialog( AlertDialog( title: Text( 'requestTimeout'.tr, // 请求超时 style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, ), ), content: Text( 'summaryTimeoutMessage'.tr, // 生成摘要请求超时,可能是网络问题或服务器繁忙。 style: TextStyle( fontSize: 16.sp, ), ), actions: [ TextButton( onPressed: () => Get.back(), child: Text( 'cancel'.tr, // 取消 style: TextStyle( fontSize: 16.sp, color: Colors.grey[700], ), ), ), TextButton( onPressed: () { Get.back(); // 使用延长的超时时间重试 controller.retryExtractSummaryWithExtendedTimeout(text); }, child: Text( 'retry'.tr, // 重试 style: TextStyle( fontSize: 16.sp, color: Colors.blue, fontWeight: FontWeight.bold, ), ), ), ], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.r), ), ), ); } // 显示删除单条记录确认对话框 void _showDeleteItemConfirmDialog(TranslationItem item) { final isDarkMode = Get.isDarkMode; Get.dialog( AlertDialog( backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, title: Text( 'deleteTranslationRecord'.tr, // 删除翻译记录 style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.black, ), ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'deleteTranslationConfirm'.tr, // 确定要删除这条翻译记录吗?此操作无法撤销。 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 = ''; if (controller.selectedTabIndex.value == 0) { // 分享原始记录 final String sourceText = _getCombinedSourceText(); final String translatedText = _getCombinedTranslatedText(); shareText = '${'originalText'.tr}:\n$sourceText\n\n${'translatedText'.tr}:\n$translatedText'; // 原文:译文: } else { // 分享摘要 if (controller.summaryResult.value.isNotEmpty) { shareText = '${'sessionSummary'.tr}:\n${controller.summaryResult.value}'; // 会话摘要: } else { Get.snackbar( 'cannotShare'.tr, // 无法分享 'generateSummaryFirst'.tr, // 请先生成摘要 snackPosition: SnackPosition.BOTTOM, duration: Duration(seconds: 2), ); return; } } // 调用系统分享 Share.share( shareText, subject: '${'translationSessionRecord'.tr} - ${DateFormat('yyyy-MM-dd HH:mm').format(controller.currentItem.timestamp)}', // 翻译会话记录 ); } }