Browse Source

[new]小听聊天界面增加一键复制功能

zk 1 year ago
parent
commit
f5c4c8ca8b

BIN
assets/images/icon_chat_copy.webp


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

@@ -178,4 +178,5 @@
     <string name="talk_summary">总结</string>
     <string name="home_talk_see_more_txt">想找更多谈话?查看</string>
     <string name="home_talk_all">全部谈话</string>
+    <string name="copy_success">已复制</string>
 </resources>

+ 6 - 3
lib/data/bean/progressing_chat_item.dart

@@ -1,8 +1,5 @@
-import 'dart:async';
-
 import 'package:electronic_assistant/data/bean/chat_item.dart';
 import 'package:get/get.dart';
-
 import '../../widget/gradually_md_text.dart';
 
 class ProgressingChatItem extends ChatItem {
@@ -28,6 +25,12 @@ class ProgressingChatItem extends ChatItem {
     graduallyController.appendDone();
   }
 
+  void setGraduallyFinishedListener() {
+    graduallyController.setGraduallyFinishedListener(() {
+      isFinished.value = true;
+    });
+  }
+
   void append(String content) {
     graduallyController.append(content);
     isGradually.value = true;

+ 11 - 1
lib/module/chat/controller.dart

@@ -15,7 +15,10 @@ import 'package:electronic_assistant/handler/event_handler.dart';
 import 'package:electronic_assistant/module/chat/start/view.dart';
 import 'package:electronic_assistant/module/chat/view.dart';
 import 'package:electronic_assistant/resource/colors.gen.dart';
+import 'package:electronic_assistant/resource/string.gen.dart';
+import 'package:electronic_assistant/utils/toast_util.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 import 'package:get/get.dart';
 import 'package:pull_to_refresh/pull_to_refresh.dart';
 import 'package:uuid/uuid.dart';
@@ -201,6 +204,7 @@ class ChatController extends BaseController {
         .streamChat(chatContent,
             talkId: talkInfo.value?.id, agendaId: agenda.value?.id)
         .then((stream) {
+      progressingChatItem.setGraduallyFinishedListener();
       StreamSubscription? sub;
       sub = stream.listen((event) {
         try {
@@ -225,7 +229,6 @@ class ChatController extends BaseController {
         progressingChatItem.setAppendDone();
         progressingChatItem.content =
             progressingChatItem.graduallyController.graduallyTxt;
-        progressingChatItem.isFinished.value = true;
       }, onError: (error) {
         _streamChatSubscriptions.remove(sub);
         progressingChatItem.isFailed.value = true;
@@ -316,6 +319,13 @@ class ChatController extends BaseController {
     }
   }
 
+  void onCopyClick(String? content) {
+    if (content != null) {
+      Clipboard.setData(ClipboardData(text: content));
+    }
+    ToastUtil.showToast(StringName.copySuccess.tr);
+  }
+
   @override
   void onClose() {
     super.onClose();

+ 78 - 43
lib/module/chat/view.dart

@@ -254,7 +254,7 @@ class ChatPage extends BasePage<ChatController> {
       child: IntrinsicWidth(
         child: progressingChatItem == null
             ? _buildAssistantChatItemContent(
-                null, null, chatItem.content, chatItem.id)
+                true, null, null, chatItem.content, chatItem.id)
             : Obx(() {
                 bool? isStreamStarted = progressingChatItem == null
                     ? null
@@ -262,6 +262,7 @@ class ChatPage extends BasePage<ChatController> {
                         progressingChatItem.isFinished.value ||
                         progressingChatItem.isFailed.value;
                 return _buildAssistantChatItemContent(
+                    progressingChatItem?.isFinished.value,
                     isStreamStarted,
                     progressingChatItem?.graduallyController,
                     progressingChatItem!.isFailed.value
@@ -273,60 +274,42 @@ class ChatPage extends BasePage<ChatController> {
     );
   }
 
-  Widget _buildAssistantChatItemContent(bool? isStreamStarted,
+  Widget _buildAssistantChatItemContent(bool? isFinish, bool? isStreamStarted,
       GraduallyController? graduallyController, String? content, String id) {
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
         SizedBox(height: 10.h),
-        isStreamStarted != null && isStreamStarted == false
-            ? Container(
-                padding: const EdgeInsets.all(1),
-                decoration: BoxDecoration(
-                    color: ColorName.colorPrimary,
-                    gradient: LinearGradient(
-                      colors: ['#B57AFF'.toColor(), '#4466FF'.toColor()],
-                      stops: const [0, 1.0],
-                      begin: Alignment.topLeft,
-                      end: Alignment.bottomRight,
-                    ),
-                    borderRadius: BorderRadius.only(
-                        topRight: Radius.circular(20.w),
-                        bottomRight: Radius.circular(20.w),
-                        bottomLeft: Radius.circular(20.w))),
-                child: Container(
-                    decoration: BoxDecoration(
-                        color: ColorName.white,
-                        borderRadius: BorderRadius.only(
-                            topRight: Radius.circular(20.w),
-                            bottomRight: Radius.circular(20.w),
-                            bottomLeft: Radius.circular(20.w))),
-                    padding:
-                        EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
-                    child: Lottie.asset(
-                        "assets/anim/anim_chat_response_loading.zip",
-                        width: 46.w,
-                        height: 20.w)),
-              )
-            : Container(
+        if (isStreamStarted != null && isStreamStarted == false)
+          Container(
+            padding: const EdgeInsets.all(1),
+            decoration: BoxDecoration(
+                color: ColorName.colorPrimary,
+                gradient: LinearGradient(
+                  colors: ['#B57AFF'.toColor(), '#4466FF'.toColor()],
+                  stops: const [0, 1.0],
+                  begin: Alignment.topLeft,
+                  end: Alignment.bottomRight,
+                ),
+                borderRadius: BorderRadius.only(
+                    topRight: Radius.circular(20.w),
+                    bottomRight: Radius.circular(20.w),
+                    bottomLeft: Radius.circular(20.w))),
+            child: Container(
                 decoration: BoxDecoration(
                     color: ColorName.white,
-                    border: Border.all(color: '#ECECEC'.color, width: 1.w),
                     borderRadius: BorderRadius.only(
                         topRight: Radius.circular(20.w),
                         bottomRight: Radius.circular(20.w),
                         bottomLeft: Radius.circular(20.w))),
                 padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
-                alignment: Alignment.centerLeft,
-                constraints: BoxConstraints(
-                  maxWidth: 0.78.sw,
-                ),
-                child: GraduallyMdText(
-                    initTxt: content,
-                    graduallyController: graduallyController,
-                    textStyle: TextStyle(
-                        fontSize: 14.w, color: ColorName.primaryTextColor)),
-              ),
+                child: Lottie.asset(
+                    "assets/anim/anim_chat_response_loading.zip",
+                    width: 46.w,
+                    height: 20.w)),
+          )
+        else
+          _buildAiContent(isFinish, content, graduallyController),
         Obx(() {
           return Visibility(
             visible: id == controller.chatAiTagId.value,
@@ -351,6 +334,58 @@ class ChatPage extends BasePage<ChatController> {
     );
   }
 
+  Widget _buildAiContent(bool? isFinish, String? content,
+      GraduallyController? graduallyController) {
+    if (isFinish == true) {
+      return Row(
+        crossAxisAlignment: CrossAxisAlignment.end,
+        children: [
+          IntrinsicWidth(
+            child: _buildGraduallyMdText(content, graduallyController),
+          ),
+          SizedBox(width: 10.w),
+          Visibility(
+            visible: isFinish == true,
+            child: Padding(
+              padding: EdgeInsets.only(bottom: 4.w),
+              child: GestureDetector(
+                  onTap: () {
+                    controller.onCopyClick(content);
+                  },
+                  child: Assets.images.iconChatCopy
+                      .image(width: 28.w, height: 28.w)),
+            ),
+          )
+        ],
+      );
+    } else {
+      return _buildGraduallyMdText(content, graduallyController);
+    }
+  }
+
+  Widget _buildGraduallyMdText(
+      String? content, GraduallyController? graduallyController) {
+    return Container(
+      decoration: BoxDecoration(
+          color: ColorName.white,
+          border: Border.all(color: '#ECECEC'.color, width: 1.w),
+          borderRadius: BorderRadius.only(
+              topRight: Radius.circular(20.w),
+              bottomRight: Radius.circular(20.w),
+              bottomLeft: Radius.circular(20.w))),
+      padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
+      alignment: Alignment.centerLeft,
+      constraints: BoxConstraints(
+        maxWidth: 0.78.sw,
+      ),
+      child: GraduallyMdText(
+          initTxt: content,
+          graduallyController: graduallyController,
+          textStyle:
+              TextStyle(fontSize: 14.w, color: ColorName.primaryTextColor)),
+    );
+  }
+
   Widget _buildUserChatItem(BuildContext context, ChatItem chatItem) {
     if (chatItem is FileChatItem) {
       return _buildUserFileChatItem(context, chatItem);

+ 6 - 0
lib/widget/gradually_md_text.dart

@@ -31,11 +31,16 @@ class GraduallyController {
   bool? isShowDone;
 
   GraduallyTxtListener? listener;
+  VoidCallback? finishedListener;
 
   setGraduallyTxtListener(GraduallyTxtListener listener) {
     this.listener = listener;
   }
 
+  setGraduallyFinishedListener(VoidCallback finishedListener) {
+    this.finishedListener = finishedListener;
+  }
+
   append(String txt) {
     graduallyTxt += txt;
     _startAppend();
@@ -65,6 +70,7 @@ class GraduallyController {
         listener?.call(graduallyTxt.substring(0, progressIndex));
       } else if (isAppendDone == true) {
         dispose();
+        finishedListener?.call();
       }
     });
   }