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_history_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'; 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: '原始记录', isSelected: controller.selectedTabIndex.value == 0, onTap: () => controller.switchTab(0), isDarkMode: isDarkMode, )), ), SizedBox(width: 12.w), Expanded( child: Obx(() => _buildTabButton( title: '记录摘要', 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) { 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: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 源文本部分 Padding( padding: EdgeInsets.fromLTRB(20.r, 20.r, 20.r, 10.r), child: Text( item.sourceText, style: TextStyle( fontSize: 15.sp, color: isDarkMode ? Colors.white : Colors.black, fontWeight: FontWeight.w500, ), ), ), // 翻译文本部分 Padding( padding: EdgeInsets.fromLTRB(20.r, 10.r, 20.r, 16.r), child: Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Expanded( child: Text( item.translatedText, style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[700], fontWeight: FontWeight.normal, ), ), ), SizedBox(width: 12.w), _buildPlayButton(() => controller.playTranslationItem(item), isDarkMode), ], ), ), ], ), ); } // 构建播放按钮 Widget _buildPlayButton(VoidCallback onTap, bool isDarkMode) { return Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(20.r), splashColor: isDarkMode ? AppColors.primary.withOpacity(0.2) : Colors.blue.withOpacity(0.2), child: Container( width: 28.w, height: 28.h, decoration: BoxDecoration( color: isDarkMode ? AppColors.primary.withAlpha(10) : Colors.blue.withAlpha(10), shape: BoxShape.circle, ), child: Icon( Icons.volume_up, color: Colors.white, size: 16.sp, ), ), ), ); } // 构建总结视图 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( '点击下方按钮生成会话摘要', 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( '摘要内容', 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( '已复制', '摘要已复制到剪贴板', 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 ? '提取摘要' : '已生成摘要', 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( '生成中...', 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( '重新生成', 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( '请求超时', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, ), ), content: Text( '生成摘要请求超时,可能是网络问题或服务器繁忙。', style: TextStyle( fontSize: 16.sp, ), ), actions: [ TextButton( onPressed: () => Get.back(), child: Text( '取消', style: TextStyle( fontSize: 16.sp, color: Colors.grey[700], ), ), ), TextButton( onPressed: () { Get.back(); // 使用延长的超时时间重试 controller.retryExtractSummaryWithExtendedTimeout(text); }, child: Text( '重试', style: TextStyle( fontSize: 16.sp, color: Colors.blue, 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 = '原文:\n$sourceText\n\n译文:\n$translatedText'; } else { // 分享摘要 if (controller.summaryResult.value.isNotEmpty) { shareText = '会话摘要:\n${controller.summaryResult.value}'; } else { Get.snackbar( '无法分享', '请先生成摘要', snackPosition: SnackPosition.BOTTOM, duration: Duration(seconds: 2), ); return; } } // 调用系统分享 Share.share( shareText, subject: '翻译会话记录 - ${DateFormat('yyyy-MM-dd HH:mm').format(controller.currentItem.timestamp)}', ); } }