|
|
|
@ -3,6 +3,10 @@ import 'package:flutter/material.dart'; |
|
|
|
import 'package:get/get.dart'; |
|
|
|
import '../controllers/agent_controller.dart'; |
|
|
|
import 'message_bubble.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'; |
|
|
|
|
|
|
|
class AgentView extends StatelessWidget { |
|
|
|
const AgentView({Key? key}) : super(key: key); |
|
|
|
@ -163,6 +167,13 @@ class AgentView extends StatelessWidget { |
|
|
|
.messages[controller.messages.length - 1 - index]; |
|
|
|
return Padding( |
|
|
|
padding: const EdgeInsets.only(bottom: 8), |
|
|
|
child: !message.isUser |
|
|
|
? GestureDetector( |
|
|
|
onLongPressStart: |
|
|
|
(LongPressStartDetails details) { |
|
|
|
_onLongPressMessageContent( |
|
|
|
context, isDarkMode, details, message); |
|
|
|
}, |
|
|
|
child: MessageBubble( |
|
|
|
isUser: message.isUser, |
|
|
|
message: message.text, |
|
|
|
@ -172,6 +183,16 @@ class AgentView extends StatelessWidget { |
|
|
|
imagePath: message.imagePath, |
|
|
|
meta: message.meta, |
|
|
|
), |
|
|
|
) |
|
|
|
: MessageBubble( |
|
|
|
isUser: message.isUser, |
|
|
|
message: message.text, |
|
|
|
timestamp: message.timestamp, |
|
|
|
isRecognizing: message.isRecognizing, |
|
|
|
hasImage: message.hasImage, |
|
|
|
imagePath: message.imagePath, |
|
|
|
meta: message.meta, |
|
|
|
), |
|
|
|
); |
|
|
|
}, |
|
|
|
), |
|
|
|
@ -522,6 +543,227 @@ class AgentView extends StatelessWidget { |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
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, |
|
|
|
)); |
|
|
|
}, |
|
|
|
), |
|
|
|
// 自定义分隔线样式 |
|
|
|
PopupMenuItem( |
|
|
|
height: 1, // 控制间距 |
|
|
|
enabled: false, |
|
|
|
child: Divider( |
|
|
|
height: 1, |
|
|
|
thickness: 0.8, |
|
|
|
indent: 12, |
|
|
|
endIndent: 12, |
|
|
|
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, |
|
|
|
() { |
|
|
|
// 实现举报功能 |
|
|
|
showModalBottomSheet( |
|
|
|
context: context, |
|
|
|
backgroundColor: |
|
|
|
isDarkMode ? AppColors.cardBackground : Colors.white, |
|
|
|
shape: RoundedRectangleBorder( |
|
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(16.r)), |
|
|
|
), |
|
|
|
builder: (BuildContext context) { |
|
|
|
return Container( |
|
|
|
padding: EdgeInsets.all(16.w), |
|
|
|
child: Column( |
|
|
|
mainAxisSize: MainAxisSize.min, |
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch, |
|
|
|
children: [ |
|
|
|
Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Text( |
|
|
|
'举报', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 18.sp, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
color: isDarkMode ? Colors.white : Colors.black, |
|
|
|
), |
|
|
|
), |
|
|
|
IconButton( |
|
|
|
icon: Icon( |
|
|
|
Icons.close, |
|
|
|
color: |
|
|
|
isDarkMode ? Colors.white70 : Colors.black54, |
|
|
|
), |
|
|
|
onPressed: () => Navigator.pop(context), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
SizedBox(height: 16.h), |
|
|
|
Wrap( |
|
|
|
spacing: 8.w, |
|
|
|
runSpacing: 8.h, |
|
|
|
children: [ |
|
|
|
_buildReportChip('有害/不安全', isDarkMode), |
|
|
|
_buildReportChip('色情低俗', isDarkMode), |
|
|
|
_buildReportChip('误导攻击', isDarkMode), |
|
|
|
_buildReportChip('违法犯罪', isDarkMode), |
|
|
|
_buildReportChip('隐私相关', isDarkMode), |
|
|
|
_buildReportChip('侵犯我的著作权、名誉权等', isDarkMode), |
|
|
|
_buildReportChip('其他', isDarkMode), |
|
|
|
], |
|
|
|
), |
|
|
|
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, |
|
|
|
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: () { |
|
|
|
// 处理举报提交逻辑 |
|
|
|
Navigator.pop(context); |
|
|
|
Get.snackbar( |
|
|
|
'提示', |
|
|
|
'举报已提交,我们会尽快处理', |
|
|
|
backgroundColor: Colors.green.withOpacity(0.8), |
|
|
|
colorText: Colors.white, |
|
|
|
); |
|
|
|
}, |
|
|
|
), |
|
|
|
SizedBox(height: 8.h), |
|
|
|
], |
|
|
|
), |
|
|
|
); |
|
|
|
}, |
|
|
|
); |
|
|
|
}, |
|
|
|
), |
|
|
|
], |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// 添加这个辅助方法来创建举报选项 |
|
|
|
Widget _buildReportChip(String label, bool isDarkMode) { |
|
|
|
return ChoiceChip( |
|
|
|
label: Text( |
|
|
|
label, |
|
|
|
style: TextStyle( |
|
|
|
color: isDarkMode ? Colors.white : Colors.black87, |
|
|
|
fontSize: 14.sp, |
|
|
|
), |
|
|
|
), |
|
|
|
selected: false, |
|
|
|
backgroundColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], |
|
|
|
shape: RoundedRectangleBorder( |
|
|
|
borderRadius: BorderRadius.circular(16.r), |
|
|
|
), |
|
|
|
onSelected: (bool selected) { |
|
|
|
// 可以在这里处理选择逻辑 |
|
|
|
}, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
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({ |
|
|
|
required BuildContext context, |
|
|
|
|