Prechádzať zdrojové kódy

[feat]亲密度分析,截图回复-识图回复Tab,增加选择回复的语气的选择卡片

hezihao 7 mesiacov pred
rodič
commit
d141af3918

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

@@ -284,6 +284,7 @@
     <string name="intimacy_analyse_sample_image">示例图</string>
     <string name="intimacy_analyse_look_analyse">查看分析</string>
     <string name="intimacy_analyse_ta_demand">TA现在的需求</string>
+    <string name="intimacy_analyse_re_upload">重新上传</string>
 
     <string name="next_step">下一步</string>
     <string name="recently">最近</string>

+ 73 - 33
lib/module/intimacy_analyse/intimacy_analyse_upload/widget/upload_item_widget.dart

@@ -33,21 +33,29 @@ class UploadItemWidget extends StatelessWidget {
   /// 图片路径
   final String imageSrc;
 
+  /// 是否有删除按钮
+  final bool hasDeleteBtn;
+
   /// 上传状态
   final UploadState uploadState;
 
+  /// 上传失败时的遮罩组件
+  final Widget? uploadFailMaskWidget;
+
   /// 点击条目时回调
   final OnClickItemCallback onClickItemCallback;
 
   /// 点击删除时回调
-  final OnClickDeleteCallback onClickDeleteCallback;
+  final OnClickDeleteCallback? onClickDeleteCallback;
 
   const UploadItemWidget({
     super.key,
     required this.imageSrc,
+    this.hasDeleteBtn = false,
     required this.uploadState,
+    this.uploadFailMaskWidget,
     required this.onClickItemCallback,
-    required this.onClickDeleteCallback,
+    this.onClickDeleteCallback,
   });
 
   @override
@@ -64,7 +72,10 @@ class UploadItemWidget extends StatelessWidget {
             // 上传状态的遮罩层
             _buildMaskByUploadStatus(uploadState),
             // 删除按钮
-            Positioned(right: 3.w, top: 3.w, child: _buildDeleteBtn()),
+            Visibility(
+              visible: hasDeleteBtn,
+              child: Positioned(right: 3.w, top: 3.w, child: _buildDeleteBtn()),
+            ),
           ],
         ),
       ),
@@ -98,15 +109,38 @@ class UploadItemWidget extends StatelessWidget {
         return SizedBox();
       case UploadState.uploading:
         // 上传中
-        return _buildUploadingMask();
+        return UploadingMaskWidget();
       case UploadState.fail:
         // 上传失败
-        return _buildUploadFailMask();
+        return uploadFailMaskWidget != null
+            ? uploadFailMaskWidget!
+            : UploadFailMaskWidget();
     }
   }
 
-  /// 上传中状态的遮罩
-  Widget _buildUploadingMask() {
+  /// 删除按钮
+  Widget _buildDeleteBtn() {
+    return GestureDetector(
+      onTap: () {
+        // 执行删除
+        if (onClickDeleteCallback != null) {
+          onClickDeleteCallback!();
+        }
+      },
+      child: Container(
+        padding: EdgeInsets.all(3.w),
+        child: Assets.images.iconUploadDelete.image(height: 20.w, width: 20.w),
+      ),
+    );
+  }
+}
+
+/// 上传中状态的遮罩
+class UploadingMaskWidget extends StatelessWidget {
+  const UploadingMaskWidget({super.key});
+
+  @override
+  Widget build(BuildContext context) {
     return Container(
       // 宽度和高度,都撑满父组件
       width: double.maxFinite,
@@ -140,9 +174,25 @@ class UploadItemWidget extends StatelessWidget {
       ),
     );
   }
+}
+
+/// 上传失败状态的遮罩
+class UploadFailMaskWidget extends StatelessWidget {
+  final Widget? iconWidget;
+
+  final Widget? spaceWidget;
+
+  final Widget? textWidget;
+
+  const UploadFailMaskWidget({
+    super.key,
+    this.iconWidget,
+    this.spaceWidget,
+    this.textWidget,
+  });
 
-  /// 上传失败状态的遮罩
-  Widget _buildUploadFailMask() {
+  @override
+  Widget build(BuildContext context) {
     return Container(
       // 宽度和高度,都撑满父组件
       width: double.maxFinite,
@@ -158,34 +208,24 @@ class UploadItemWidget extends StatelessWidget {
         crossAxisAlignment: CrossAxisAlignment.center,
         children: [
           // 图标
-          Assets.images.iconUploadFail.image(width: 26.w, height: 26.w),
+          iconWidget != null
+              ? iconWidget!
+              : Assets.images.iconUploadFail.image(width: 26.w, height: 26.w),
           // 间距
-          SizedBox(height: 6.h),
+          spaceWidget != null ? spaceWidget! : SizedBox(height: 6.h),
           // 文字
-          Text(
-            StringName.intimacyAnalyseUploadFail,
-            style: TextStyle(
-              color: ColorName.white,
-              fontSize: 12.sp,
-              fontWeight: FontWeight.w700,
-            ),
-          ),
+          textWidget != null
+              ? textWidget!
+              : Text(
+                StringName.intimacyAnalyseUploadFail,
+                style: TextStyle(
+                  color: ColorName.white,
+                  fontSize: 12.sp,
+                  fontWeight: FontWeight.w700,
+                ),
+              ),
         ],
       ),
     );
   }
-
-  /// 删除按钮
-  Widget _buildDeleteBtn() {
-    return GestureDetector(
-      onTap: () {
-        // 执行删除
-        onClickDeleteCallback();
-      },
-      child: Container(
-        padding: EdgeInsets.all(3.w),
-        child: Assets.images.iconUploadDelete.image(height: 20.w, width: 20.w),
-      ),
-    );
-  }
 }

+ 1 - 0
lib/module/intimacy_analyse/intimacy_analyse_upload/widget/upload_nine_grid.dart

@@ -95,6 +95,7 @@ class UploadNineGrid extends StatelessWidget {
   Widget _buildUploadItem(String imageSrc) {
     return UploadItemWidget(
       imageSrc: imageSrc,
+      hasDeleteBtn: true,
       uploadState: UploadState.fail,
       onClickDeleteCallback: () {
         // 删除图片

+ 4 - 3
lib/module/intimacy_analyse/screenshot_reply/conversation_analysis/conversation_analysis_controller.dart

@@ -15,7 +15,7 @@ class ConversationAnalysisController extends BaseController {
   Rx<bool> isUploadPage = false.obs;
 
   /// 报告是否已经生成
-  Rx<bool> hasReport = true.obs;
+  Rx<bool> hasReport = false.obs;
 
   /// 是否已解锁
   Rx<bool> isUnlock = false.obs;
@@ -48,10 +48,11 @@ class ConversationAnalysisController extends BaseController {
   @override
   void onReady() {
     super.onReady();
-    _initDirectionOptionSelectConfigList();
+    _initOptionSelectConfigList();
   }
 
-  void _initDirectionOptionSelectConfigList() {
+  /// 初始化选项列表
+  void _initOptionSelectConfigList() {
     optionSelectConfigList.add(
       OptionSelectConfig("对于TA", "", [
         OptionSelectItem("现在的需求", false),

+ 24 - 4
lib/module/intimacy_analyse/screenshot_reply/conversation_analysis/conversation_analysis_view.dart

@@ -95,12 +95,32 @@ class ConversationAnalysisView
 
   /// 选项卡片
   Widget _buildOptionCard() {
-    return StepCard(
-      hasStep: false,
-      contentWidget: Column(
+    return Container(
+      width: double.maxFinite,
+      margin: EdgeInsets.only(left: 14.w, right: 14.w),
+      // 圆角背景
+      decoration: BoxDecoration(
+        gradient: LinearGradient(
+          colors: [
+            Color(0xFFEFE9FF),
+            Color(0xFFFBFAFF),
+          ],
+          begin: Alignment.topCenter,
+          end: Alignment.bottomRight,
+        ),
+        shape: BoxShape.rectangle,
+        border: Border.all(color: ColorName.white80, width: 1.w),
+        borderRadius: BorderRadius.all(Radius.circular(20.r)),
+      ),
+      child: Column(
         children: [
           Container(
-            margin: EdgeInsets.only(left: 12.w, top: 17.h, right: 12.w),
+            margin: EdgeInsets.only(
+              left: 12.w,
+              top: 17.h,
+              right: 12.w,
+              bottom: 12.h,
+            ),
             padding: EdgeInsets.only(
               left: 12.w,
               top: 12.h,

+ 47 - 0
lib/module/intimacy_analyse/screenshot_reply/scan_image_reply/scan_image_reply_controller.dart

@@ -1,9 +1,56 @@
+import 'package:get/get_rx/src/rx_types/rx_types.dart';
 import 'package:injectable/injectable.dart';
 import 'package:keyboard/base/base_controller.dart';
 
+import '../../../../data/bean/option_select_config.dart';
+import '../../../../data/bean/option_select_item.dart';
+
 /// 识图回复Controller
 @injectable
 class ScanImageReplyController extends BaseController {
+  /// 回复语气选项列表
+  RxList<OptionSelectConfig> replyToneOptionSelectConfigList =
+      <OptionSelectConfig>[].obs;
+
+  @override
+  void onReady() {
+    super.onReady();
+    _initReplyToneOptionSelectConfigList();
+  }
+
+  /// 回复语气列表
+  void _initReplyToneOptionSelectConfigList() {
+    replyToneOptionSelectConfigList.add(
+      OptionSelectConfig("选择回复语气", "", [
+        OptionSelectItem("😁 高冷", false),
+        OptionSelectItem("😁 幽默搞笑", false),
+        OptionSelectItem("😁 贴心暖男", false),
+        OptionSelectItem("😁 温柔体贴", false),
+        OptionSelectItem("😁 阳光大男孩", false),
+        OptionSelectItem("😁 都市精英", false),
+        OptionSelectItem("😁 小狼狗", false),
+        OptionSelectItem("😁 小奶狗", false),
+      ]),
+    );
+  }
+
+  /// 选中回复语气的选项
+  void selectReplyToneOption(
+      OptionSelectConfig rowConfig,
+      OptionSelectItem optionItem,
+      ) {
+    // 先全部反选
+    rowConfig.options =
+        rowConfig.options.map((ele) {
+          ele.selected = false;
+          return ele;
+        }).toList();
+    // 再勾选当前的选中项
+    optionItem.selected = true;
+    // 由于Rx响应式变量,无法监听对象中嵌套对象的某个属性的变化,导致页面不会刷新,需要手动刷新页面
+    replyToneOptionSelectConfigList.refresh();
+  }
+
   /// 点击上传截图按钮
   void clickUploadScreenshotBtn() {
   }

+ 166 - 32
lib/module/intimacy_analyse/screenshot_reply/scan_image_reply/scan_image_reply_view.dart

@@ -1,10 +1,15 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
 import 'package:keyboard/base/base_view.dart';
 
+import '../../../../data/bean/option_select_config.dart';
+import '../../../../data/bean/option_select_item.dart';
 import '../../../../resource/assets.gen.dart';
 import '../../../../resource/colors.gen.dart';
 import '../../../../resource/string.gen.dart';
+import '../../intimacy_analyse_upload/widget/upload_item_widget.dart';
+import '../../widget/option_select_widget.dart';
 import '../../widget/step_card.dart';
 import 'package:keyboard/module/intimacy_analyse/screenshot_reply/scan_image_reply/scan_image_reply_controller.dart';
 
@@ -21,11 +26,18 @@ class ScanImageReplyView extends BaseView<ScanImageReplyController> {
   Widget buildBody(BuildContext context) {
     return Stack(
       children: [
-        Column(
-          children: [
-            // 上传截图例子卡片
-            _buildUploadScreenshotSampleCard(),
-          ],
+        SingleChildScrollView(
+          child: Column(
+            children: [
+              // 上传截图的卡片
+              _buildUploadScreenshotCard(false),
+              SizedBox(height: 14.h),
+              // 选项卡片
+              _buildOptionCard(),
+              // 距离底部有一定间距
+              SizedBox(height: 90.h),
+            ],
+          ),
         ),
         // 上传截图按钮
         Positioned.fill(
@@ -38,8 +50,83 @@ class ScanImageReplyView extends BaseView<ScanImageReplyController> {
     );
   }
 
-  /// 上传截图例子卡片
-  Widget _buildUploadScreenshotSampleCard() {
+  /// 上传截图的卡片
+  /// [isSample] 是否是样例图片
+  Widget _buildUploadScreenshotCard(bool isSample) {
+    Widget actionBtn;
+
+    // 例子,示例图
+    if (isSample) {
+      actionBtn = Container(
+        padding: EdgeInsets.all(6.w),
+        decoration: BoxDecoration(
+          color: ColorName.black28,
+          borderRadius: BorderRadius.circular(9.r),
+        ),
+        child: Text(
+          StringName.intimacyAnalyseSampleImage,
+          style: TextStyle(
+            color: ColorName.white,
+            fontSize: 12.sp,
+            fontWeight: FontWeight.w500,
+          ),
+        ),
+      );
+    } else {
+      // 重新上传按钮
+      actionBtn = Container(
+        padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 12.w),
+        decoration: BoxDecoration(
+          color: ColorName.black80,
+          borderRadius: BorderRadius.circular(30.r),
+        ),
+        child: Text(
+          StringName.intimacyAnalyseReUpload,
+          style: TextStyle(
+            color: ColorName.white,
+            fontSize: 12.sp,
+            fontWeight: FontWeight.w500,
+          ),
+        ),
+      );
+    }
+
+    Widget imageWidget;
+    // 样例图片
+    if (isSample) {
+      imageWidget = Assets.images.iconUploadScreenshotSampleImage.image(
+        width: 288.w,
+        height: 290.h,
+        fit: BoxFit.cover,
+      );
+    } else {
+      // 上传
+      imageWidget = UploadItemWidget(
+        imageSrc: "",
+        hasDeleteBtn: false,
+        uploadState: UploadState.fail,
+        // 失败时的遮罩
+        uploadFailMaskWidget: UploadFailMaskWidget(
+          iconWidget: Assets.images.iconUploadFail.image(
+            width: 40.w,
+            height: 40.w,
+          ),
+          spaceWidget: SizedBox(height: 12.h),
+          textWidget: Text(
+            StringName.intimacyAnalyseUploadFail,
+            style: TextStyle(
+              color: ColorName.white,
+              fontSize: 16.sp,
+              fontWeight: FontWeight.w500,
+            ),
+          ),
+        ),
+        onClickItemCallback: () {
+          // 预览图片
+        },
+      );
+    }
+
     return StepCard(
       bgImageProvider: Assets.images.bgIntimacyAnalyseUploadCard.provider(),
       stepLabel: "",
@@ -68,34 +155,11 @@ class ScanImageReplyView extends BaseView<ScanImageReplyController> {
                     child: ClipRRect(
                       // 图片裁切成圆角
                       borderRadius: BorderRadius.circular(16.r),
-                      child: Assets.images.iconUploadScreenshotSampleImage
-                          .image(
-                        width: 288.w,
-                        height: 290.h,
-                        fit: BoxFit.cover,
-                      ),
-                    ),
-                  ),
-                ),
-                Positioned(
-                  right: 0,
-                  bottom: 0,
-                  child: Container(
-                    padding: EdgeInsets.all(6.w),
-                    decoration: BoxDecoration(
-                      color: ColorName.black28,
-                      borderRadius: BorderRadius.circular(9.r),
-                    ),
-                    child: Text(
-                      StringName.intimacyAnalyseSampleImage,
-                      style: TextStyle(
-                        color: ColorName.white,
-                        fontSize: 12.sp,
-                        fontWeight: FontWeight.w500,
-                      ),
+                      child: imageWidget,
                     ),
                   ),
                 ),
+                Positioned(right: 0, bottom: 0, child: actionBtn),
               ],
             ),
           ),
@@ -104,6 +168,76 @@ class ScanImageReplyView extends BaseView<ScanImageReplyController> {
     );
   }
 
+  /// 选项卡片
+  Widget _buildOptionCard() {
+    return Container(
+      width: double.maxFinite,
+      margin: EdgeInsets.only(left: 14.w, right: 14.w),
+      // 圆角背景
+      decoration: BoxDecoration(
+        gradient: LinearGradient(
+          colors: [Color(0xFFEFE9FF), Color(0xFFFBFAFF)],
+          begin: Alignment.topCenter,
+          end: Alignment.bottomRight,
+        ),
+        shape: BoxShape.rectangle,
+        border: Border.all(color: ColorName.white80, width: 1.w),
+        borderRadius: BorderRadius.all(Radius.circular(20.r)),
+      ),
+      child: Column(
+        children: [
+          Container(
+            margin: EdgeInsets.only(
+              left: 12.w,
+              top: 17.h,
+              right: 12.w,
+              bottom: 12.h,
+            ),
+            padding: EdgeInsets.only(
+              left: 12.w,
+              top: 12.h,
+              right: 12.w,
+              bottom: 12.h,
+            ),
+            decoration: BoxDecoration(
+              color: ColorName.white,
+              borderRadius: BorderRadius.circular(16.r),
+            ),
+            child: Obx(() {
+              return Column(
+                children: [
+                  // 添加选项行
+                  for (var optionConfig
+                      in controller.replyToneOptionSelectConfigList)
+                    _buildOptionRow(optionConfig),
+                ],
+              );
+            }),
+          ),
+        ],
+      ),
+    );
+  }
+
+  /// 构建一行选择选项行
+  Widget _buildOptionRow(OptionSelectConfig config) {
+    return Column(
+      children: [
+        // 选择项
+        OptionSelectWidget(
+          optionSelect: config,
+          optionSelectCallback: (
+            OptionSelectConfig rowConfig,
+            OptionSelectItem optionItem,
+          ) {
+            controller.selectReplyToneOption(rowConfig, optionItem);
+          },
+        ),
+        SizedBox(height: 20.h),
+      ],
+    );
+  }
+
   /// 上传截图按钮
   Widget _buildUploadScreenshotBtn() {
     return GestureDetector(

+ 8 - 6
lib/module/intimacy_analyse/widget/option_select_widget.dart

@@ -32,7 +32,7 @@ class OptionSelectWidget extends StatelessWidget {
       children: [
         // 标题
         _buildTitle(),
-        SizedBox(height: 8.h),
+        SizedBox(height: 16.h),
         // 选项
         _buildOptionList(),
       ],
@@ -63,8 +63,8 @@ class OptionSelectWidget extends StatelessWidget {
         Text(
           optionSelect.title,
           style: TextStyle(
-            fontSize: 12.sp,
-            fontWeight: FontWeight.w500,
+            fontSize: 14.sp,
+            fontWeight: FontWeight.w700,
             color: ColorName.black80,
           ),
         ),
@@ -85,10 +85,10 @@ class OptionSelectWidget extends StatelessWidget {
       gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
         // 一行3列
         crossAxisCount: 3,
-        // 垂直方向的间距
-        crossAxisSpacing: 8.0,
         // 水平方向的间距
-        mainAxisSpacing: 7.0,
+        crossAxisSpacing: 6.0,
+        // 垂直方向的间距
+        mainAxisSpacing: 12.0,
         // 子项的宽高比
         childAspectRatio: 2.6,
       ),
@@ -155,7 +155,9 @@ class OptionSelectWidget extends StatelessWidget {
         child: Center(
           child: Text(
             optionItem.name,
+            maxLines: 1,
             style: TextStyle(
+              overflow: TextOverflow.ellipsis,
               fontSize: 13.sp,
               fontWeight: FontWeight.w400,
               // 文字颜色,选中时为白色,未选中时为黑色

+ 2 - 5
lib/module/intimacy_analyse/widget/step_card.dart

@@ -45,12 +45,9 @@ class StepCard extends StatelessWidget {
     if (bgImageProvider == null) {
       decoration = BoxDecoration(
         gradient: LinearGradient(
-          colors: [
-            Color(0xFFEFE9FF),
-            Color(0xFFFBFAFF),
-          ],
+          colors: [Color(0xFFEFE9FF), Color(0xFFFBFAFF)],
           begin: Alignment.topCenter,
-          end: Alignment.bottomCenter,
+          end: Alignment.bottomRight,
         ),
         shape: BoxShape.rectangle,
         border: Border.all(color: ColorName.white80, width: 1.w),

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

@@ -200,6 +200,7 @@ class StringName {
   static final String intimacyAnalyseSampleImage = 'intimacy_analyse_sample_image'.tr; // 示例图
   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 nextStep = 'next_step'.tr; // 下一步
   static final String recently = 'recently'.tr; // 最近
   static final String addHobbies = 'add_hobbies'.tr; // 添加爱好
@@ -407,6 +408,7 @@ class StringMultiSource {
       'intimacy_analyse_sample_image': '示例图',
       'intimacy_analyse_look_analyse': '查看分析',
       'intimacy_analyse_ta_demand': 'TA现在的需求',
+      'intimacy_analyse_re_upload': '重新上传',
       'next_step': '下一步',
       'recently': '最近',
       'add_hobbies': '添加爱好',