| | |
| | | import moment from 'moment'; |
| | | import type { Ref, ShallowRef } from 'vue'; |
| | | import { computed, nextTick, onBeforeUnmount, ref, unref } from 'vue'; |
| | | import { nextTick, onBeforeUnmount, ref, unref } from 'vue'; |
| | | import { LOAD_CHAT_LIMIT } from '../constants'; |
| | | import type { ChatContent, ChatMessage, StepItem } from '../model/types'; |
| | | import { AnswerType, MultiChatType, RoleEnum, StepEnum } from '../model/types'; |
| | | import { GetHistoryAnswer, QueryHistoryDetail, getShareChatJsonByPost } from '/@/api/ai/chat'; |
| | | import router from '/@/router'; |
| | | import { isSharePage } from '/@/stores/chatRoom'; |
| | | import type { ChatMessage } from '../model/types'; |
| | | import { useLoadData } from './useLoadData'; |
| | | import { QueryHistoryDetail } from '/@/api/ai/chat'; |
| | | type UseScrollLoadOption = { |
| | | container: ShallowRef<HTMLDivElement>; |
| | | historyGroupId: string | Ref<string>; |
| | | messageList: Ref<ChatMessage[]>; |
| | | parseAnswerContent: (res: any) => ChatContent; |
| | | loadReplyData: (res: any) => Promise<ChatMessage[]>; |
| | | }; |
| | | |
| | | export const convertProcessItem = (processItem: any) => { |
| | | switch (processItem.mode) { |
| | | case 'begin': |
| | | break; |
| | | case 'end': |
| | | break; |
| | | } |
| | | return { |
| | | status: StepEnum.Success, |
| | | title: processItem.value, |
| | | } as StepItem; |
| | | }; |
| | | export const convertProcessToStep = (process: any[],isHistory = true) => { |
| | | const stepList = (process ?? []).reduce((preVal, curVal) => { |
| | | if (curVal.mode === 'question' ) { |
| | | if(isHistory) return preVal; |
| | | const last = preVal.at(-1); |
| | | if (!last.subStep) { |
| | | last.subStep = []; |
| | | } |
| | | const sub = { |
| | | data: curVal.value, |
| | | type: MultiChatType.Select, |
| | | }; |
| | | last.subStep.push(sub); |
| | | } else { |
| | | const cur = convertProcessItem(curVal); |
| | | preVal.push(cur); |
| | | } |
| | | return preVal; |
| | | }, []); |
| | | return stepList; |
| | | }; |
| | | export const formatShowTimeYear = (str: string) => { |
| | | const date = moment(str); |
| | | const now = moment(); |
| | | const diffDays = now.diff(date, 'days'); |
| | | |
| | | if (diffDays === 0) { |
| | | return `今天 ${date.format('HH:mm:ss')}`; |
| | | } else if (diffDays === 1) { |
| | | return `昨天 ${date.format('HH:mm:ss')}`; |
| | | } else if (diffDays === 2) { |
| | | return `前天 ${date.format('HH:mm:ss')}`; |
| | | } |
| | | |
| | | if (date.year() === now.year()) { |
| | | return date.format('MM月DD日 HH:mm:ss'); |
| | | } else { |
| | | return date.format('YYYY年MM月DD日 HH:mm:ss'); |
| | | } |
| | | }; |
| | | /** |
| | | * 滚动加载数据 |
| | | * @returns |
| | | */ |
| | | export const useScrollLoad = (option: UseScrollLoadOption) => { |
| | | const { container, historyGroupId, messageList, parseAnswerContent } = option; |
| | | const { container, historyGroupId, messageList, loadReplyData } = option; |
| | | const moreIsLoading = ref(false); |
| | | |
| | | /** @description 下次需要加载的用户结束索引(倒着数) */ |
| | |
| | | |
| | | // 是否没有更多数据了 |
| | | let isNoMore = false; |
| | | const getAnswerById = async (historyId: string) => { |
| | | return await GetHistoryAnswer({ |
| | | history_id: historyId, |
| | | }); |
| | | }; |
| | | |
| | | |
| | | |
| | | /** |
| | | * 更新加载索引 |
| | | */ |
| | | const updateLoadIndex = () => { |
| | | nextUserMsgEndIndex++; |
| | | const updateLoadIndex = (addCount = 1) => { |
| | | nextUserMsgEndIndex += addCount; |
| | | }; |
| | | /** |
| | | * 获取用户回复数据,并插入到对话当中去 |
| | | */ |
| | | const loadReplyData = async (userMsg: any[]) => { |
| | | const userItemIdMap = new Map(); |
| | | // 用户消息 |
| | | const tmpMessageList: ChatMessage[] = userMsg.map((item) => { |
| | | return { |
| | | historyId: item.history_id, |
| | | role: RoleEnum.user, |
| | | content: { |
| | | type: AnswerType.Text, |
| | | values: item.question, |
| | | }, |
| | | isChecked: false, |
| | | } as ChatMessage; |
| | | }); |
| | | const resList = await Promise.all( |
| | | (userMsg ?? []).map((item) => { |
| | | userItemIdMap.set(item.history_id, item); |
| | | return getAnswerById(item.history_id); |
| | | }) |
| | | ); |
| | | let i = 0; |
| | | resList.map((item, index) => { |
| | | const insertIndex = index + 1 + i; |
| | | const currentUserMsg = tmpMessageList[insertIndex - 1]; |
| | | currentUserMsg.content.values = item?.answer?.question ?? currentUserMsg.content.values; |
| | | |
| | | const mapUser = userItemIdMap.get(item.answer?.history_id); |
| | | |
| | | const answerTime = formatShowTimeYear(mapUser?.create_time); |
| | | tmpMessageList.splice( |
| | | insertIndex, |
| | | 0, |
| | | item.answer === null |
| | | ? null |
| | | : { |
| | | historyId: item.answer?.history_id, |
| | | role: RoleEnum.assistant, |
| | | content: parseAnswerContent(item?.answer), |
| | | state: item.answer_state, |
| | | createTime: answerTime, |
| | | isStopMsg: false, |
| | | stepGroup: (item?.answer?.reports ?? []).map((item) => ({ |
| | | value: convertProcessToStep(item?.exec_process), |
| | | isShow: false, |
| | | })), |
| | | |
| | | conclusion: item?.answer?.conclusion ?? [], |
| | | isChecked: false, |
| | | } |
| | | ); |
| | | i++; |
| | | }); |
| | | |
| | | messageList.value.unshift(...tmpMessageList); |
| | | }; |
| | | |
| | | |
| | | |
| | | /** |
| | | * 加载滚动范围数据 |
| | | */ |
| | | const loadRangeData = async (lastEnd = nextUserMsgEndIndex) => { |
| | | |
| | | const res = await QueryHistoryDetail({ |
| | | history_group_id: unref(historyGroupId), |
| | | last_end: lastEnd, |
| | |
| | | const result: ChatMessage[] = res.details ?? []; |
| | | if (result.length) { |
| | | nextUserMsgEndIndex += result.length; |
| | | await loadReplyData(res.details); |
| | | const rangeMsgList = await loadReplyData(res.details); |
| | | messageList.value.unshift(...rangeMsgList); |
| | | } else { |
| | | isNoMore = true; |
| | | } |