|
@@ -8,6 +8,8 @@ import QChat from '@/views/OaSystem/aiQA/components/QChat.vue'
|
|
|
import { useSSE, type AResultRecord } from '@/hooks/web/useSSE'
|
|
|
import { collectChatHistory, createChatHistory, getChatHistory, kbChat } from '@/service/aiService'
|
|
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
|
|
+import ResultMessageView from '@/views/OaSystem/aiQA/components/ResultMessageView.vue'
|
|
|
+import { cloneDeep } from 'lodash-es'
|
|
|
|
|
|
interface ChatContentProps {
|
|
|
currentQuestion: { text: string; id?: string }
|
|
@@ -25,14 +27,21 @@ const currentQuestionId = ref('')
|
|
|
// 对话
|
|
|
const {
|
|
|
data,
|
|
|
+ isLoading,
|
|
|
mutate: startChat,
|
|
|
- loading
|
|
|
+ stop
|
|
|
} = useSSE({
|
|
|
mutationFn: kbChat,
|
|
|
onSuccess: (res) => {
|
|
|
const question = Object.keys(contents)?.at(-1)
|
|
|
- contents[question] = res
|
|
|
- // todo 保存当前历史
|
|
|
+ contents[question] = cloneDeep(res)
|
|
|
+ // 保存当前历史
|
|
|
+ const queryBody = {
|
|
|
+ chatId: currentQuestionId.value,
|
|
|
+ question,
|
|
|
+ answer: res?.result?.['3']?.choices?.[0]?.delta?.content
|
|
|
+ }
|
|
|
+ collectHistory(queryBody)
|
|
|
}
|
|
|
})
|
|
|
|
|
@@ -47,22 +56,22 @@ const { mutate: createHistory } = useMutation({
|
|
|
})
|
|
|
|
|
|
// 更新历史
|
|
|
-const { mutate: collectHistory } = useMutation({
|
|
|
- mutationFn: collectChatHistory,
|
|
|
- onSuccess: (res) => {
|
|
|
- console.log('collectHistory success:', res)
|
|
|
- }
|
|
|
-})
|
|
|
+const { mutate: collectHistory } = useMutation({ mutationFn: collectChatHistory })
|
|
|
|
|
|
// 获取历史详情
|
|
|
-useQuery({
|
|
|
+const { isLoading: historyLoading } = useQuery({
|
|
|
queryKey: ['getChatHistory', currentQuestion],
|
|
|
queryFn: async () => {
|
|
|
if ((currentQuestion.value?.id ?? '') === '') return null
|
|
|
return await getChatHistory(currentQuestion.value.id)
|
|
|
},
|
|
|
onSuccess: (res) => {
|
|
|
- console.log('getChatHistory success:', res)
|
|
|
+ if ((res?.length ?? 0) > 0) {
|
|
|
+ currentQuestionId.value = currentQuestion.value?.id
|
|
|
+ res?.forEach(({ question, answer }) => {
|
|
|
+ contents[question] = { result: { '3': { choices: [{ delta: { content: answer } }] } } }
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
})
|
|
|
|
|
@@ -87,18 +96,16 @@ const handleSend = (text): void => {
|
|
|
watch(
|
|
|
currentQuestion,
|
|
|
(newVal) => {
|
|
|
- const { text, id } = newVal
|
|
|
+ const { text } = newVal
|
|
|
if ((text ?? '') === '') {
|
|
|
//重置对话记录
|
|
|
Object.keys(contents).forEach((key) => {
|
|
|
delete contents[key]
|
|
|
})
|
|
|
+ currentQuestionId.value = ''
|
|
|
} else {
|
|
|
handleSend(text)
|
|
|
}
|
|
|
- if ((id ?? '') !== '') {
|
|
|
- // 加载历史记录
|
|
|
- }
|
|
|
},
|
|
|
{ deep: true }
|
|
|
)
|
|
@@ -106,12 +113,14 @@ watch(
|
|
|
|
|
|
<template>
|
|
|
<div class="chat-content">
|
|
|
- <div class="message-body">
|
|
|
+ <div class="message-body" v-loading="historyLoading">
|
|
|
<template v-if="(Object.keys(contents)?.length ?? 0) > 0">
|
|
|
<template v-for="(value, key) in contents" :key="key">
|
|
|
<QChat>{{ key }}</QChat>
|
|
|
- <!-- 思维链 -->
|
|
|
- <AChat v-if="value == null"> {{ data.content }}...</AChat>
|
|
|
+ <AChat v-if="value == null">
|
|
|
+ <!-- 思维链过程 -->
|
|
|
+ <ResultMessageView isLoading defaultOpen :data="data?.content ?? ''" />
|
|
|
+ </AChat>
|
|
|
<AChat v-else>
|
|
|
<!-- 回答结果 -->
|
|
|
<AResult :data="value" />
|
|
@@ -138,7 +147,8 @@ watch(
|
|
|
placeholder="请输入问题,可以通过回车换行"
|
|
|
/>
|
|
|
<div class="q-btn" @click="handleSend(textarea)">
|
|
|
- <img :src="sendImg" alt="发送" />
|
|
|
+ <div v-if="isLoading" class="stop-btn" @click="stop"></div>
|
|
|
+ <img v-else :src="sendImg" alt="发送" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -201,12 +211,22 @@ watch(
|
|
|
overflow: hidden;
|
|
|
cursor: pointer;
|
|
|
align-self: flex-end;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
|
|
|
img {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
}
|
|
|
|
|
|
+ .stop-btn {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 2px;
|
|
|
+ }
|
|
|
+
|
|
|
&:hover {
|
|
|
opacity: 0.9;
|
|
|
}
|