wujingjing
2025-04-09 2b8b2cac4fe3f05474459a034bc4034f2d7aa0cb
src/components/chat/hooks/useAssistantContentOpt.ts
@@ -1,31 +1,63 @@
import { ElMessage } from 'element-plus';
import type { ComputedRef } from 'vue';
import { computed, nextTick, ref } from 'vue';
import { computed, nextTick, onDeactivated, ref } from 'vue';
// import useClipboard from 'vue-clipboard3';
import { onClickOutside, useClipboard } from '@vueuse/core';
import type { ChatMessage } from '../model/types';
import markdownToTxt from 'markdown-to-txt';
import { AnswerState, AnswerType, RoleEnum } from '../model/types';
import { SetHistoryAnswerState } from '/@/api/ai/chat';
import { isSharePage } from '/@/stores/chatRoom';
import BrowserSpeechSynthesis from '/@/utils/speech/synthesis';
export type AssistantContentOptOption = {
   sendChatMessage: any;
};
const activeSpeakItem = ref(null);
export const useAssistantContentOpt = (option: AssistantContentOptOption) => {
   const isSpeaking = ref(false);
   const { sendChatMessage } = option;
   const { copy } = useClipboard();
   const copyClick = (item) => {
      const type = item.content.type;
      let text = '';
      if (type === AnswerType.Knowledge) {
         text = item.content.values?.map((item) => item.answer).join('\n\n') ?? '';
      } else {
         text = item.content.values;
      }
      const isText = checkIsText(item);
      if (!isText) return;
      const text = getPlainText(item);
      ElMessage.success('复制成功');
      copy(text);
   };
   const checkIsText = (item) => {
      const isText = item?.content?.values?.some((item) => item?.content?.type === AnswerType.Knowledge)  || item?.conclusion?.length > 0;
      return isText;
   };
   const getPlainText = (item) => {
      let result = '';
      const knowledgeText = item.content.values
         .filter((item) => {
            const type = item?.content?.type;
            return type === AnswerType.Knowledge;
         })
         .reduce((acc, cur) => {
            const answer = cur?.content?.values
               ?.map((item) => {
                  const mdText = item.answer;
                  const linkText = item.metadata?.Title;
                  if (linkText) {
                     return `${mdText}\n\n${linkText}`;
                  }
                  return mdText;
               })
               .join('\n\n');
            return acc + answer;
         }, '');
      const conclusionText =
         item.conclusion
            ?.filter((item) => !!item.report)
            .map((item) => item.report)
            .join('\n\n') ?? '';
      result += knowledgeText + conclusionText;
      return markdownToTxt(result);
   };
   const likeClick = async (item) => {
@@ -63,10 +95,15 @@
      feedbackIsShow.value = true;
      nextTick(() => {
         feedbackPosition.value = {
            x: -feedbackPanelRef.value[index].$el.clientWidth + offsetX,
            y: -feedbackPanelRef.value[index].$el.clientHeight + offsetY,
            x: -feedbackPanelRef.value[index]?.$el.clientWidth + offsetX,
            y: -feedbackPanelRef.value[index]?.$el.clientHeight + offsetY,
         };
      });
   };
   const isItemSpeaking = (item) => {
      const checkSpeak = activeSpeakItem.value === item && isSpeaking.value;
      return checkSpeak;
   };
   onClickOutside(
@@ -85,9 +122,8 @@
   //    }
   // );
   const showFixQuestion = (item) => {
      const isShow = item?.role === RoleEnum.assistant  && item.content?.origin?.sample_question?.length > 0 && !isSharePage.value;
      const isShow = item?.role === RoleEnum.assistant && item.content?.origin?.sample_question?.length > 0 && !isSharePage.value;
      return isShow;
   };
   const askMoreClick = (item) => {
@@ -108,6 +144,58 @@
      }
   };
   let isEnterStop = false;
   const resetSpeak = () => {
      isSpeaking.value = false;
      isEnterStop = false;
      const instance = BrowserSpeechSynthesis.getInstance();
      instance.cancel();
      activeSpeakItem.value = null;
   };
   const speechClick = (item) => {
      if (!checkIsText(item)) return;
      if (activeSpeakItem.value !== item) {
         resetSpeak();
      }
      isSpeaking.value = !isSpeaking.value;
      if (isSpeaking.value) {
         startSpeechClick(item);
      } else {
         stopSpeechClick();
      }
   };
   const startSpeechClick = (item) => {
      activeSpeakItem.value = item;
      const instance = BrowserSpeechSynthesis.getInstance();
      instance.onEnd(() => {
         resetSpeak();
      });
      if (isEnterStop) {
         instance.resume();
      } else {
         const text = getPlainText(item);
         if (text) {
            instance.speak(text);
         }
      }
      isEnterStop = false;
   };
   const stopSpeechClick = () => {
      isEnterStop = true;
      const instance = BrowserSpeechSynthesis.getInstance();
      instance.pause();
   };
   onDeactivated(() => {
      const instance = BrowserSpeechSynthesis.getInstance();
      instance.cancel();
   });
   return {
      copyClick,
      likeClick,
@@ -122,5 +210,9 @@
      askMoreClick,
      fixQuestionClick,
      showFixQuestion,
      speechClick,
      isSpeaking,
      isItemSpeaking,
      checkIsText,
   };
};