import moment from 'moment';
|
import { Ref, ShallowRef, computed, nextTick, onBeforeUnmount, ref, unref } from 'vue';
|
import { LOAD_CHAT_LIMIT } from '../constants';
|
import { AnswerType, ChatContent, ChatMessage, MultiChatType, RoleEnum, StepEnum, StepItem } from '../model/types';
|
import { GetHistoryAnswer, QueryHistoryDetail, getShareChatJsonByPost } from '/@/api/ai/chat';
|
import router from '/@/router';
|
import { isSharePage } from '/@/stores/chatRoom';
|
type UseScrollLoadOption = {
|
container: ShallowRef<HTMLDivElement>;
|
historyGroupId: string | Ref<string>;
|
messageList: Ref<ChatMessage[]>;
|
parseAnswerContent: (res: any) => ChatContent;
|
};
|
|
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[]) => {
|
const stepList = (process ?? []).reduce((preVal, curVal) => {
|
if (curVal.mode === 'question') {
|
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;
|
};
|
|
/**
|
* 滚动加载数据
|
* @returns
|
*/
|
export const useScrollLoad = (option: UseScrollLoadOption) => {
|
const { container, historyGroupId, messageList, parseAnswerContent } = option;
|
const moreIsLoading = ref(false);
|
|
/** @description 下次需要加载的用户结束索引(倒着数) */
|
const nextUserMsgEndIndex = ref(0);
|
|
// 是否没有更多数据了
|
let isNoMore = false;
|
const getAnswerById = async (historyId: string) => {
|
return await GetHistoryAnswer({
|
history_id: historyId,
|
});
|
};
|
const formatShowTimeYear = computed(() => {
|
return (str) => {
|
return moment(str).format('MM月DD日 HH:mm:ss');
|
};
|
});
|
/**
|
* 获取用户回复数据,并插入到对话当中去
|
*/
|
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.value(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,
|
sectionAId: mapUser?.section_a_id,
|
createTime: answerTime,
|
stepList: convertProcessToStep(item?.answer?.exec_process),
|
stepIsShow: false,
|
isStopMsg: false,
|
|
conclusion: item?.answer?.conclusion ?? [],
|
isChecked: false,
|
}
|
);
|
i++;
|
});
|
|
messageList.value.unshift(...tmpMessageList);
|
};
|
|
/**
|
* 加载分享数据
|
*/
|
const loadShareData = async () => {
|
const res = await getShareChatJsonByPost({
|
share_id: router.currentRoute.value.query.id as string,
|
});
|
|
const msgValue = res?.values;
|
if (!msgValue) {
|
messageList.value = [];
|
return;
|
}
|
const userMsg: ChatMessage = {
|
historyId: msgValue.history_id,
|
role: RoleEnum.user,
|
content: {
|
type: AnswerType.Text,
|
values: msgValue.question,
|
},
|
isChecked: false,
|
};
|
|
const assistantMsg: ChatMessage = {
|
historyId: msgValue.history_id,
|
role: RoleEnum.assistant,
|
content: parseAnswerContent(msgValue),
|
stepList: convertProcessToStep(msgValue.exec_process),
|
stepIsShow: false,
|
isStopMsg: false,
|
|
conclusion: msgValue.conclusion ?? [],
|
isChecked: false,
|
};
|
messageList.value = [userMsg, assistantMsg];
|
};
|
|
/**
|
* 加载滚动范围数据
|
*/
|
const loadRangeData = async (lastEnd = nextUserMsgEndIndex.value) => {
|
if (isSharePage.value) {
|
await loadShareData();
|
return;
|
}
|
const res = await QueryHistoryDetail({
|
history_group_id: unref(historyGroupId),
|
last_end: lastEnd,
|
last_count: LOAD_CHAT_LIMIT,
|
});
|
const result: ChatMessage[] = res.details ?? [];
|
if (result.length) {
|
nextUserMsgEndIndex.value += result.length;
|
await loadReplyData(res.details);
|
} else {
|
isNoMore = true;
|
}
|
|
return result;
|
};
|
|
//滚动监听
|
async function onChatListScroll() {
|
if (container.value.scrollTop == 0) {
|
// 更多数据正在加载时
|
if (moreIsLoading.value) {
|
return;
|
}
|
|
if (isNoMore) {
|
return;
|
}
|
|
let h1 = container.value.scrollHeight;
|
moreIsLoading.value = true;
|
await loadRangeData(nextUserMsgEndIndex.value).finally(() => {
|
moreIsLoading.value = false;
|
});
|
//更新后,等待页面渲染完毕再去拿scrollHeight,否则拿到的是之前的
|
nextTick(() => {
|
nextTick(() => {
|
nextTick(() => {
|
let h2 = container.value.scrollHeight;
|
container.value.scrollTo({
|
//顶部在原先基础上往下滚动50px,露出新加载数据的一点
|
// top: h2 - h1 - 50,
|
top: h2 - h1,
|
behavior: 'instant', //auto-自动滚动 instant-瞬间滚动 smooth-平滑滚动
|
});
|
});
|
});
|
});
|
}
|
}
|
|
onBeforeUnmount(() => {
|
container.value.removeEventListener('scroll', onChatListScroll);
|
});
|
|
return {
|
nextUserMsgEndIndex,
|
loadRangeData,
|
onChatListScroll,
|
moreIsLoading,
|
};
|
};
|