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.
402 lines
12 KiB
402 lines
12 KiB
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:haptic_feedback/haptic_feedback.dart';
|
|
import '../../../core/theme/app_colors.dart';
|
|
import '../../../data/models/message_model.dart';
|
|
import '../../../data/services/network/api.dart';
|
|
|
|
/// 消息操作控制器
|
|
/// 负责处理消息长按、复制、举报等操作
|
|
class MessageActionController extends GetxController {
|
|
// 当前举报的消息
|
|
final reportingMessage = Rxn<Message>();
|
|
|
|
// 选择的举报原因
|
|
List<String> selectedReasons = [];
|
|
|
|
// 举报描述
|
|
final reportDescription = ''.obs;
|
|
|
|
/// 处理消息长按事件
|
|
void onLongPressMessage(
|
|
BuildContext context,
|
|
bool isDarkMode,
|
|
LongPressStartDetails details,
|
|
Message message,
|
|
) {
|
|
// 触发触觉反馈
|
|
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: _buildMenuItems(context, isDarkMode, message),
|
|
);
|
|
}
|
|
|
|
/// 构建弹出菜单项
|
|
List<PopupMenuEntry> _buildMenuItems(
|
|
BuildContext context,
|
|
bool isDarkMode,
|
|
Message message,
|
|
) {
|
|
final List<PopupMenuEntry> items = [
|
|
// 复制选项
|
|
_buildPopupMenuItem(
|
|
Icon(
|
|
Icons.copy,
|
|
size: 20.w,
|
|
color: isDarkMode ? Colors.white : Colors.black87,
|
|
),
|
|
'复制',
|
|
isDarkMode ? Colors.white : Colors.black87,
|
|
() => _copyMessage(message),
|
|
),
|
|
];
|
|
|
|
// 如果不是用户消息,添加举报选项
|
|
if (!message.isUser) {
|
|
items.addAll([
|
|
// 分隔线
|
|
PopupMenuItem(
|
|
height: 1,
|
|
enabled: false,
|
|
child: Divider(
|
|
height: 1,
|
|
thickness: 0.8,
|
|
indent: 0,
|
|
endIndent: 0,
|
|
color: isDarkMode ? Colors.grey[700] : Colors.grey[300],
|
|
),
|
|
),
|
|
// 举报选项
|
|
_buildPopupMenuItem(
|
|
Icon(
|
|
Icons.report,
|
|
size: 20.w,
|
|
color: isDarkMode ? Colors.red[700] : Colors.red,
|
|
),
|
|
'举报'.tr,
|
|
isDarkMode ? Colors.red[700]! : Colors.red,
|
|
() => _showReportForm(context, isDarkMode, message),
|
|
),
|
|
]);
|
|
}
|
|
|
|
return items;
|
|
}
|
|
|
|
/// 构建弹出菜单项
|
|
PopupMenuEntry _buildPopupMenuItem(
|
|
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 _copyMessage(Message message) {
|
|
Clipboard.setData(ClipboardData(text: message.text)).then((_) {
|
|
Get.snackbar(
|
|
'success'.tr,
|
|
'复制成功',
|
|
backgroundColor: Colors.blue.withOpacity(0.8),
|
|
colorText: Colors.white,
|
|
);
|
|
});
|
|
}
|
|
|
|
/// 显示举报表单
|
|
void _showReportForm(BuildContext context, bool isDarkMode, Message message) {
|
|
// 初始化举报状态
|
|
initReport(message);
|
|
|
|
showModalBottomSheet(
|
|
context: context,
|
|
isScrollControlled: true,
|
|
backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(16.r)),
|
|
),
|
|
builder: (BuildContext context) {
|
|
return Padding(
|
|
padding: EdgeInsets.only(
|
|
bottom: MediaQuery.of(context).viewInsets.bottom,
|
|
),
|
|
child: _ReportForm(isDarkMode: isDarkMode),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
/// 初始化举报状态
|
|
void initReport(Message message) {
|
|
reportingMessage.value = message;
|
|
selectedReasons.clear();
|
|
reportDescription.value = '';
|
|
}
|
|
|
|
/// 切换举报原因选择状态
|
|
void toggleReason(String reason) {
|
|
if (selectedReasons.contains(reason)) {
|
|
selectedReasons.remove(reason);
|
|
} else {
|
|
selectedReasons.add(reason);
|
|
}
|
|
update();
|
|
}
|
|
|
|
/// 更新举报描述
|
|
void updateDescription(String text) {
|
|
reportDescription.value = text;
|
|
}
|
|
|
|
/// 提交举报
|
|
Future<void> submitReport() async {
|
|
if (reportingMessage.value == null) return;
|
|
|
|
// 构建举报数据
|
|
final reportData = {
|
|
'describe': reportDescription.value,
|
|
'message': [],
|
|
'reportContext': reportingMessage.value!.text,
|
|
'reporttype': selectedReasons.toString(),
|
|
};
|
|
|
|
try {
|
|
// 调用API提交举报
|
|
var response = await Api.reportAiContent(reportData);
|
|
|
|
if (response != null) {
|
|
print('提交举报响应: $response');
|
|
}
|
|
|
|
// 提交成功后重置状态
|
|
selectedReasons.clear();
|
|
reportDescription.value = '';
|
|
reportingMessage.value = null;
|
|
|
|
// 关闭举报表单
|
|
Navigator.of(Get.context!).pop();
|
|
|
|
// 显示成功通知
|
|
Get.snackbar(
|
|
'提交成功',
|
|
'举报已提交,我们会尽快处理',
|
|
backgroundColor: Colors.green.withOpacity(0.8),
|
|
colorText: Colors.white,
|
|
duration: const Duration(seconds: 2),
|
|
);
|
|
} catch (e) {
|
|
// 显示错误通知
|
|
Get.snackbar(
|
|
'提交失败',
|
|
'网络错误,请稍后重试',
|
|
backgroundColor: Colors.red.withOpacity(0.8),
|
|
colorText: Colors.white,
|
|
duration: const Duration(seconds: 2),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 举报表单组件
|
|
class _ReportForm extends StatelessWidget {
|
|
final bool isDarkMode;
|
|
final MessageActionController controller = Get.find<MessageActionController>();
|
|
|
|
_ReportForm({required this.isDarkMode});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
padding: EdgeInsets.all(16.w),
|
|
child: SingleChildScrollView(
|
|
child: GetBuilder<MessageActionController>(
|
|
builder: (_) => Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
// 标题栏
|
|
Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
Center(
|
|
child: Text(
|
|
'举报',
|
|
style: TextStyle(
|
|
fontSize: 18.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: isDarkMode ? Colors.white : Colors.black,
|
|
),
|
|
),
|
|
),
|
|
Positioned(
|
|
right: 0,
|
|
child: IconButton(
|
|
icon: Icon(
|
|
Icons.close,
|
|
color: isDarkMode ? Colors.grey[400] : Colors.black54,
|
|
),
|
|
onPressed: () => Navigator.pop(context),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
// 分隔线
|
|
Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 12.w),
|
|
margin: EdgeInsets.only(bottom: 10.w),
|
|
child: Divider(
|
|
height: 1,
|
|
color: Colors.grey[300],
|
|
),
|
|
),
|
|
|
|
// 举报原因选择
|
|
GetBuilder<MessageActionController>(
|
|
builder: (controller) {
|
|
return Wrap(
|
|
spacing: 8.w,
|
|
runSpacing: 8.h,
|
|
children: [
|
|
_buildReportChip('有害/不安全', isDarkMode, controller),
|
|
_buildReportChip('色情低俗', isDarkMode, controller),
|
|
_buildReportChip('误导攻击', isDarkMode, controller),
|
|
_buildReportChip('违法犯罪', isDarkMode, controller),
|
|
_buildReportChip('隐私相关', isDarkMode, controller),
|
|
_buildReportChip('侵犯我的著作权、名誉权等', isDarkMode, controller),
|
|
_buildReportChip('其他', isDarkMode, controller),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
|
|
SizedBox(height: 16.h),
|
|
|
|
// 描述输入框
|
|
Container(
|
|
padding: EdgeInsets.all(12.w),
|
|
decoration: BoxDecoration(
|
|
color: isDarkMode ? Colors.grey[800] : Colors.grey[200],
|
|
borderRadius: BorderRadius.circular(8.r),
|
|
),
|
|
child: TextField(
|
|
maxLines: 3,
|
|
onChanged: controller.updateDescription,
|
|
style: TextStyle(
|
|
color: isDarkMode ? Colors.white : Colors.black,
|
|
),
|
|
decoration: InputDecoration(
|
|
hintText: '我们想知道你举报的原因,你可以描述你遇到的问题。',
|
|
hintStyle: TextStyle(
|
|
color: isDarkMode ? Colors.white70 : Colors.black54,
|
|
fontSize: 14.sp,
|
|
),
|
|
border: InputBorder.none,
|
|
),
|
|
),
|
|
),
|
|
|
|
SizedBox(height: 14.h),
|
|
|
|
// 提交按钮
|
|
ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.blue,
|
|
foregroundColor: Colors.white,
|
|
padding: EdgeInsets.symmetric(vertical: 12.h),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8.r),
|
|
),
|
|
),
|
|
child: Text('提交'),
|
|
onPressed: () => controller.submitReport(),
|
|
),
|
|
|
|
SizedBox(height: 8.h),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 构建举报原因芯片
|
|
Widget _buildReportChip(
|
|
String label,
|
|
bool isDarkMode,
|
|
MessageActionController controller,
|
|
) {
|
|
final isSelected = controller.selectedReasons.contains(label);
|
|
|
|
return GestureDetector(
|
|
onTap: () => controller.toggleReason(label),
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
|
|
decoration: BoxDecoration(
|
|
color: isSelected
|
|
? Colors.red
|
|
: (isDarkMode ? Colors.grey[800] : Colors.grey[200]),
|
|
borderRadius: BorderRadius.circular(16.r),
|
|
border: Border.all(
|
|
color: isSelected
|
|
? Colors.red[700]!
|
|
: (isDarkMode ? Colors.grey[700]! : Colors.grey[300]!),
|
|
width: isSelected ? 1.0 : 0.5,
|
|
),
|
|
),
|
|
child: Text(
|
|
label,
|
|
style: TextStyle(
|
|
color: isSelected
|
|
? Colors.white
|
|
: (isDarkMode ? Colors.white : Colors.black87),
|
|
fontSize: 14.sp,
|
|
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|