You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
633 lines
20 KiB
633 lines
20 KiB
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.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<TranslationDetailController> {
|
|
TranslationDetailView({Key? key}) : super(key: key);
|
|
|
|
// 缓存文本,避免重复计算
|
|
final _sourceTextCache = Rx<String?>(null);
|
|
final _translatedTextCache = Rx<String?>(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: SafeArea(
|
|
top: false,
|
|
child: 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<Widget> _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 _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)}', // 翻译会话记录
|
|
);
|
|
}
|
|
}
|
|
|