Pārlūkot izejas kodu

[feat]亲密度分析,截图回复-识图回复Tab,增加底部的评论模式切换按钮、获取回复按钮

hezihao 7 mēneši atpakaļ
vecāks
revīzija
11af9a9fdc

+ 1 - 0
assets/string/base/string.xml

@@ -285,6 +285,7 @@
     <string name="intimacy_analyse_look_analyse">查看分析</string>
     <string name="intimacy_analyse_ta_demand">TA现在的需求</string>
     <string name="intimacy_analyse_re_upload">重新上传</string>
+    <string name="intimacy_analyse_get_reply">获取回复</string>
 
     <string name="next_step">下一步</string>
     <string name="recently">最近</string>

+ 1 - 0
lib/module/intimacy_analyse/intimacy_analyse_upload/intimacy_analyse_upload_page.dart

@@ -543,6 +543,7 @@ class IntimacyAnalyseUploadPage
         ),
         child: ActionBtn(
           leftBtn: _buildAiModelSwitchBtn(),
+          spaceWidget: SizedBox(width: 10.w),
           rightBtn: _buildNextBtn(),
         ),
       );

+ 1 - 0
lib/module/intimacy_analyse/intimacy_analyse_upload/popup/ai_model_select_popup.dart

@@ -157,6 +157,7 @@ class AiModelSelectPopup {
         SmartDialog.dismiss();
       },
       child: Container(
+        width: 128.w,
         alignment: Alignment.center,
         child: Container(
           margin: EdgeInsets.only(left: 6.w, right: 6.w, bottom: 10.h),

+ 221 - 0
lib/module/intimacy_analyse/intimacy_analyse_upload/popup/reply_mode_select_popup.dart

@@ -0,0 +1,221 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:keyboard/resource/colors.gen.dart';
+
+import '../../../../resource/assets.gen.dart';
+import '../../../../widget/gradient_text.dart';
+
+/// 选择新的Ai模型时回调
+typedef OnChooseReplyModeCallback = void Function(String newMode);
+
+/// 回复模式选择,Popup锚点弹窗
+class ReplyModeSelectPopup {
+  /// 用于标识弹窗的唯一性
+  static const _tag = "ReplyModeSelectPopup";
+
+  /// 弹窗是否显示中
+  static bool _isShowing = false;
+
+  /// 弹窗是否显示中
+  static bool isShowing() {
+    return _isShowing;
+  }
+
+  /// 显示
+  /// [anchorBtnKey] 锚点按钮的GlobalKey
+  /// [modeList] 模型列表
+  /// [currentMode] 当前使用的回复模式
+  /// [onChooseReplyModeCallback] 选择新的Ai模型时回调
+  static void show(
+    GlobalKey anchorBtnKey,
+    BuildContext context, {
+    required List<String> modeList,
+    required String currentMode,
+    required OnChooseReplyModeCallback onChooseReplyModeCallback,
+  }) {
+    if (_isShowing) {
+      return;
+    }
+
+    double popupWidth = 145.w;
+
+    SmartDialog.showAttach(
+      // 绑定唯一tag
+      tag: _tag,
+      // 绑定锚点的BuildContext,才能确定弹窗的显示位置
+      targetContext: context,
+      // 在锚点的顶部显示
+      alignment: _calculateBestAlignment(anchorBtnKey, context),
+      // 使用动画
+      useAnimation: true,
+      // 动画类型
+      animationType: SmartAnimationType.fade,
+      // 是否允许事件穿透
+      usePenetrate: true,
+      // 监听弹窗关闭
+      onDismiss: () {
+        _isShowing = false;
+      },
+      // 位置偏移配置
+      targetBuilder: (Offset targetOffset, Size targetSize) {
+        // 获取锚点信息
+        final anchorRenderBox =
+            anchorBtnKey.currentContext!.findRenderObject() as RenderBox;
+        final anchorPosition = anchorRenderBox.localToGlobal(Offset.zero);
+        final anchorSize = anchorRenderBox.size;
+
+        // 计算X轴的偏移量,距离屏幕左边有一段距离
+        double leftOffset = 6.w;
+        double popupX =
+            anchorPosition.dx +
+            (anchorSize.width / 2) -
+            (popupWidth / 2) +
+            leftOffset;
+
+        // 计算Y轴的偏移量(默认是在按钮的上边显示,UI设计要求盖着按钮,所以需要增加偏移量)
+        double bottomOffset = 46.h;
+        double popupY = anchorPosition.dy + bottomOffset;
+
+        return Offset(popupX, popupY);
+      },
+      // 构建弹窗内容
+      builder: (_) {
+        return Container(
+          // 固定弹窗的宽度
+          width: popupWidth,
+          decoration: BoxDecoration(
+            color: ColorName.white,
+            border: Border.all(
+              color: ColorName.bgAiModelSwitchBorderBtn,
+              width: 1.0.w,
+            ),
+            borderRadius: BorderRadius.all(Radius.circular(16.r)),
+          ),
+          child: Column(
+            // 包裹内容
+            mainAxisSize: MainAxisSize.min,
+            children: [
+              SizedBox(height: 12.h),
+              // Ai模型列表
+              Flexible(
+                child: ListView.builder(
+                  // ListView包裹内容
+                  shrinkWrap: true,
+                  physics: const ClampingScrollPhysics(),
+                  // 去除默认padding
+                  padding: EdgeInsets.zero,
+                  itemCount: modeList.length,
+                  itemBuilder: (BuildContext context, int index) {
+                    String itemData = modeList[index];
+                    return _buildListItem(
+                      index,
+                      itemData,
+                      currentMode == itemData,
+                      onChooseReplyModeCallback,
+                    );
+                  },
+                ),
+              ),
+              // 当前选中项
+              _buildCurrentSelectItem(currentMode),
+            ],
+          ),
+        );
+      },
+    );
+
+    // 设置为正在显示中
+    _isShowing = true;
+  }
+
+  /// 计算对齐方式
+  static Alignment _calculateBestAlignment(
+    GlobalKey anchorBtnKey,
+    BuildContext context,
+  ) {
+    final renderBox =
+        anchorBtnKey.currentContext?.findRenderObject() as RenderBox;
+    final position = renderBox.localToGlobal(Offset.zero);
+    final screenHeight = MediaQuery.of(context).size.height;
+
+    return position.dy > screenHeight / 2
+        ? Alignment.topCenter
+        : Alignment.bottomCenter;
+  }
+
+  /// 列表项
+  static Widget _buildListItem(
+    int index,
+    String itemData,
+    bool isSelected,
+    OnChooseReplyModeCallback onChooseReplyModeCallback,
+  ) {
+    return GestureDetector(
+      onTap: () {
+        onChooseReplyModeCallback(itemData);
+        SmartDialog.dismiss();
+      },
+      child: Container(
+        alignment: Alignment.center,
+        child: Container(
+          width: 128.w,
+          margin: EdgeInsets.only(left: 6.w, right: 6.w, bottom: 10.h),
+          padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 35.w),
+          decoration: BoxDecoration(
+            borderRadius: BorderRadius.circular(7.r),
+            color: isSelected ? ColorName.bgAiModelSelected : ColorName.white,
+          ),
+          child: Text(
+            itemData,
+            style: TextStyle(
+              color: ColorName.black80,
+              fontSize: 14.sp,
+              fontWeight: isSelected ? FontWeight.w500 : FontWeight.w400,
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 当前选中项
+  static Widget _buildCurrentSelectItem(String currentSelect) {
+    return GestureDetector(
+      onTap: () {
+        dismiss();
+      },
+      child: Container(
+        padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 14.w),
+        child: Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            // 文字
+            GradientText(
+              colors: [
+                ColorName.aiModelSwitchBtnColor1,
+                ColorName.aiModelSwitchBtnColor2,
+              ],
+              child: Text(
+                currentSelect,
+                style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w500),
+              ),
+            ),
+            SizedBox(width: 8.w),
+            // 箭头
+            Assets.images.iconModeSwitchArrow.image(width: 15.w, height: 15.w),
+          ],
+        ),
+      ),
+    );
+  }
+
+  /// 隐藏
+  static void dismiss() {
+    if (!_isShowing) {
+      return;
+    }
+    SmartDialog.dismiss(tag: _tag);
+  }
+}

+ 23 - 4
lib/module/intimacy_analyse/screenshot_reply/scan_image_reply/scan_image_reply_controller.dart

@@ -12,6 +12,16 @@ class ScanImageReplyController extends BaseController {
   RxList<OptionSelectConfig> replyToneOptionSelectConfigList =
       <OptionSelectConfig>[].obs;
 
+  /// 是否上传页
+  RxBool isUploadPage = false.obs;
+
+  /// Ai模型列表
+  RxList<String> replyModelList =
+      <String>['评论模式', '搭讪模式'].obs;
+
+  /// 当前应用的Ai模型
+  Rx<String> currentReplyMode = '评论模式'.obs;
+
   @override
   void onReady() {
     super.onReady();
@@ -36,9 +46,9 @@ class ScanImageReplyController extends BaseController {
 
   /// 选中回复语气的选项
   void selectReplyToneOption(
-      OptionSelectConfig rowConfig,
-      OptionSelectItem optionItem,
-      ) {
+    OptionSelectConfig rowConfig,
+    OptionSelectItem optionItem,
+  ) {
     // 先全部反选
     rowConfig.options =
         rowConfig.options.map((ele) {
@@ -51,7 +61,16 @@ class ScanImageReplyController extends BaseController {
     replyToneOptionSelectConfigList.refresh();
   }
 
+  /// 切换回复模型
+  void switchReplyMode(String newMode) {
+    currentReplyMode.value = newMode;
+  }
+
   /// 点击上传截图按钮
   void clickUploadScreenshotBtn() {
+    isUploadPage.value = true;
   }
-}
+
+  /// 点击获取回复按钮
+  void clickGetReplyBtn() {}
+}

+ 153 - 10
lib/module/intimacy_analyse/screenshot_reply/scan_image_reply/scan_image_reply_view.dart

@@ -8,6 +8,9 @@ import '../../../../data/bean/option_select_item.dart';
 import '../../../../resource/assets.gen.dart';
 import '../../../../resource/colors.gen.dart';
 import '../../../../resource/string.gen.dart';
+import '../../../../widget/actionbtn/action_btn.dart';
+import '../../../../widget/gradient_text.dart';
+import '../../intimacy_analyse_upload/popup/reply_mode_select_popup.dart';
 import '../../intimacy_analyse_upload/widget/upload_item_widget.dart';
 import '../../widget/option_select_widget.dart';
 import '../../widget/step_card.dart';
@@ -15,7 +18,10 @@ import 'package:keyboard/module/intimacy_analyse/screenshot_reply/scan_image_rep
 
 /// 识图回复Tab页
 class ScanImageReplyView extends BaseView<ScanImageReplyController> {
-  const ScanImageReplyView({super.key});
+  ScanImageReplyView({super.key});
+
+  /// 回复模式,切换按钮的GlobalKey
+  final GlobalKey _replyModeSwitchBtnAnchorKey = GlobalKey();
 
   @override
   Color backgroundColor() {
@@ -273,16 +279,153 @@ class ScanImageReplyView extends BaseView<ScanImageReplyController> {
     );
   }
 
+  /// 获取回复按钮
+  Widget _buildGetReplayBtn() {
+    return GestureDetector(
+      onTap: () {
+        controller.clickGetReplyBtn();
+      },
+      child: Container(
+        padding: EdgeInsets.symmetric(vertical: 14.h),
+        width: double.maxFinite,
+        decoration: ShapeDecoration(
+          // 渐变背景
+          gradient: LinearGradient(
+            colors: [ColorName.purpleGradient3, ColorName.purpleGradient4],
+            begin: Alignment.centerLeft,
+            end: Alignment.centerRight,
+          ),
+          shape: RoundedRectangleBorder(
+            borderRadius: BorderRadius.circular(50.r),
+          ),
+        ),
+        child: Center(
+          child: Text(
+            StringName.intimacyAnalyseGetReply,
+            style: TextStyle(
+              color: ColorName.white,
+              fontSize: 16.sp,
+              fontWeight: FontWeight.w500,
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// Ai模型切换按钮
+  Widget _buildReplyModelSwitchBtn() {
+    return Builder(
+      builder: (context) {
+        return GestureDetector(
+          key: _replyModeSwitchBtnAnchorKey,
+          onTap: () {
+            // 显示回复切换的Popup弹窗
+            if (ReplyModeSelectPopup.isShowing()) {
+              ReplyModeSelectPopup.dismiss();
+              return;
+            }
+            // 切换回复模式
+            ReplyModeSelectPopup.show(
+              _replyModeSwitchBtnAnchorKey,
+              context,
+              modeList: controller.replyModelList.toList(),
+              currentMode: controller.currentReplyMode.value,
+              onChooseReplyModeCallback: (String newMode) {
+                controller.switchReplyMode(newMode);
+              },
+            );
+          },
+          child: Container(
+            padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 14.w),
+            decoration: BoxDecoration(
+              color: ColorName.white,
+              borderRadius: BorderRadius.all(Radius.circular(30.w)),
+              border: Border.all(
+                color: ColorName.bgAiModelSwitchBorderBtn,
+                width: 1.w,
+              ),
+            ),
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.center,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                Obx(() {
+                  return
+                  // 文字
+                  GradientText(
+                    colors: [
+                      ColorName.aiModelSwitchBtnColor1,
+                      ColorName.aiModelSwitchBtnColor2,
+                    ],
+                    child: Text(
+                      controller.currentReplyMode.value,
+                      style: TextStyle(
+                        fontSize: 14.sp,
+                        fontWeight: FontWeight.w500,
+                      ),
+                    ),
+                  );
+                }),
+                SizedBox(width: 8.w),
+                // 箭头
+                Assets.images.iconModeSwitchArrow.image(
+                  width: 15.w,
+                  height: 15.w,
+                ),
+              ],
+            ),
+          ),
+        );
+      },
+    );
+  }
+
+  /// 底部操作按钮
+  Widget _buildBottomActionBtn() {
+    if (controller.isUploadPage.value) {
+      // 上传页,显示上传截图按钮
+      return Container(
+        width: double.maxFinite,
+        margin: EdgeInsets.only(
+          left: 16.w,
+          top: 8.h,
+          right: 16.w,
+          bottom: 20.h,
+        ),
+        child: _buildUploadScreenshotBtn(),
+      );
+    } else {
+      // 结果页,显示2个按钮,模式切换按钮和获取回复按钮
+      return Container(
+        width: double.maxFinite,
+        margin: EdgeInsets.only(
+          left: 16.w,
+          top: 8.h,
+          right: 16.w,
+          bottom: 20.h,
+        ),
+        child: ActionBtn(
+          leftBtn: _buildReplyModelSwitchBtn(),
+          spaceWidget: SizedBox(width: 10.w,),
+          rightBtn: _buildGetReplayBtn(),
+        ),
+      );
+    }
+  }
+
   /// 底部布局
   Widget _buildBottomLayout(BuildContext context) {
-    return Column(
-      // 高度包裹内容
-      mainAxisSize: MainAxisSize.min,
-      crossAxisAlignment: CrossAxisAlignment.start,
-      children: [
-        // 上传截图按钮
-        _buildUploadScreenshotBtn(), SizedBox(height: 20.h),
-      ],
-    );
+    return Obx(() {
+      return Column(
+        // 高度包裹内容
+        mainAxisSize: MainAxisSize.min,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          // 操作按钮
+          _buildBottomActionBtn(),
+        ],
+      );
+    });
   }
 }

+ 2 - 0
lib/resource/string.gen.dart

@@ -201,6 +201,7 @@ class StringName {
   static final String intimacyAnalyseLookAnalyse = 'intimacy_analyse_look_analyse'.tr; // 查看分析
   static final String intimacyAnalyseTaDemand = 'intimacy_analyse_ta_demand'.tr; // TA现在的需求
   static final String intimacyAnalyseReUpload = 'intimacy_analyse_re_upload'.tr; // 重新上传
+  static final String intimacyAnalyseGetReply = 'intimacy_analyse_get_reply'.tr; // 获取回复
   static final String nextStep = 'next_step'.tr; // 下一步
   static final String recently = 'recently'.tr; // 最近
   static final String addHobbies = 'add_hobbies'.tr; // 添加爱好
@@ -409,6 +410,7 @@ class StringMultiSource {
       'intimacy_analyse_look_analyse': '查看分析',
       'intimacy_analyse_ta_demand': 'TA现在的需求',
       'intimacy_analyse_re_upload': '重新上传',
+      'intimacy_analyse_get_reply': '获取回复',
       'next_step': '下一步',
       'recently': '最近',
       'add_hobbies': '添加爱好',

+ 10 - 4
lib/widget/actionbtn/action_btn.dart

@@ -6,15 +6,21 @@ class ActionBtn extends StatelessWidget {
   /// 左侧按钮
   final Widget leftBtn;
 
+  /// 按钮之间的间隙组件
+  final Widget spaceWidget;
+
   /// 右侧按钮
   final Widget rightBtn;
 
-  const ActionBtn({super.key, required this.leftBtn, required this.rightBtn});
+  const ActionBtn({
+    super.key,
+    required this.leftBtn,
+    required this.spaceWidget,
+    required this.rightBtn,
+  });
 
   @override
   Widget build(BuildContext context) {
-    return Row(
-      children: [leftBtn, SizedBox(width: 10.w), Expanded(child: rightBtn)],
-    );
+    return Row(children: [leftBtn, spaceWidget, Expanded(child: rightBtn)]);
   }
 }