|
@@ -6,50 +6,98 @@ import ExampleChat from '@/views/OaSystem/aiQA/components/ExampleChat.vue'
|
|
|
import sendImg from '@/assets/imgs/ai/send.png'
|
|
|
import QChat from '@/views/OaSystem/aiQA/components/QChat.vue'
|
|
|
import { useSSE, type AResultRecord } from '@/hooks/web/useSSE'
|
|
|
-import { kbChat } from '@/service/aiService'
|
|
|
+import { collectChatHistory, createChatHistory, getChatHistory, kbChat } from '@/service/aiService'
|
|
|
+import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
|
|
|
|
|
interface ChatContentProps {
|
|
|
- currentQuestion: string[]
|
|
|
- changeQuestion: (q: string, init?: boolean) => void
|
|
|
+ currentQuestion: { text: string; id?: string }
|
|
|
+ changeQuestion: (q: string, id?: string) => void
|
|
|
}
|
|
|
|
|
|
+const queryClient = useQueryClient()
|
|
|
const props = defineProps<ChatContentProps>()
|
|
|
const { currentQuestion } = toRefs(props)
|
|
|
const { changeQuestion } = props
|
|
|
const textarea = ref('')
|
|
|
-const contents = reactive<{ string: AResultRecord }>({}) //对话记录,以问题为key保存
|
|
|
-
|
|
|
-const { data, mutation: startChat } = useSSE({
|
|
|
+const contents = reactive<{ string?: AResultRecord }>({}) //对话记录,以问题为key保存
|
|
|
+const currentQuestionId = ref('')
|
|
|
+
|
|
|
+// 对话
|
|
|
+const {
|
|
|
+ data,
|
|
|
+ mutate: startChat,
|
|
|
+ loading
|
|
|
+} = useSSE({
|
|
|
mutationFn: kbChat,
|
|
|
onSuccess: (res) => {
|
|
|
- const question = currentQuestion.value[currentQuestion.value.length - 1]
|
|
|
+ const question = Object.keys(contents)?.at(-1)
|
|
|
contents[question] = res
|
|
|
+ // todo 保存当前历史
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 创建历史
|
|
|
+const { mutate: createHistory } = useMutation({
|
|
|
+ mutationFn: createChatHistory,
|
|
|
+ onSuccess: (id) => {
|
|
|
+ currentQuestionId.value = id
|
|
|
+ // 刷新历史记录列表
|
|
|
+ void queryClient.invalidateQueries({ queryKey: ['chatHistoryList'] })
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 更新历史
|
|
|
+const { mutate: collectHistory } = useMutation({
|
|
|
+ mutationFn: collectChatHistory,
|
|
|
+ onSuccess: (res) => {
|
|
|
+ console.log('collectHistory success:', res)
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 获取历史详情
|
|
|
+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)
|
|
|
}
|
|
|
})
|
|
|
|
|
|
const handleClear = (): void => {
|
|
|
- changeQuestion('', true)
|
|
|
+ changeQuestion('')
|
|
|
}
|
|
|
|
|
|
-const handleSend = (): void => {
|
|
|
- changeQuestion(textarea.value)
|
|
|
+const handleSend = (text): void => {
|
|
|
+ if ((text ?? '') === '') {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if ((currentQuestionId.value ?? '') === '') {
|
|
|
+ // 创建历史
|
|
|
+ createHistory(text)
|
|
|
+ }
|
|
|
textarea.value = ''
|
|
|
+ startChat({ query: text })
|
|
|
+ contents[text] = undefined
|
|
|
}
|
|
|
|
|
|
/*监控问题变化,开启问答*/
|
|
|
watch(
|
|
|
currentQuestion,
|
|
|
(newVal) => {
|
|
|
- const length = newVal?.length ?? 0
|
|
|
- const qText = newVal[length - 1]
|
|
|
- if (qText) {
|
|
|
- startChat({ query: qText })
|
|
|
- }
|
|
|
- if (length <= 1) {
|
|
|
- //首条对话, 重置对话记录
|
|
|
+ const { text, id } = newVal
|
|
|
+ if ((text ?? '') === '') {
|
|
|
+ //重置对话记录
|
|
|
Object.keys(contents).forEach((key) => {
|
|
|
delete contents[key]
|
|
|
})
|
|
|
+ } else {
|
|
|
+ handleSend(text)
|
|
|
+ }
|
|
|
+ if ((id ?? '') !== '') {
|
|
|
+ // 加载历史记录
|
|
|
}
|
|
|
},
|
|
|
{ deep: true }
|
|
@@ -59,14 +107,14 @@ watch(
|
|
|
<template>
|
|
|
<div class="chat-content">
|
|
|
<div class="message-body">
|
|
|
- <template v-if="(currentQuestion?.length ?? 0) > 0">
|
|
|
- <template v-for="question in currentQuestion" :key="question">
|
|
|
- <QChat>{{ question }}</QChat>
|
|
|
+ <template v-if="(Object.keys(contents)?.length ?? 0) > 0">
|
|
|
+ <template v-for="(value, key) in contents" :key="key">
|
|
|
+ <QChat>{{ key }}</QChat>
|
|
|
<!-- 思维链 -->
|
|
|
- <AChat v-if="contents?.[question] == null"> {{ data.content }}...</AChat>
|
|
|
+ <AChat v-if="value == null"> {{ data.content }}...</AChat>
|
|
|
<AChat v-else>
|
|
|
<!-- 回答结果 -->
|
|
|
- <AResult :data="contents[question]" />
|
|
|
+ <AResult :data="value" />
|
|
|
</AChat>
|
|
|
</template>
|
|
|
</template>
|
|
@@ -89,7 +137,7 @@ watch(
|
|
|
resize="none"
|
|
|
placeholder="请输入问题,可以通过回车换行"
|
|
|
/>
|
|
|
- <div class="q-btn" @click="handleSend">
|
|
|
+ <div class="q-btn" @click="handleSend(textarea)">
|
|
|
<img :src="sendImg" alt="发送" />
|
|
|
</div>
|
|
|
</div>
|