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.

1392 lines
51 KiB

import 'package:common_utils/common_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:haptic_feedback/haptic_feedback.dart';
import '../../../core/theme/app_colors.dart';
import '../../../routes/app_routes.dart';
import '../controllers/meeting_controller.dart';
import '../model/meeting_model.dart';
class MeetingView extends GetView<MeetingController> {
const MeetingView({super.key});
@override
Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return PopScope(
onPopInvokedWithResult: controller.onPopInvokedWithResult,
child: Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
appBar: _buildAppBar(context, isDarkMode),
body: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildConnect(isDarkMode),
Expanded(
child: _buildBody(context, isDarkMode),
),
],
),
_buildMicButton(context, isDarkMode),
],
),
floatingActionButton: _buildFloatingButtons(),
),
);
}
Widget _buildConnect(bool isDarkMode) {
return GestureDetector(
onTap: () {
Get.toNamed(Routes.meetingConnect);
},
child: Container(
padding: EdgeInsets.all(5.w),
margin: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 10.w,
),
decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white,
borderRadius: BorderRadius.circular(50.w),
boxShadow: [
BoxShadow(
color: isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(50.w),
),
child: Icon(
Icons.add,
color: Colors.white,
size: 20.sp,
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: Text(
'连接',
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87,
fontSize: 14.sp,
),
),
)
],
),
),
);
}
/// 构建底部中间的录音按钮
Widget _buildMicButton(BuildContext context, bool isDarkMode) {
return Positioned(
bottom: 20,
left: 0,
right: 0,
child: Center(
child: FloatingActionButton(
heroTag: "recordAudio",
onPressed: () {
_showStartsRecordSheet(context, isDarkMode);
},
child: const Icon(Icons.mic),
),
),
);
}
/// 构建右下角的悬浮按钮
Widget _buildFloatingButtons() {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
FloatingActionButton(
heroTag: "addAudio",
onPressed: () {
controller.addAudio();
},
child: const Icon(Icons.add),
),
],
);
}
// 开始录音弹窗
void _showStartsRecordSheet(BuildContext context, bool isDarkMode) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) {
return Container(
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(18.w)),
),
padding: EdgeInsets.only(
left: 0,
right: 0,
top: 12.w,
bottom: MediaQuery.of(context).viewInsets.bottom + 12.w,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 8.w),
child: Text(
'开始录音',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
color: isDarkMode ? Colors.white : Colors.black87,
),
),
),
Divider(
height: 1,
color: isDarkMode ? Colors.white12 : Colors.grey[200]),
// 选项列表
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.w),
child: Column(
children: [
// 现场录音 - 始终可用
_importAudioItem(
context,
isDarkMode,
icon: Image.asset('assets/images/explore.png',
width: 32.w, height: 32.w),
title: '现场录音',
enabled: true, // 始终可用
onTap: () async {
Navigator.of(context).pop();
await Get.toNamed(Routes.meetingRecord, arguments: {
'audioTypes': 0,
});
controller.addMeetingAudio();
},
),
SizedBox(height: 8.w),
// 音、视频录音 - 根据控制器状态禁用
_importAudioItem(
context, isDarkMode,
icon: Image.asset('assets/images/explore.png',
width: 32.w, height: 32.w),
title: '音、视频录音',
enabled: controller.bleManager.isConnected, // 依赖控制器状态
onTap: controller.bleManager.isConnected
? () async {
// 条件启用
Navigator.of(context).pop();
await Get.toNamed(Routes.meetingRecord,
arguments: {
'audioTypes': 1,
});
controller.addMeetingAudio();
}
: null, // 禁用时置空onTap
),
SizedBox(height: 8.w),
// 通话录音 - 根据控制器状态禁用
_importAudioItem(
context, isDarkMode,
icon: Row(
// ... 图标组合保持不变
),
title: '通话录音',
enabled: controller.bleManager.isConnected, // 依赖控制器状态
onTap: controller.bleManager.isConnected
? () async {
// 条件启用
Navigator.of(context).pop();
await Get.toNamed(Routes.meetingRecord,
arguments: {
'audioTypes': 2,
});
controller.addMeetingAudio();
}
: null, // 禁用时置空onTap
),
],
),
),
SizedBox(height: 8.w),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: Text(
'支持导入单文件时长不超过5小时的音频',
style: TextStyle(
fontSize: 13.sp,
color: isDarkMode ? Colors.white54 : Colors.grey[600],
),
),
),
SizedBox(height: 16.w),
GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Icon(Icons.close, size: 32.w, color: Colors.grey),
),
SizedBox(height: 8.w),
],
),
);
},
);
}
// 选项框 (增加enabled参数)
Widget _importAudioItem(
BuildContext context,
bool isDarkMode, {
required Widget icon,
required String title,
required bool enabled, // 新增启用状态参数
required VoidCallback? onTap, // 改为可空回调
}) {
return Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(12.w),
onTap: onTap, // 禁用时onTap为null
child: Container(
height: 52.w,
padding: EdgeInsets.symmetric(horizontal: 12.w),
decoration: BoxDecoration(
color: isDarkMode
? Colors.white.withOpacity(enabled ? 0.03 : 0.01) // 调整禁用透明度
: Colors.grey[50]!.withOpacity(enabled ? 1 : 0.5),
borderRadius: BorderRadius.circular(12.w),
),
child: Row(
children: [
// 图标添加透明度效果
Opacity(
opacity: enabled ? 1.0 : 0.5,
child: icon,
),
SizedBox(width: 14.w),
Expanded(
child: Text(
title,
style: TextStyle(
fontSize: 16.sp,
color: isDarkMode
? Colors.white.withOpacity(enabled ? 1 : 0.5)
: Colors.black87.withOpacity(enabled ? 1 : 0.5),
),
),
),
Icon(Icons.arrow_forward_ios,
size: 18.w,
color: Colors.grey.withOpacity(enabled ? 1 : 0.5)),
],
),
),
),
);
}
// 新增右侧弹窗
void _showRightDrawerDialog(BuildContext context, bool isDarkMode) {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: "rightDrawer",
transitionDuration: const Duration(milliseconds: 300),
pageBuilder: (context, anim1, anim2) {
return Align(
alignment: Alignment.centerRight,
child: FractionallySizedBox(
widthFactor: 0.7,
child: Material(
color: Colors.transparent,
child: Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
color: isDarkMode ? Colors.white : Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(18.w),
bottomLeft: Radius.circular(18.w),
),
),
child: _buildDrawerContent(context, isDarkMode),
),
),
),
);
},
transitionBuilder: (context, anim1, anim2, child) {
final offsetAnimation = Tween<Offset>(
begin: const Offset(1, 0),
end: Offset.zero,
).animate(CurvedAnimation(parent: anim1, curve: Curves.easeOutCubic));
return SlideTransition(
position: offsetAnimation,
child: child,
);
},
);
}
// 侧边栏内容
Widget _buildDrawerContent(BuildContext context, bool isDarkMode) {
return SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(20.w),
child: TextField(
decoration: InputDecoration(
hintText: '在所有文件中搜索',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.w),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.grey[100],
contentPadding: EdgeInsets.zero,
),
),
),
ListTile(
leading: Icon(Icons.folder, color: Colors.red),
title:
Text('全部文件(4)', style: TextStyle(fontWeight: FontWeight.bold)),
onTap: () {},
),
ListTile(
leading: Icon(Icons.category, color: Colors.blue),
title: Text('未分类(4)'),
onTap: () {},
),
ListTile(
leading: Icon(Icons.delete, color: Colors.teal),
title: Text('回收站'),
onTap: () {},
),
Divider(),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.w),
child: Text('文件夹', style: TextStyle(color: Colors.grey)),
),
...['NOTE', 'CALL', 'NotePin', 'Import', 'Desktop']
.map((name) => ListTile(
title: Text(name),
onTap: () {},
)),
Divider(),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.w),
child: Text('排序方式', style: TextStyle(color: Colors.grey)),
),
ListTile(
title: Text('创建时间'),
trailing: Icon(Icons.arrow_upward),
onTap: () {},
),
ListTile(
title: Text('修改时间'),
trailing: Icon(Icons.arrow_upward),
onTap: () {},
),
],
),
);
}
// 构建应用栏
PreferredSizeWidget _buildAppBar(BuildContext context, bool isDarkMode) {
return AppBar(
backgroundColor: isDarkMode ? Colors.black : Colors.white,
surfaceTintColor: Colors.transparent,
elevation: 0.5,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
color: isDarkMode ? Colors.white : Colors.black,
size: 20.sp,
),
onPressed: () => Get.back(),
),
title: Text(
'conferenceAssistant'.tr, // 商务会议助手
style: TextStyle(
fontSize: 17.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
),
),
centerTitle: true,
actions: [
IconButton(
icon: Icon(Icons.folder_open,
color: isDarkMode ? Colors.white : Colors.black),
onPressed: () {
_showRightDrawerDialog(context, isDarkMode);
},
),
],
);
}
// 构建主体内容
Widget _buildBody(BuildContext context, bool isDarkMode) {
return Obx(
() => ListView.builder(
controller: controller.listController,
itemCount: controller.dataList.length,
padding: EdgeInsets.fromLTRB(12.w, 5.w, 12.w, 90.w),
itemBuilder: (BuildContext context, int index) {
MeetingModel data = MeetingModel.fromJson(controller.dataList[index]);
return _item(context, isDarkMode, data);
},
),
);
}
Widget _item(BuildContext context, bool isDarkMode, MeetingModel data) {
return GestureDetector(
onTap: () {
Get.toNamed(Routes.meetingDetails, arguments: {
'id': data.id,
});
},
onLongPressStart: (LongPressStartDetails details) {
_onLongPressItem(context, isDarkMode, details, data);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 10.w),
margin: EdgeInsets.symmetric(vertical: 5.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white,
borderRadius: BorderRadius.circular(12.w),
boxShadow: [
BoxShadow(
color: isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
data.title,
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87,
fontSize: 18.sp,
),
overflow: TextOverflow.ellipsis,
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.w),
child: Row(
children: [
Icon(
Icons.calendar_month,
size: 14.w,
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
),
2.horizontalSpace,
Text(
DateUtil.formatDateMs(
data.creationtime,
format: DateFormats.y_mo_d_h_m,
),
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 12.sp,
),
),
20.horizontalSpace,
Icon(
Icons.access_time,
size: 14.w,
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
),
2.horizontalSpace,
Text(
_formatDateSeconds(data.seconds),
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 12.sp,
),
),
],
),
),
Row(
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(
data.type,
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
fontSize: 10.sp,
),
),
),
const Spacer(),
Obx(() {
Map uploadData =
controller.meetingUploadService.uploadList.firstWhere(
(element) => element['id'] == data.id,
orElse: () => {},
);
return uploadData.isNotEmpty
? Stack(
alignment: Alignment.center,
children: [
if (uploadData['id'] ==
controller.meetingUploadService.meetingId.value)
Obx(
() => SizedBox(
width: 20.w,
height: 20.w,
child: CircularProgressIndicator(
value: controller.meetingUploadService
.uploadProgress.value,
valueColor:
const AlwaysStoppedAnimation<Color>(
Colors.red),
strokeWidth: 1.w,
),
),
),
SizedBox(
width: 20.w,
height: 20.w,
child: Icon(
uploadData['audiourl'].isNotEmpty
? Icons.cloud_done
: Icons.cloud_upload,
color: Colors.blue,
size: 16.w,
),
),
],
)
: const SizedBox();
}),
Obx(() {
Map taskData =
controller.meetingTaskService.taskFinishList.firstWhere(
(element) => element['id'] == data.id,
orElse: () => {},
);
return taskData.isNotEmpty
? Padding(
padding: EdgeInsets.only(top: 5.w),
child: Text(
taskData['tasktype'] == 1
? '正在转写...'
: taskData['tasktype'] == 2
? '正在总结...'
: '总结完成',
style: TextStyle(
color: Colors.blue,
fontSize: 10.sp,
),
),
)
: const SizedBox();
}),
],
),
],
),
),
);
}
void _onLongPressItem(BuildContext context, bool isDarkMode,
LongPressStartDetails details, MeetingModel data) {
Haptics.vibrate(HapticsType.success);
RenderBox? renderBox =
Overlay.of(context).context.findRenderObject() as RenderBox;
final RelativeRect position = RelativeRect.fromRect(
Rect.fromLTRB(
details.globalPosition.dx,
details.globalPosition.dy,
details.globalPosition.dx + 110, // 菜单显示位置X轴坐标
details.globalPosition.dy - 40, // 菜单显示位置Y轴坐标
),
Offset.zero & renderBox.size,
);
showMenu(
context: context,
position: position,
color: isDarkMode ? AppColors.cardBackground : Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
items: [
_popupMenuEntry(
Icon(
Icons.edit_calendar,
size: 20.w,
color: isDarkMode ? Colors.white : Colors.black87,
),
'edit'.tr, // 编辑
isDarkMode ? Colors.white : Colors.black87,
() {
final dotIndex = data.title.lastIndexOf('.');
controller.titleController.text =
dotIndex != -1 ? data.title.substring(0, dotIndex) : data.title;
controller.titleCSuffix =
data.title.substring(dotIndex, data.title.length);
_bottomSheet(context, isDarkMode, data.id);
},
),
_popupMenuEntry(
Icon(
Icons.delete_outline,
size: 20.w,
color: isDarkMode ? Colors.red[700] : Colors.red,
),
'delete'.tr, // 删除
isDarkMode ? Colors.red[700]! : Colors.red,
() {
controller.deleteMeeting(data.id, data.filepath);
},
),
],
);
}
PopupMenuEntry _popupMenuEntry(
Icon icon,
String title,
Color color,
Function() onTap,
) {
return PopupMenuItem(
onTap: onTap,
child: SizedBox(
width: 140.w,
child: Row(
children: [
icon,
8.horizontalSpace,
Text(
title,
style: TextStyle(
color: color,
fontSize: 14.sp,
),
),
],
),
),
);
}
void _bottomSheet(
BuildContext context,
bool isDarkMode,
int id,
) {
Get.bottomSheet(
Padding(
padding: MediaQuery.of(context).viewInsets,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 20.w),
color: isDarkMode ? AppColors.darkBackground : Colors.grey[50],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'editMeetingTitle'.tr, // 编辑会议标题
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87,
fontSize: 16.sp,
),
),
Container(
height: 48.w,
margin: EdgeInsets.only(top: 10.w, bottom: 20.w),
alignment: Alignment.center,
decoration: BoxDecoration(
color: isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.white,
borderRadius: BorderRadius.circular(12.w),
boxShadow: [
BoxShadow(
color: isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: TextField(
controller: controller.titleController,
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87,
fontSize: 16.sp,
),
decoration: InputDecoration(
hintText: 'maxCharacters'.tr,
hintStyle: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54,
fontSize: 16.sp,
),
fillColor: Colors.transparent,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 10.w),
),
),
),
Padding(
padding: EdgeInsets.only(top: 30.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
Get.back();
},
child: Container(
width: 120.w,
height: 40.w,
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(
width: 2.w,
color: isDarkMode ? AppColors.primary : Colors.blue,
),
borderRadius: BorderRadius.circular(12.w),
),
child: Text(
'cancel'.tr, // 取消
style: TextStyle(
fontSize: 14.sp,
color: isDarkMode ? AppColors.primary : Colors.blue,
),
),
),
),
GestureDetector(
onTap: () {
String title = controller.titleController.text +
controller.titleCSuffix;
controller.editMeetingTitle(id, title);
},
child: Container(
width: 120.w,
height: 40.w,
alignment: Alignment.center,
decoration: BoxDecoration(
color: isDarkMode ? AppColors.primary : Colors.blue,
borderRadius: BorderRadius.circular(12.w),
),
child: Text(
'confirm'.tr, // 确认
style: TextStyle(
color: Colors.white,
fontSize: 14.sp,
),
),
),
),
],
),
),
],
),
),
),
isScrollControlled: true,
clipBehavior: Clip.hardEdge,
);
}
String _formatDateSeconds(int seconds) {
if (seconds < 60) {
return '${seconds}s';
} else if (seconds < 3600) {
int minutes = seconds ~/ 60;
int remainingSeconds = seconds % 60;
return '${minutes}m ${remainingSeconds}s';
} else {
int hours = seconds ~/ 3600;
int remainingMinutes = (seconds % 3600) ~/ 60;
int remainingSeconds = seconds % 60;
return '${hours}h ${remainingMinutes}m ${remainingSeconds}s';
}
}
// ...existing code...
// // 选项框
// Widget _importAudioItem1(
// BuildContext context,
// bool isDarkMode, {
// required Widget icon,
// required String title,
// required VoidCallback onTap,
// }) {
// return Material(
// color: Colors.transparent,
// child: InkWell(
// borderRadius: BorderRadius.circular(12.w),
// onTap: onTap,
// child: Container(
// height: 52.w,
// padding: EdgeInsets.symmetric(horizontal: 12.w),
// decoration: BoxDecoration(
// color:
// isDarkMode ? Colors.white.withOpacity(0.03) : Colors.grey[50],
// borderRadius: BorderRadius.circular(12.w),
// ),
// child: Row(
// children: [
// icon,
// SizedBox(width: 14.w),
// Expanded(
// child: Text(
// title,
// style: TextStyle(
// fontSize: 16.sp,
// color: isDarkMode ? Colors.white : Colors.black87,
// ),
// ),
// ),
// Icon(Icons.arrow_forward_ios, size: 18.w, color: Colors.grey),
// ],
// ),
// ),
// ),
// );
// }
// // 底部+号菜单
// void _showAddAudioMenu(BuildContext context, bool isDarkMode) {
// showModalBottomSheet(
// context: context,
// isScrollControlled: true,
// backgroundColor: Colors.transparent,
// builder: (context) {
// return Padding(
// padding: EdgeInsets.only(
// bottom: MediaQuery.of(context).viewInsets.bottom + 30.w,
// left: 16.w,
// right: 16.w,
// ),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// // 录音按钮
// Container(
// margin: EdgeInsets.only(bottom: 12.w),
// child: GestureDetector(
// onTap: () {
// Navigator.of(context).pop();
// // TODO: 实现开始录音逻辑
// _showStartsRecordSheet(context, isDarkMode);
// },
// child: Container(
// height: 56.w,
// decoration: BoxDecoration(
// color: isDarkMode
// ? Colors.white.withOpacity(0.05)
// : Colors.grey[100],
// borderRadius: BorderRadius.circular(28.w),
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Container(
// width: 36.w,
// height: 36.w,
// decoration: BoxDecoration(
// color: Colors.red.withOpacity(0.15),
// shape: BoxShape.circle,
// ),
// child: Icon(Icons.mic, color: Colors.red, size: 24.w),
// ),
// SizedBox(width: 12.w),
// Text(
// '开始录音',
// style: TextStyle(
// color: isDarkMode ? Colors.white70 : Colors.black87,
// fontSize: 16.sp,
// fontWeight: FontWeight.w500,
// ),
// ),
// Spacer(),
// Icon(Icons.arrow_forward_ios,
// color: Colors.grey, size: 18.w),
// SizedBox(width: 16.w),
// ],
// ),
// ),
// ),
// ),
// // 导入音频按钮
// Container(
// margin: EdgeInsets.only(bottom: 24.w),
// child: GestureDetector(
// onTap: () {
// Navigator.of(context).pop();
// _showImportAudioSheet(context, isDarkMode);
// },
// child: Container(
// height: 56.w,
// decoration: BoxDecoration(
// color: isDarkMode
// ? Colors.white.withOpacity(0.10)
// : Colors.white,
// borderRadius: BorderRadius.circular(28.w),
// border: Border.all(
// color: Colors.blue.withOpacity(0.15),
// width: 2,
// ),
// boxShadow: [
// BoxShadow(
// color: Colors.blue.withOpacity(0.08),
// blurRadius: 8,
// offset: Offset(0, 2),
// ),
// ],
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Container(
// width: 36.w,
// height: 36.w,
// decoration: BoxDecoration(
// color: Colors.blue.withOpacity(0.15),
// shape: BoxShape.circle,
// ),
// child: Icon(Icons.file_upload_outlined,
// color: Colors.blue, size: 24.w),
// ),
// SizedBox(width: 12.w),
// Text(
// '导入音频',
// style: TextStyle(
// color: Colors.blue,
// fontSize: 16.sp,
// fontWeight: FontWeight.w500,
// ),
// ),
// Spacer(),
// Icon(Icons.arrow_forward_ios,
// color: Colors.grey, size: 18.w),
// SizedBox(width: 16.w),
// ],
// ),
// ),
// ),
// ),
// // 关闭按钮
// GestureDetector(
// onTap: () => Navigator.of(context).pop(),
// child: Container(
// width: 56.w,
// height: 56.w,
// decoration: BoxDecoration(
// color: isDarkMode
// ? Colors.white.withOpacity(0.08)
// : Colors.black.withOpacity(0.08),
// shape: BoxShape.circle,
// ),
// child: Icon(Icons.close,
// color: isDarkMode ? Colors.white : Colors.black54,
// size: 32.w),
// ),
// ),
// SizedBox(height: 10.w),
// ],
// ),
// );
// },
// );
// }
// // 新增:从opus导出选择弹窗
// void _showLocalOpusPicker(BuildContext context, bool isDarkMode) async {
// // 这里以用户目录下的 Downloads 为例,可根据实际需求修改路径
// controller.loadTranslatopus();
// showModalBottomSheet(
// context: context,
// isScrollControlled: true,
// backgroundColor: Colors.transparent,
// builder: (context) {
// return StatefulBuilder(
// builder: (context, setState) {
// return Container(
// decoration: BoxDecoration(
// color: isDarkMode ? Colors.grey[900] : Colors.white,
// borderRadius: BorderRadius.vertical(top: Radius.circular(18.w)),
// ),
// padding: EdgeInsets.only(
// left: 0,
// right: 0,
// top: 12.w,
// bottom: MediaQuery.of(context).viewInsets.bottom + 12.w,
// ),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// Text(
// '选择音视频文件',
// style: TextStyle(
// fontSize: 18.sp,
// fontWeight: FontWeight.bold,
// color: isDarkMode ? Colors.white : Colors.black87,
// ),
// ),
// Divider(
// height: 1,
// color: isDarkMode ? Colors.white12 : Colors.grey[200]),
// SizedBox(height: 8.w),
// SizedBox(
// height: 300.w,
// child: ListView.builder(
// itemCount: controller.files.length,
// itemBuilder: (context, idx) {
// final file = controller.files[idx];
// final fileName = file.path.split('/').last;
// final isChecked =
// controller.selected.contains(file.path);
// return CheckboxListTile(
// value: isChecked,
// onChanged: (checked) {
// setState(() {
// if (checked == true) {
// controller.selected.add(file.path);
// } else {
// controller.selected.remove(file.path);
// }
// });
// },
// title: Text(
// fileName,
// style: TextStyle(
// color: isDarkMode ? Colors.white : Colors.black87,
// fontSize: 15.sp,
// ),
// ),
// controlAffinity: ListTileControlAffinity.leading,
// activeColor: Colors.blue,
// );
// },
// ),
// ),
// SizedBox(height: 12.w),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: Colors.grey,
// ),
// onPressed: () => Navigator.of(context).pop(),
// child:
// Text('取消', style: TextStyle(color: Colors.white)),
// ),
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: Colors.blue,
// ),
// onPressed: controller.selected.isEmpty
// ? null
// : () {
// Navigator.of(context).pop();
// // TODO: 调用导入逻辑
// controller.shareAllTranslatAudio();
// },
// child:
// Text('导入选中', style: TextStyle(color: Colors.white)),
// ),
// ],
// ),
// SizedBox(height: 8.w),
// ],
// ),
// );
// },
// );
// },
// );
// }
// // 新增:本地音视频文件选择弹窗
// void _showLocalAudioVideoPicker(BuildContext context, bool isDarkMode) async {
// // 这里以用户目录下的 Downloads 为例,可根据实际需求修改路径
// controller.loadTranslatAudio();
// showModalBottomSheet(
// context: context,
// isScrollControlled: true,
// backgroundColor: Colors.transparent,
// builder: (context) {
// return StatefulBuilder(
// builder: (context, setState) {
// return Container(
// decoration: BoxDecoration(
// color: isDarkMode ? Colors.grey[900] : Colors.white,
// borderRadius: BorderRadius.vertical(top: Radius.circular(18.w)),
// ),
// padding: EdgeInsets.only(
// left: 0,
// right: 0,
// top: 12.w,
// bottom: MediaQuery.of(context).viewInsets.bottom + 12.w,
// ),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// Text(
// '选择音视频文件',
// style: TextStyle(
// fontSize: 18.sp,
// fontWeight: FontWeight.bold,
// color: isDarkMode ? Colors.white : Colors.black87,
// ),
// ),
// Divider(
// height: 1,
// color: isDarkMode ? Colors.white12 : Colors.grey[200]),
// SizedBox(height: 8.w),
// SizedBox(
// height: 300.w,
// child: ListView.builder(
// itemCount: controller.files.length,
// itemBuilder: (context, idx) {
// final file = controller.files[idx];
// final fileName = file.path.split('/').last;
// final isChecked =
// controller.selected.contains(file.path);
// return CheckboxListTile(
// value: isChecked,
// onChanged: (checked) {
// setState(() {
// if (checked == true) {
// controller.selected.add(file.path);
// } else {
// controller.selected.remove(file.path);
// }
// });
// },
// title: Text(
// fileName,
// style: TextStyle(
// color: isDarkMode ? Colors.white : Colors.black87,
// fontSize: 15.sp,
// ),
// ),
// controlAffinity: ListTileControlAffinity.leading,
// activeColor: Colors.blue,
// );
// },
// ),
// ),
// SizedBox(height: 12.w),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: Colors.grey,
// ),
// onPressed: () => Navigator.of(context).pop(),
// child:
// Text('取消', style: TextStyle(color: Colors.white)),
// ),
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: Colors.blue,
// ),
// onPressed: controller.selected.isEmpty
// ? null
// : () {
// Navigator.of(context).pop();
// // TODO: 调用导入逻辑
// controller.addTranslatAudio();
// },
// child:
// Text('导入选中', style: TextStyle(color: Colors.white)),
// ),
// ],
// ),
// SizedBox(height: 8.w),
// ],
// ),
// );
// },
// );
// },
// );
// }
// // 导入音频弹窗
// void _showImportAudioSheet(BuildContext context, bool isDarkMode) {
// showModalBottomSheet(
// context: context,
// isScrollControlled: true,
// backgroundColor: Colors.transparent,
// builder: (context) {
// return Container(
// decoration: BoxDecoration(
// color: isDarkMode ? Colors.grey[900] : Colors.white,
// borderRadius: BorderRadius.vertical(top: Radius.circular(18.w)),
// ),
// padding: EdgeInsets.only(
// left: 0,
// right: 0,
// top: 12.w,
// bottom: MediaQuery.of(context).viewInsets.bottom + 12.w,
// ),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// Container(
// alignment: Alignment.centerLeft,
// padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 8.w),
// child: Text(
// '导入音频',
// style: TextStyle(
// fontSize: 18.sp,
// fontWeight: FontWeight.bold,
// color: isDarkMode ? Colors.white : Colors.black87,
// ),
// ),
// ),
// Divider(
// height: 1,
// color: isDarkMode ? Colors.white12 : Colors.grey[200]),
// // 选项列表
// Padding(
// padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.w),
// child: Column(
// children: [
// _importAudioItem1(
// context,
// isDarkMode,
// icon: Image.asset('assets/images/explore.png',
// width: 32.w, height: 32.w),
// title: '从外部导入',
// onTap: () {
// Navigator.of(context).pop();
// controller.addAudio();
// // TODO: 实现从文件导入逻辑
// },
// ),
// // SizedBox(height: 8.w),
// // _importAudioItem1(
// // context,
// // isDarkMode,
// // icon: Image.asset('assets/images/explore.png',
// // width: 32.w, height: 32.w),
// // title: '从翻译里导入',
// // onTap: () {
// // Navigator.of(context).pop();
// // _showLocalAudioVideoPicker(context, isDarkMode);
// // // TODO: 实现从相册导入逻辑
// // },
// // ),
// // SizedBox(height: 8.w),
// // _importAudioItem1(
// // context,
// // isDarkMode,
// // icon: Row(
// // mainAxisSize: MainAxisSize.min,
// // children: [
// // Image.asset('assets/images/explore.png',
// // width: 20.w, height: 20.w),
// // SizedBox(width: 4.w),
// // Image.asset('assets/images/explore.png',
// // width: 20.w, height: 20.w),
// // SizedBox(width: 4.w),
// // Image.asset('assets/images/explore.png',
// // width: 20.w, height: 20.w),
// // ],
// // ),
// // title: '从opus导出',
// // onTap: () {
// // Navigator.of(context).pop();
// // // TODO: 实现从其他APP导入逻辑
// // _showLocalOpusPicker(context, isDarkMode);
// // },
// // ),
// ],
// ),
// ),
// SizedBox(height: 8.w),
// Padding(
// padding: EdgeInsets.symmetric(horizontal: 20.w),
// child: Text(
// '支持导入单文件时长不超过5小时的音频',
// style: TextStyle(
// fontSize: 13.sp,
// color: isDarkMode ? Colors.white54 : Colors.grey[600],
// ),
// ),
// ),
// SizedBox(height: 16.w),
// GestureDetector(
// onTap: () => Navigator.of(context).pop(),
// child: Icon(Icons.close, size: 32.w, color: Colors.grey),
// ),
// SizedBox(height: 8.w),
// ],
// ),
// );
// },
// );
// }
// void _showRecordingSheet(BuildContext context, bool isDarkMode) {
// if (_recordingOverlayEntry != null) return; // 防止重复添加
// _recordingOverlayEntry = OverlayEntry(
// builder: (ctx) => RecordingFloatingPanel(
// onStop: () {
// _recordingOverlayEntry?.remove();
// _recordingOverlayEntry = null;
// // 录音完成后的逻辑
// },
// ),
// );
// Overlay.of(context).insert(_recordingOverlayEntry!);
// }
}