From b0ed28ddb73b0c23052bce4240eb5e31e5c4d681 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Fri, 15 Aug 2025 17:44:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9agent=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E9=83=A8=E4=BB=B6=E9=AB=98=E5=BA=A6=E6=8A=96=E5=8A=A8=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/modules/agent/views/agent_view.dart | 790 ++++++++++++------------ 1 file changed, 383 insertions(+), 407 deletions(-) diff --git a/lib/modules/agent/views/agent_view.dart b/lib/modules/agent/views/agent_view.dart index c78eb62d3..131a1f08c 100644 --- a/lib/modules/agent/views/agent_view.dart +++ b/lib/modules/agent/views/agent_view.dart @@ -339,14 +339,12 @@ class _AgentViewState extends State with WidgetsBindingObserver { ), ), child: Column( - mainAxisSize: MainAxisSize.min, children: [ // 图片预览区域 - 仅在图片输入模式下显示 Obx(() { if (controller.isImageInputActive.value && controller.selectedImagePath.value != null) { return Column( - mainAxisSize: MainAxisSize.min, children: [ // 图片预览和关闭按钮 Stack( @@ -421,454 +419,432 @@ class _AgentViewState extends State with WidgetsBindingObserver { // 输入控件区域 - 包含输入行和展开的选项 Column( children: [ - // 输入控件行 - 使用IntrinsicHeight确保按钮与输入框底部对齐 - IntrinsicHeight( - child: Row( - crossAxisAlignment: - CrossAxisAlignment.end, // 所有元素底部对齐 - children: [ - // 键盘/语音切换按钮 - Obx(() { - final isTextMode = - controller.isTextInputMode.value; - return GestureDetector( - onTap: () { - controller.toggleInputMode(); - }, - child: Container( - width: 44.w, - height: 44.w, - decoration: BoxDecoration( - color: isDarkMode - ? Colors.grey[800] - : Colors.grey[200], - borderRadius: BorderRadius.circular(22), - ), - child: Icon( - isTextMode - ? Icons.mic_none - : Icons.keyboard, - color: isDarkMode - ? Colors.grey[300] - : Colors.grey[700], - size: 22.sp, - ), + Row( + crossAxisAlignment: + CrossAxisAlignment.end, // 所有元素底部对齐 + children: [ + // 键盘/语音切换按钮 + Obx(() { + final isTextMode = + controller.isTextInputMode.value; + return GestureDetector( + onTap: () { + controller.toggleInputMode(); + }, + child: Container( + width: 44.w, + height: 44.w, + margin: EdgeInsets.symmetric(vertical: 2.w), + decoration: BoxDecoration( + color: isDarkMode + ? Colors.grey[800] + : Colors.grey[200], + borderRadius: BorderRadius.circular(22), ), - ); - }), + child: Icon( + isTextMode + ? Icons.mic_none + : Icons.keyboard, + color: isDarkMode + ? Colors.grey[300] + : Colors.grey[700], + size: 22.sp, + ), + ), + ); + }), - SizedBox(width: 8.w), + SizedBox(width: 8.w), - // 文本输入框或语音波形图 - Expanded( - child: Obx(() { - final isTextMode = - controller.isTextInputMode.value; + // 文本输入框或语音波形图 + Expanded( + child: Obx(() { + final isTextMode = + controller.isTextInputMode.value; - if (controller.isTextInputMode.value) { - // 文本输入模式 - 限制最大高度并只向上扩展 - return IntroStepBuilder( - group: 'ai', - order: 1, - overlayBuilder: (params) { - return IntroOverlayBuilder.aiBuilder( - params, - controller.noviceGuideService - .aiStepsList[ + if (controller.isTextInputMode.value) { + // 文本输入模式 - 限制最大高度并只向上扩展 + return IntroStepBuilder( + group: 'ai', + order: 1, + overlayBuilder: (params) { + return IntroOverlayBuilder.aiBuilder( + params, + controller + .noviceGuideService.aiStepsList[ + controller.noviceGuideService + .aiStep]['start'], + buttonText: '确定输入', + onNext: () { + controller.textController.text = controller.noviceGuideService - .aiStep]['start'], - buttonText: '确定输入', - onNext: () { - controller.textController.text = - controller.noviceGuideService - .aiStepsList[ - controller - .noviceGuideService - .aiStep]['input']; + .aiStepsList[ + controller.noviceGuideService + .aiStep]['input']; + }, + ); + }, + onWidgetLoad: () { + controller.noviceGuideService + .startAIGuide(); + }, + builder: + (BuildContext context, GlobalKey key) => + Container( + key: key, + constraints: BoxConstraints( + minHeight: 48.w, + ), + padding: EdgeInsets.symmetric( + horizontal: 16.w, + vertical: 5.w, + ), + alignment: Alignment.center, + decoration: BoxDecoration( + color: isDarkMode + ? Colors.grey[800] + : Colors.grey[200], + borderRadius: + BorderRadius.circular(22.w), + border: Border.all( + color: isDarkMode + ? Colors.grey[800]! + : Colors.grey.withOpacity(0.1), + width: 1.w, + ), + ), + child: Obx(() { + // 图片模式下使用更高的输入框 + final isImageActive = + controller.isImageInputActive.value; + + return TextField( + controller: controller.textController, + maxLines: isImageActive + ? 3 + : 5, // 设置合理的最大行数 + minLines: isImageActive ? 2 : 1, + keyboardType: + TextInputType.multiline, // 启用多行键盘 + textInputAction: + TextInputAction.newline, // 支持回车换行 + onTap: () { + // 当点击输入框时,如果'+'选项已展开,则收缩 + if (controller + .isPlusOptionsExpanded.value) { + controller.collapsePlusOptions(); + } }, + style: TextStyle( + fontSize: 15.sp, + color: isDarkMode + ? Colors.white + : Colors.grey[800], + ), + decoration: InputDecoration( + hintText: isImageActive + ? 'addQuestionAboutImage' + .tr // 添加对图片的问题或描述... + : 'inputMessage'.tr, // 输入消息... + hintStyle: TextStyle( + color: Colors.grey[500], + fontSize: 15.sp, + ), + filled: false, + isDense: true, + border: InputBorder.none, + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: isDarkMode + ? Colors.grey[800]! + : Colors.grey[200]!, + width: 1.w, + ), + ), + contentPadding: EdgeInsets.zero, + ), + onSubmitted: (_) => + controller.sendMessage(), ); - }, - onWidgetLoad: () { - controller.noviceGuideService - .startAIGuide(); - }, - builder: (BuildContext context, - GlobalKey key) => - Container( - key: key, - constraints: BoxConstraints( - minHeight: 44.h, - maxHeight: 120.h, - ), + }), + ), + ); + } else { + Timer? _holdTimer; // 增加延迟触发的计时器 + // 语音输入模式 - 按住说话模式 + return Listener( + onPointerDown: (_) { + // 设置300ms延迟防止误触 + _holdTimer = Timer( + const Duration(milliseconds: 200), + () { + controller.startPushToTalk(); + // if (activeSpeaker == 0) { + // if (speakerId == 1) { + // controller + // .restoreOriginalAudioState(); + // } else { + // controller + // .disableBluetoothAudio(); + // } + // controller + // .setActiveSpeaker(speakerId); + // } + }); + }, + onPointerUp: (_) { + _holdTimer?.cancel(); + controller.endPushToTalk(); + }, + onPointerCancel: (_) { + _holdTimer?.cancel(); + controller.endPushToTalk(); + }, + // onLongPressStart: (_) { + // // 开始按住说话 + // controller.startPushToTalk(); + // }, + // onLongPressEnd: (_) { + // // 结束按住说话 + // controller.endPushToTalk(); + // }, + // onLongPressCancel: () { + // // 取消按住说话 + // controller.endPushToTalk(); + // }, + child: Obx(() { + final isPushToTalkActive = + controller.isPushToTalkActive.value; + final isListening = + controller.isListening.value; + + return Container( + height: 48.w, padding: EdgeInsets.symmetric( horizontal: 16.w), decoration: BoxDecoration( - color: isDarkMode - ? Colors.grey[800] - : Colors.grey[200], + color: isPushToTalkActive + ? (isDarkMode + ? Colors.blue[900] + : Colors.blue[100]) + : (isDarkMode + ? Colors.grey[800] + : Colors.grey[200]), borderRadius: BorderRadius.circular(22.w), border: Border.all( - color: isDarkMode - ? Colors.grey[800]! - : Colors.grey.withOpacity(0.1), - width: 1.w, + color: isPushToTalkActive + ? Colors.blue + : (isDarkMode + ? Colors.grey[800]! + : Colors.grey + .withOpacity(0.1)), + width: + isPushToTalkActive ? 2.w : 1.w, ), ), child: Row( - crossAxisAlignment: CrossAxisAlignment - .end, // 对齐到底部,实现向上扩展 children: [ - Expanded( - child: Obx(() { - // 图片模式下使用更高的输入框 - final isImageActive = controller - .isImageInputActive.value; + // 语音波形图 - 只在按住时显示 + if (isPushToTalkActive) ...[ + const AgentSoundBarsWidget(), + //const Spacer(), + ], - return TextField( - controller: - controller.textController, - maxLines: isImageActive - ? 3 - : 5, // 设置合理的最大行数 - minLines: - isImageActive ? 2 : 1, - keyboardType: TextInputType - .multiline, // 启用多行键盘 - textInputAction: - TextInputAction - .newline, // 支持回车换行 - onTap: () { - // 当点击输入框时,如果'+'选项已展开,则收缩 - if (controller - .isPlusOptionsExpanded - .value) { - controller - .collapsePlusOptions(); - } - }, - style: TextStyle( - fontSize: 15.sp, - color: isDarkMode - ? Colors.white - : Colors.grey[800], + // 状态文字和图标 + Expanded( + child: Row( + mainAxisAlignment: + isPushToTalkActive + ? MainAxisAlignment + .center + : MainAxisAlignment + .center, + children: [ + Icon( + isPushToTalkActive + ? Icons.mic + : Icons.mic_none, + size: 16.sp, + color: isPushToTalkActive + ? Colors.blue + : Colors.grey[500], ), - decoration: InputDecoration( - hintText: isImageActive - ? 'addQuestionAboutImage' - .tr // 添加对图片的问题或描述... - : 'inputMessage' - .tr, // 输入消息... - hintStyle: TextStyle( - color: Colors.grey[500], - fontSize: 15.sp, - ), - filled: false, - border: InputBorder.none, - focusedBorder: - OutlineInputBorder( - borderSide: BorderSide( - color: isDarkMode - ? Colors.grey[800]! - : Colors.grey[200]!, - width: 1.w, - ), + const SizedBox(width: 6), + Text( + isPushToTalkActive + ? 'recording' + .tr // 正在录音... + : 'holdToSpeak' + .tr, // 按住说话 + style: TextStyle( + fontSize: 14, + fontWeight: + FontWeight.w500, + color: isPushToTalkActive + ? Colors.blue + : Colors.grey[600], ), - contentPadding: - EdgeInsets.symmetric( - vertical: - isImageActive - ? 12.h - : 8.h), ), - onSubmitted: (_) => - controller.sendMessage(), - ); - }), + ], + ), ), ], ), - ), - ); - } else { - Timer? _holdTimer; // 增加延迟触发的计时器 - // 语音输入模式 - 按住说话模式 - return Listener( - onPointerDown: (_) { - // 设置300ms延迟防止误触 - _holdTimer = Timer( - const Duration(milliseconds: 200), - () { - controller.startPushToTalk(); - // if (activeSpeaker == 0) { - // if (speakerId == 1) { - // controller - // .restoreOriginalAudioState(); - // } else { - // controller - // .disableBluetoothAudio(); - // } - // controller - // .setActiveSpeaker(speakerId); - // } - }); - }, - onPointerUp: (_) { - _holdTimer?.cancel(); - controller.endPushToTalk(); - }, - onPointerCancel: (_) { - _holdTimer?.cancel(); - controller.endPushToTalk(); - }, - // onLongPressStart: (_) { - // // 开始按住说话 - // controller.startPushToTalk(); - // }, - // onLongPressEnd: (_) { - // // 结束按住说话 - // controller.endPushToTalk(); - // }, - // onLongPressCancel: () { - // // 取消按住说话 - // controller.endPushToTalk(); - // }, - child: Obx(() { - final isPushToTalkActive = - controller.isPushToTalkActive.value; - final isListening = - controller.isListening.value; - - return Container( - height: 52.h, - padding: EdgeInsets.symmetric( - horizontal: 16.w), - decoration: BoxDecoration( - color: isPushToTalkActive - ? (isDarkMode - ? Colors.blue[900] - : Colors.blue[100]) - : (isDarkMode - ? Colors.grey[800] - : Colors.grey[200]), - borderRadius: - BorderRadius.circular(22.w), - border: Border.all( - color: isPushToTalkActive - ? Colors.blue - : (isDarkMode - ? Colors.grey[800]! - : Colors.grey - .withOpacity(0.1)), - width: isPushToTalkActive - ? 2.w - : 1.w, - ), - ), - child: Row( - children: [ - // 语音波形图 - 只在按住时显示 - if (isPushToTalkActive) ...[ - const AgentSoundBarsWidget(), - //const Spacer(), - ], - - // 状态文字和图标 - Expanded( - child: Row( - mainAxisAlignment: - isPushToTalkActive - ? MainAxisAlignment - .center - : MainAxisAlignment - .center, - children: [ - Icon( - isPushToTalkActive - ? Icons.mic - : Icons.mic_none, - size: 16.sp, - color: isPushToTalkActive - ? Colors.blue - : Colors.grey[500], - ), - const SizedBox(width: 6), - Text( - isPushToTalkActive - ? 'recording' - .tr // 正在录音... - : 'holdToSpeak' - .tr, // 按住说话 - style: TextStyle( - fontSize: 14, - fontWeight: - FontWeight.w500, - color: - isPushToTalkActive - ? Colors.blue - : Colors - .grey[600], - ), - ), - ], - ), - ), - ], - ), - ); - }), - ); - } - }), - ), + ); + }), + ); + } + }), + ), - SizedBox(width: 8.w), + SizedBox(width: 8.w), - // 动态按钮区域:展开按钮 + 发送按钮 或 图片选择按钮 - Builder(builder: (context) { - StepWidgetParams? widgetParams; - return IntroStepBuilder( - group: 'ai', - order: 2, - onHighlightWidgetTap: () async { - await controller - .noviceGuideService.aiStepsList[ - controller.noviceGuideService - .aiStep]['method'](); - widgetParams!.onNext!(); - controller.sendMessage(); - }, - overlayBuilder: (params) { - widgetParams = params; - return IntroOverlayBuilder.aiBuilder( - params, - '点击发送,${controller.noviceGuideService.aiStepsList[controller.noviceGuideService.aiStep]['send']}', - alignment: false, - ); - }, - builder: - (BuildContext context, GlobalKey key) => - Obx( - key: key, - () { - final hasText = - controller.hasTextInput.value; - final hasImage = - controller.isImageInputActive.value; - final shouldShowSend = - hasText || hasImage; - final shouldShowExpand = controller - .shouldShowExpandButton.value; + // 动态按钮区域:展开按钮 + 发送按钮 或 图片选择按钮 + Builder(builder: (context) { + StepWidgetParams? widgetParams; + return IntroStepBuilder( + group: 'ai', + order: 2, + onHighlightWidgetTap: () async { + await controller + .noviceGuideService.aiStepsList[ + controller.noviceGuideService.aiStep] + ['method'](); + widgetParams!.onNext!(); + controller.sendMessage(); + }, + overlayBuilder: (params) { + widgetParams = params; + return IntroOverlayBuilder.aiBuilder( + params, + '点击发送,${controller.noviceGuideService.aiStepsList[controller.noviceGuideService.aiStep]['send']}', + alignment: false, + ); + }, + builder: + (BuildContext context, GlobalKey key) => + Obx( + key: key, + () { + final hasText = + controller.hasTextInput.value; + final hasImage = + controller.isImageInputActive.value; + final shouldShowSend = hasText || hasImage; + final shouldShowExpand = + controller.shouldShowExpandButton.value; - if (shouldShowSend) { - // 显示发送按钮(可能还有展开按钮) - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - // 展开按钮(在发送按钮上方) - if (shouldShowExpand && hasText) - Padding( - padding: EdgeInsets.only( - bottom: 40.h), - child: GestureDetector( - onTap: () { - // 打开底部弹出编辑模式 + if (shouldShowSend) { + // 显示发送按钮(可能还有展开按钮) + return Column( + children: [ + // 展开按钮(在发送按钮上方) + if (shouldShowExpand && hasText) + Padding( + padding: + EdgeInsets.only(bottom: 40.h), + child: GestureDetector( + onTap: () { + // 打开底部弹出编辑模式 + controller + .openFullScreenEdit(); + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: + Colors.transparent, + builder: (context) => + const FullScreenEditor(), + ).then((_) { + // 弹出页关闭时的回调 controller - .openFullScreenEdit(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: - Colors.transparent, - builder: (context) => - const FullScreenEditor(), - ).then((_) { - // 弹出页关闭时的回调 - controller - .closeFullScreenEdit(); - }); - }, - child: Container( - width: 32.w, - height: 32.w, - decoration: BoxDecoration( - color: isDarkMode - ? Colors.grey[700] - : Colors.grey[300], - borderRadius: - BorderRadius.circular( - 16.w), - ), - child: Icon( - Icons.expand_less, - color: isDarkMode - ? Colors.grey[300] - : Colors.grey[700], - size: 20.sp, - ), + .closeFullScreenEdit(); + }); + }, + child: Container( + width: 32.w, + height: 32.w, + decoration: BoxDecoration( + color: isDarkMode + ? Colors.grey[700] + : Colors.grey[300], + borderRadius: + BorderRadius.circular( + 16.w), + ), + child: Icon( + Icons.expand_less, + color: isDarkMode + ? Colors.grey[300] + : Colors.grey[700], + size: 20.sp, ), - ), - ), - // 发送按钮 - GestureDetector( - onTap: () => - controller.sendMessage(), - child: Container( - width: 44.w, - height: 44.w, - decoration: BoxDecoration( - color: primaryColor, - borderRadius: - BorderRadius.circular( - 22.w), - ), - child: Icon( - Icons.send, - color: Colors.white, - size: 22.sp, ), ), ), - ], - ); - } else { - // 显示'+'选择按钮 - return Obx(() { - final isExpanded = controller - .isPlusOptionsExpanded.value; - return GestureDetector( - onTap: () { - controller.togglePlusOptions(); - }, + // 发送按钮 + GestureDetector( + onTap: () => + controller.sendMessage(), child: Container( width: 44.w, height: 44.w, + margin: EdgeInsets.symmetric( + vertical: 2.w), decoration: BoxDecoration( - color: isDarkMode - ? Colors.grey[800] - : Colors.grey[200], + color: primaryColor, borderRadius: BorderRadius.circular(22.w), ), child: Icon( - isExpanded - ? Icons.close - : Icons.add, - color: isDarkMode - ? Colors.grey[300] - : Colors.grey[700], + Icons.send, + color: Colors.white, size: 22.sp, ), ), - ); - }); - } - }, - ), - ); - }), - ], - ), + ), + ], + ); + } else { + // 显示'+'选择按钮 + return Obx(() { + final isExpanded = controller + .isPlusOptionsExpanded.value; + return GestureDetector( + onTap: () { + controller.togglePlusOptions(); + }, + child: Container( + width: 44.w, + height: 44.w, + margin: EdgeInsets.symmetric( + vertical: 2.w), + decoration: BoxDecoration( + color: isDarkMode + ? Colors.grey[800] + : Colors.grey[200], + borderRadius: + BorderRadius.circular(22.w), + ), + child: Icon( + isExpanded + ? Icons.close + : Icons.add, + color: isDarkMode + ? Colors.grey[300] + : Colors.grey[700], + size: 22.sp, + ), + ), + ); + }); + } + }, + ), + ); + }), + ], ), // 展开的'+'选择选项