From 8645aa354c79e729fbaf2b2049ad6c3f1662dc28 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Fri, 27 Jun 2025 17:39:38 +0800 Subject: [PATCH] =?UTF-8?q?refactor(agent):=20=E9=87=8D=E6=9E=84=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=93=8D=E4=BD=9C=E9=80=BB=E8=BE=91=E8=87=B3=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E6=8E=A7=E5=88=B6=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将原本分散在AgentView中的消息操作逻辑(复制、举报等)提取到独立的MessageActionController中 删除不再使用的ReportController 更新相关绑定和视图引用 --- lib/modules/agent/bindings/agent_binding.dart | 4 +- .../message_action_controller.dart | 402 ++++++++++++++++++ .../agent/controllers/report_controller.dart | 80 ---- lib/modules/agent/views/agent_view.dart | 274 +----------- 4 files changed, 409 insertions(+), 351 deletions(-) create mode 100644 lib/modules/agent/controllers/message_action_controller.dart delete mode 100644 lib/modules/agent/controllers/report_controller.dart diff --git a/lib/modules/agent/bindings/agent_binding.dart b/lib/modules/agent/bindings/agent_binding.dart index ebb69cab2..4ae587113 100644 --- a/lib/modules/agent/bindings/agent_binding.dart +++ b/lib/modules/agent/bindings/agent_binding.dart @@ -1,11 +1,11 @@ import 'package:get/get.dart'; import '../controllers/agent_controller.dart'; -import '../controllers/report_controller.dart'; +import '../controllers/message_action_controller.dart'; class AgentBinding extends Bindings { @override void dependencies() { Get.lazyPut(() => AgentController()); - Get.lazyPut(() => ReportController()); + Get.lazyPut(() => MessageActionController()); } } diff --git a/lib/modules/agent/controllers/message_action_controller.dart b/lib/modules/agent/controllers/message_action_controller.dart new file mode 100644 index 000000000..00efd5963 --- /dev/null +++ b/lib/modules/agent/controllers/message_action_controller.dart @@ -0,0 +1,402 @@ +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(); + + // 选择的举报原因 + List 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 _buildMenuItems( + BuildContext context, + bool isDarkMode, + Message message, + ) { + final List 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 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(); + + _ReportForm({required this.isDarkMode}); + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.all(16.w), + child: SingleChildScrollView( + child: GetBuilder( + 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( + 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, + ), + ), + ), + ); + } +} diff --git a/lib/modules/agent/controllers/report_controller.dart b/lib/modules/agent/controllers/report_controller.dart deleted file mode 100644 index 816d8277d..000000000 --- a/lib/modules/agent/controllers/report_controller.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:get/get.dart'; -//import 'agent_controller.dart'; // 确保导入Message模型 -import 'package:flutter/material.dart'; -import '../../../data/services/network/api.dart'; -import '../../../data/models/message_model.dart'; - -class ReportController extends GetxController { - // 当前举报的消息 - final reportingMessage = Rxn(); - - // 选择的举报原因 - List selectedReasons = []; - - // 举报描述 - final reportDescription = ''.obs; - - // 初始化举报状态 - 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 submitReport() async { - if (reportingMessage.value == null) return; - - // 构建举报数据 - final reportData = { - 'describe': reportDescription.value, - // 将Message对象转换为可序列化的Map - 'message': [], - // 同样处理reportContext - 'reportContext': reportingMessage.value!.text, - - 'reporttype': selectedReasons.toString(), - }; - - print('提交举报数据: ${reportData}'); - - //调用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), - ); - } -} diff --git a/lib/modules/agent/views/agent_view.dart b/lib/modules/agent/views/agent_view.dart index a754a1cca..5577c6502 100644 --- a/lib/modules/agent/views/agent_view.dart +++ b/lib/modules/agent/views/agent_view.dart @@ -2,14 +2,10 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../controllers/agent_controller.dart'; -import '../controllers/report_controller.dart'; +import '../controllers/message_action_controller.dart'; import 'message_bubble.dart'; import '../widgets/full_screen_editor.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import '../../../core/theme/app_colors.dart'; -import 'package:haptic_feedback/haptic_feedback.dart'; -import '../../../data/models/message_model.dart'; class AgentView extends StatefulWidget { const AgentView({Key? key}) : super(key: key); @@ -20,12 +16,14 @@ class AgentView extends StatefulWidget { class _AgentViewState extends State with WidgetsBindingObserver { late AgentController controller; + late MessageActionController messageActionController; double _keyboardHeight = 0; @override void initState() { super.initState(); controller = Get.find(); + messageActionController = Get.find(); WidgetsBinding.instance.addObserver(this); } @@ -205,7 +203,7 @@ class _AgentViewState extends State with WidgetsBindingObserver { padding: const EdgeInsets.only(bottom: 8), child: GestureDetector( onLongPressStart: (LongPressStartDetails details) { - _onLongPressMessageContent( + messageActionController.onLongPressMessage( context, isDarkMode, details, message); }, child: MessageBubble( @@ -715,130 +713,11 @@ class _AgentViewState extends State with WidgetsBindingObserver { ); } - void _onLongPressMessageContent(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: [ - _popupMenuEntry( - Icon( - Icons.copy, - size: 20.w, - color: isDarkMode ? Colors.white : Colors.black87, - ), - '复制', - isDarkMode ? Colors.white : Colors.black87, - () { - Clipboard.setData( - ClipboardData( - text: message.text, - ), - ).then((value) => Get.snackbar( - 'success'.tr, // 成功 - '复制成功', - backgroundColor: Colors.blue.withOpacity(0.8), - colorText: Colors.white, - )); - }, - ), - if (!message.isUser) ...[ - // 自定义分隔线样式 - PopupMenuItem( - height: 1, // 控制间距 - enabled: false, - child: Divider( - height: 1, - thickness: 0.8, - indent: 0, - endIndent: 0, - color: isDarkMode ? Colors.grey[700] : Colors.grey[300], - ), - ), - _popupMenuEntry( - Icon( - Icons.report, - size: 20.w, - color: isDarkMode ? Colors.red[700] : Colors.red, - ), - '举报'.tr, // 举报 - isDarkMode ? Colors.red[700]! : Colors.red, - () { - // 实现举报功能 - final reportController = Get.find(); - reportController.initReport(message); - _showReportForm(context, isDarkMode); - }, - ), - ] - ], - ); - } - // 更新_showReportForm方法 - void _showReportForm(BuildContext context, bool isDarkMode) { - 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), - ); - }, - ); - } - 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, - ), - ), - ], - ), - ), - ); - } + // 构建提示选项芯片 Widget _buildPromptChip({ @@ -941,150 +820,7 @@ class _AgentViewState extends State with WidgetsBindingObserver { } } -class _ReportForm extends StatelessWidget { - final bool isDarkMode; - final ReportController reportController = Get.find(); - - _ReportForm({required this.isDarkMode}); - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(16.w), - child: SingleChildScrollView( - child: GetBuilder( - 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( - 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: reportController.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: () => reportController.submitReport(), - ), - SizedBox(height: 8.h), - ], - ), - ), - ), - ); - } - - Widget _buildReportChip( - String label, bool isDarkMode, ReportController controller) { - final isSelected = reportController.selectedReasons.contains(label); - - return GestureDetector( - onTap: () => reportController.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, - ), - ), - ), - ); - } -} // 声音柱动画组件 class _SoundBar extends StatefulWidget {