import { ElMessage } from 'element-plus';
|
import { computed, nextTick, onDeactivated, ref } from 'vue';
|
// import useClipboard from 'vue-clipboard3';
|
import { onClickOutside, useClipboard } from '@vueuse/core';
|
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 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) => {
|
const toSetState = item.state === AnswerState.Like ? AnswerState.Null : AnswerState.Like;
|
const res = await SetHistoryAnswerState({
|
history_id: item.historyId,
|
answer_state: toSetState,
|
});
|
item.state = toSetState;
|
};
|
|
const unLikeClick = async (item) => {
|
const toSetState = item.state === AnswerState.Unlike ? AnswerState.Null : AnswerState.Unlike;
|
const res = await SetHistoryAnswerState({
|
history_id: item.historyId,
|
answer_state: toSetState,
|
});
|
item.state = toSetState;
|
};
|
const feedbackPosition = ref({
|
x: 0,
|
y: 0,
|
});
|
|
const feedbackIsShow = ref(false);
|
const feedbackContent = ref('');
|
const feedbackPanelRef = ref<HTMLDivElement>(null);
|
const currentFeedbackMapItem = ref(null);
|
const curFeedbackIndex = ref(0);
|
const feedbackClick = async (e, item, index) => {
|
currentFeedbackMapItem.value = item;
|
curFeedbackIndex.value = index;
|
const offsetX = -4;
|
const offsetY = -8;
|
feedbackIsShow.value = true;
|
nextTick(() => {
|
feedbackPosition.value = {
|
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(
|
computed(() => feedbackPanelRef.value?.[curFeedbackIndex.value]),
|
(e) => {
|
feedbackIsShow.value = false;
|
feedbackContent.value = '';
|
}
|
);
|
// useClickOther(
|
// computed(() => feedbackPanelRef.value?.[curFeedbackIndex.value]),
|
// feedbackIsShow,
|
// () => {
|
// feedbackIsShow.value = false;
|
// feedbackContent.value = '';
|
// }
|
// );
|
|
const showFixQuestion = (item) => {
|
const isShow = item?.role === RoleEnum.assistant && item.content?.origin?.sample_question?.length > 0 && !isSharePage.value;
|
return isShow;
|
};
|
const askMoreClick = (item) => {
|
if (!item.question) return;
|
sendChatMessage({ type: AnswerType.Text, values: item.question });
|
};
|
|
const fixQuestionClick = (item, originData) => {
|
if (!item) return;
|
// preQuestion.value = originData?.question;
|
try {
|
sendChatMessage({
|
type: AnswerType.Text,
|
values: item,
|
});
|
} finally {
|
// preQuestion.value = null;
|
}
|
};
|
|
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,
|
unLikeClick,
|
feedbackPosition,
|
feedbackIsShow,
|
feedbackContent,
|
feedbackPanelRef,
|
currentFeedbackMapItem,
|
curFeedbackIndex,
|
feedbackClick,
|
askMoreClick,
|
fixQuestionClick,
|
showFixQuestion,
|
speechClick,
|
isSpeaking,
|
isItemSpeaking,
|
checkIsText,
|
};
|
};
|