From a166dd543a10a1092528e7f27b680f35330f02c6 Mon Sep 17 00:00:00 2001
From: gerson <1405270578@qq.com>
Date: 星期六, 06 七月 2024 16:14:56 +0800
Subject: [PATCH] 单击提问示例输入;聊天室搜索;text回答支持,text,knowledge支持复制;question 进度查询

---
 src/components/chat/Chat.vue |  127 +++++++++++++++++++++++++++++++++++++----
 1 files changed, 113 insertions(+), 14 deletions(-)

diff --git a/src/components/chat/Chat.vue b/src/components/chat/Chat.vue
index 930ab85..0be8fda 100644
--- a/src/components/chat/Chat.vue
+++ b/src/components/chat/Chat.vue
@@ -11,14 +11,31 @@
 								<component class="max-w-[100ch]" :is="answerTypeMapCom[item.content.type]" :data="item.content.values" />
 							</div>
 
-							<!-- <div v-if="item.role === RoleEnum.assistant" class="absolute flex items-center right-0 space-x-2 mr-2 mt-2">
-								<SvgIcon class="cursor-pointer" name="ele-CopyDocument" @click="copyClick(item.content)" />
-								<SvgIcon class="cursor-pointer" name="ywicon icon-dianzan" />
-								<SvgIcon class="cursor-pointer" :size="12" name="ywicon icon-buzan" />
-							</div> -->
+							<div v-if="item.role === RoleEnum.assistant" class="absolute flex items-center right-0 mr-2 mt-2 space-x-2">
+								<div
+									class="flex items-center justify-center size-[15px]"
+									v-if="item.content?.type === AnswerType.Text || item.content?.type === AnswerType.Knowledge"
+								>
+									<i class="p-2 ywicon icon-copy cursor-pointer hover:text-[#0284ff] hover:!text-[18px]" @click="copyClick(item)" />
+								</div>
+								<div class="flex items-center justify-center size-[15px]">
+									<i
+										:class="{ 'text-[#0284ff]': item.state === AnswerState.Like }"
+										class="p-2 ywicon icon-dianzan cursor-pointer hover:text-[#0284ff] font-medium hover:!text-[18px]"
+										@click="likeClick(item)"
+									/>
+								</div>
+								<div class="flex items-center justify-center size-[15px]">
+									<i
+										:class="{ 'text-[#0284ff]': item.state === AnswerState.Unlike }"
+										class="p-2 ywicon icon-buzan cursor-pointer hover:text-[#0284ff] !text-[13px] hover:!text-[15px]"
+										@click="unLikeClick(item)"
+									/>
+								</div>
+							</div>
 						</div>
 
-						<Loding v-else />
+						<Loding v-else :process="process" />
 					</div>
 				</div>
 			</div>
@@ -36,11 +53,12 @@
 import useClipboard from 'vue-clipboard3';
 import Loding from './components/Loding.vue';
 import type { ChatContent } from './model/types';
-import { AnswerType, RoleEnum, answerTypeMapCom, roleImageMap, type ChatMessage } from './model/types';
-import { GetHistoryAnswer, QueryHistoryDetail, QuestionAi } from '/@/api/ai/chat';
+import { AnswerType, RoleEnum, answerTypeMapCom, roleImageMap, type ChatMessage, AnswerState } from './model/types';
+import { GetHistoryAnswer, QueryHistoryDetail, QuestionAi, SetHistoryAnswerState, getQuestionProcess } from '/@/api/ai/chat';
 import PlayBar from '/@/components/chat/components/playBar/PlayBar.vue';
 import router from '/@/router';
 import { activeChatRoom, activeLLMId, activeRoomId, activeSampleId, activeSectionAId } from '/@/stores/chatRoom';
+import { v4 as uuidv4 } from 'uuid';
 
 let isTalking = ref(false);
 let messageContent = ref<ChatContent>({
@@ -94,13 +112,47 @@
 	}
 	return content;
 };
+//#region ====================== 鏌ヨ杩涘害 ======================
+let processId = '';
+const QUERY_PROCESS_INTERVAL = 300;
+const process = ref('');
+let processTimer = null;
+let finishProcess = true;
 
+const queryProcessApi = async () => {
+	const res = await getQuestionProcess({
+		process_id: processId,
+	}).catch((err) => {
+		process.value = err;
+	});
+
+	process.value = res.process;
+	finishProcess = true;
+};
+
+const queryProcess = () => {
+	processTimer = setInterval(() => {
+		if (!finishProcess) return;
+		finishProcess = false;
+		queryProcessApi();
+	}, QUERY_PROCESS_INTERVAL);
+};
+
+const clearQueryProcess = () => {
+	process.value = '';
+	clearInterval(processTimer);
+};
+
+//#endregion
+
+let questionRes = null;
 const questionAi = async (text) => {
 	if (!currentSectionId) {
 		ElMessage.warning('鍙戦�佸け璐ワ紝鏈‘瀹氬簲鐢ㄥ満鏅紒');
 	}
-
+	processId = uuidv4();
 	const params = {
+		process_id: processId,
 		question: text,
 		// FIXME: 鏆傛椂杩欐牱
 		section_a_id: currentSectionId,
@@ -114,8 +166,11 @@
 	if (currentLLMId) {
 		params.llm_id = currentLLMId;
 	}
-
+	clearQueryProcess();
+	queryProcess();
 	const res = await QuestionAi(params);
+	clearQueryProcess();
+	questionRes = res;
 	// const res = {
 	// 	json_ok: true,
 	// 	question: '鏄ㄦ棩浜斾竴骞垮満鍘嬪姏',
@@ -173,15 +228,20 @@
 	}
 	try {
 		isTalking.value = true;
+		const userItem: ChatMessage = { role: RoleEnum.user, content } as any;
+		const assistantItem: ChatMessage = { role: RoleEnum.assistant, content: null, state: AnswerState.Null } as any;
 		// 鍙戦�佸綋鍓�
-		messageList.value.push({ role: RoleEnum.user, content });
+		messageList.value.push(userItem);
 		// 娓呯┖杈撳叆妗�
 		clearMessageContent();
+
 		// 鍑虹幇鍥炲锛岀疆绌哄嚭鐜扮瓑寰呭姩鐢�
-		messageList.value.push({ role: RoleEnum.assistant, content: null });
+		messageList.value.push(assistantItem);
 
 		let resMsgContent: ChatContent = null;
 		resMsgContent = await questionAi(content.values);
+		userItem.historyId = questionRes.history_id;
+		assistantItem.historyId = questionRes.history_id;
 		appendLastMessageContent(resMsgContent);
 	} catch (error: any) {
 		appendLastMessageContent({
@@ -219,6 +279,7 @@
 			});
 			messageList.value = (res.details ?? []).map((item) => {
 				return {
+					historyId: item.history_id,
 					role: RoleEnum.user,
 					content: {
 						type: AnswerType.Text,
@@ -234,8 +295,10 @@
 			resList.map((item, index) => {
 				const insertIndex = index + 1 + i;
 				messageList.value.splice(insertIndex, 0, {
+					historyId: item.answer.history_id,
 					role: RoleEnum.assistant,
 					content: parseContent(item.answer),
+					state: item.answer_state,
 				});
 				i++;
 			});
@@ -246,9 +309,11 @@
 	}
 );
 
+let forbidScroll = false;
 watch(
 	messageList,
 	() => {
+		if (forbidScroll) return;
 		nextTick(() => scrollToBottom());
 	},
 	{
@@ -260,9 +325,43 @@
 
 const { toClipboard } = useClipboard();
 
-const copyClick = (content) => {
+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;
+	}
 	ElMessage.success('澶嶅埗鎴愬姛');
-	toClipboard(content);
+	toClipboard(text);
+};
+
+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;
+	forbidScroll = true;
+	nextTick(() => {
+		forbidScroll = false;
+	});
+};
+
+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;
+
+	forbidScroll = true;
+	nextTick(() => {
+		forbidScroll = false;
+	});
 };
 //#endregion
 </script>

--
Gitblit v1.9.3