Parcourir la source

feat: 专家问答 - 新建对话

sunxiao il y a 9 mois
Parent
commit
b0bd423b46

+ 5 - 1
src/api/chat.js

@@ -14,6 +14,10 @@ export const chatApi = {
 
   getAnswerHistoryDetail: params => http.get('/front/bigModel/qa/qaListBySessionId', { params }),
 
-  getChatStream: ({ data, onDownloadProgress }) =>  http.post('/grpc/inferStreamRag', data, { onDownloadProgress }),
+  getChatStream: ({ data, onDownloadProgress }) => http.post('/grpc/inferStreamRag', data, { onDownloadProgress }),
+
+  getChatSessionTag: params =>http.get('/front/bigModel/chat/generateSessionId', { params }),
+
+  getWelcomeRecommend: params => http.get('/front/bigModel/home/recommendQAList/' + params),
 }
 

+ 3 - 0
src/assets/styles/common.scss

@@ -25,6 +25,9 @@
   background: #fff;
 }
 
+.code-block-wrapper {
+  padding: 10px 0;
+}
 
 // pre code,
 // pre tt {

+ 4 - 3
src/assets/styles/github-markdown.scss

@@ -98,10 +98,10 @@ html {
   -ms-text-size-adjust: 100%;
   -webkit-text-size-adjust: 100%;
   margin: 0;
-  color: var(--color-fg-default);
+  // color: var(--color-fg-default);
   background-color: var(--color-canvas-default);
   font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";
-  font-size: 16px;
+  // font-size: 16px;
   line-height: 1.5;
   word-wrap: break-word;
 }
@@ -154,7 +154,8 @@ html {
 
 .markdown-body b,
 .markdown-body strong {
-  font-weight: var(--base-text-weight-semibold, 600);
+  font-size: 15px;
+  font-weight: bold;
 }
 
 .markdown-body dfn {

+ 2 - 2
src/assets/styles/highlight.scss

@@ -107,7 +107,7 @@ html.dark {
 	}
 
 	.hljs-strong {
-		font-weight: 700
+		// font-weight: 700
 	}
 
 	.hljs-link {
@@ -197,7 +197,7 @@ html {
 	}
 
 	.hljs-strong {
-		font-weight: 700
+		// font-weight: 700
 	}
 
 	.hljs-link {

+ 2 - 1
src/assets/styles/index.scss

@@ -2,4 +2,5 @@
 @import "./variables.scss";
 @import "reset.css";
 @import "common.scss";
-// @import "./highlight.scss";
+@import "./github-markdown.scss";
+@import "./highlight.scss";

+ 0 - 1
src/assets/styles/reset.css

@@ -64,7 +64,6 @@ h4,
 h5,
 h6 {
   font-size: 100%;
-  background: red;
 }
 
 address,

+ 7 - 18
src/components/Chat/ChatAnswer.vue

@@ -6,8 +6,7 @@ import mila from 'markdown-it-link-attributes';
 import mdKatex from '@traptitech/markdown-it-katex';
 import { SvgIcon } from '@/components';
 
-import "highlight.js/styles/atom-one-light.min.css"
-// import "@/assets/styles/github-markdown.scss"
+// import "highlight.js/styles/atom-one-light.min.css";
 
 const props = defineProps({
   content: {
@@ -23,21 +22,10 @@ const props = defineProps({
     default: false
   }
 })
-{/* <div class="code-block-header">
-        <span class="code-block-header__lang">${lang}</span>
-        <span class="code-block-header__copy">复制</span>
-      </div> */}
+
 function highlightBlock(str, lang) {
   return `<pre class="code-block-wrapper"><div class="code-block-header"><span class="code-block-header__copy"></span></div><code class="hljs code-block-body ${lang}">${str}</code></pre>`
 }
-// const highlightBlock = (str, lang) => {
-//   return `
-
-//     <pre class="code-block-wrapper">
-
-//       <code class="hljs code-block-body ${lang}">${str}</code>
-//     </pre>`
-// }
 
 const mdi = new MarkdownIt({
   html: true,
@@ -75,13 +63,14 @@ const text = computed(() => {
           <div v-for="item in 5" :key="item"></div>
         </div>
       </div>
-      <div class="flex-1 pt-[4px] ml-[16px] text-[15px] leading-[24px]">
+      <div class="flex-1 pt-[4px] ml-[16px] text-[15px]">
         <template v-if="loading && delayLoading">
-          <p class="font-bold text-[#1A2029]">内容生成中...</p>
+          <p class="font-bold text-[#1A2029] leading-[24px]">内容生成中...</p>
         </template>
         <!-- <template > -->
-          <p class="ml-[16px] text-[15px] leading-[24px]" v-html="text" v-if="content">
-          </p>
+          <div class="markdown-body text-[15px]" v-if="content">
+            <div v-html="text"></div>
+          </div>
         <!-- </template> -->
         <template>
           <slot></slot>

+ 7 - 2
src/components/ChatWelcome/index.vue

@@ -19,6 +19,12 @@ defineProps({
     default: []
   }
 });
+
+const emit = defineEmits(['on-click']);
+
+const handleEmit = ({ content: question }) => {
+  emit('on-click', question);
+}
 </script>
 
 <template>
@@ -35,7 +41,6 @@ defineProps({
     <dl class="answer-list rounded-[8px] bg-white py-[30px] pl-[82px] mt-[36px]">
       <dt class="mb-[18px] text-[20px] text-[#1A2029] leading-[28px] font-bold">{{ cardTitle }}</dt>
       <dd
-        class=""
         :class="[
           'text-[15px]', 'text-[#2E5CFF]', 'leading-[21px]', 'cursor-pointer', 'hover:text-[#2E5CFF]/90',
           {'mb-[19px]': index !== cardContent.length - 1 }
@@ -43,7 +48,7 @@ defineProps({
         :key="index"
         v-for="(item, index) in cardContent"
       >
-        <span>{{ item }}</span>
+        <span @click="handleEmit(item)">{{ item.question }}</span>
       </dd>
     </dl>
   </div>

+ 12 - 1
src/composables/useChat.js

@@ -3,6 +3,10 @@ import { ref, unref } from 'vue';
 export const useChat = () => {
   const chatDataSource = ref([]);
 
+  const createChat = chat => {
+    chatDataSource.value = [chat];
+  }
+
   const addChat = chat => {
     chatDataSource.value.push(chat);
   }
@@ -13,9 +17,16 @@ export const useChat = () => {
     chatDataSource.value[index] = chat;
   }
 
+  const clearChat = () => {
+    chatDataSource.value = [];
+    console.log("clearChat", clearChat);
+  }
+
   return {
     chatDataSource,
+    createChat,
     addChat,
-    updateChat
+    updateChat,
+    clearChat
   }
 }

+ 6 - 1
src/composables/useInfinite.js

@@ -8,6 +8,10 @@ export const useInfinite = props => {
   const isFetching = ref(false);
   const noMore = ref(false);
 
+  const addHistoryRecord = record => {
+    recordList.value.unshift(...record);
+  }
+
   const onScrolltolower = async () => {
     if(unref(isFetching) || unref(noMore)) return;
 
@@ -31,7 +35,8 @@ export const useInfinite = props => {
   return {
     recordList,
     isFetching,
-    onScrolltolower
+    onScrolltolower,
+    addHistoryRecord
   }
 }
 //   {

+ 16 - 0
src/composables/useRecommend.js

@@ -0,0 +1,16 @@
+import { ref, onMounted } from 'vue';
+import { chatApi } from '@/api/chat';
+
+export const useRecommend = ({ type }) => {
+
+  const recommendList = ref([]);
+
+  onMounted(async () => {
+    const { data } = await chatApi.getWelcomeRecommend(type);
+    recommendList.value = data;
+  })
+
+  return {
+    recommendList
+  }
+}

+ 1 - 1
src/utils/request.ts

@@ -119,5 +119,5 @@ export class Request {
 
 export default new Request({
   baseURL,
-  timeout: 10 * 1000
+  timeout: 120 * 1000
 });

+ 44 - 21
src/views/answer/AnswerView.vue

@@ -1,6 +1,6 @@
 <script setup>
-import { ref, unref, onMounted } from 'vue';
-import { NPopconfirm } from 'naive-ui';
+import { ref, unref, computed, onMounted } from 'vue';
+import { useMessage } from 'naive-ui';
 import { SvgIcon, BaseButton, RecodeCardItem, TheSubMenu, TheChatView, ChatWelcome } from '@/components';
 import { ChatAsk, ChatAnswer, ChatInput } from '@/components/Chat';
 import { chatApi } from '@/api/chat';
@@ -8,20 +8,40 @@ import { chatApi } from '@/api/chat';
 import { useInfinite } from '@/composables/useInfinite';
 import { useScroll } from '@/composables/useScroll';
 import { useChat } from '@/composables/useChat';
+import { useRecommend } from '@/composables/useRecommend';
 
 // TODO: 如果这里的key不一样,将会在拆一层组件出来 - list
-const { recordList, isFetching, onScrolltolower } = useInfinite({model: 0});
+const { recordList, isFetching, onScrolltolower, addHistoryRecord } = useInfinite({model: 0});
 const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
-const { chatDataSource, addChat, updateChat } = useChat();
+const { chatDataSource, addChat, updateChat, clearChat } = useChat();
+const { recommendList } = useRecommend({type: 0});
+
+const message = useMessage();
 
-// 控制按钮
 const isLoading = ref(false);
 
 const currenSessionId = ref(null);
 
+const isExistInHistory = computed(() => !(recordList.value.findIndex(({ sessionId: sId }) => sId === unref(currenSessionId)) === -1));
+
 // 新建对话
-const handleCreateDialog = () => {
-  console.log("handleCreateDialog");
+const handleCreateDialog = async () => {
+  message.destroyAll();
+
+  if (unref(isLoading)) {
+    return message.warning('当前对话生成中');
+  }
+
+  if (!unref(chatDataSource).length) {
+    return message.info('已切换最新会话');
+  }
+  
+  clearChat();
+}
+
+// 处理推荐问题
+const handleWelcomeRecommend = question => {
+  
 }
 
 // 查询对话详情
@@ -35,12 +55,13 @@ const handleChatDetail = async ({ sessionId }) => {
 const onRegenerate = async ({ question, sessionId }) => {
   const params = {
     data: {
-      sessionId, 
-      question, 
+      sessionId,
+      question,
     },
     onDownloadProgress: ({ event }) => {
       const xhr = event.target;
       const { responseText: answer } = xhr;
+
       updateChat({
         sessionId,
         question,
@@ -48,12 +69,12 @@ const onRegenerate = async ({ question, sessionId }) => {
         loading: true,
         delayLoading: false
       })
+
       scrollToBottomIfAtBottom();
     }
   }
 
   try {
-    isLoading.value = true;
     const { data: answer } = await chatApi.getChatStream(params);
     updateChat({
       sessionId,
@@ -67,10 +88,18 @@ const onRegenerate = async ({ question, sessionId }) => {
     isLoading.value = false;
   }
 }
+
 // 提交问题
 const handleSubmit = async (question) => {
-  // 此步骤用于模拟 - 内容生成前置等待状态
-  const sessionId = unref(currenSessionId.value);
+  // 用于模拟 - 内容生成前置等待状态
+  const sessionId = unref(currenSessionId);
+
+  if (unref(isExistInHistory)) {
+    const { data: sessionId } = await chatApi.getChatSessionTag();
+    currenSessionId.value = sessionId;
+  }
+
+  isLoading.value = true;
 
   addChat({
     sessionId,
@@ -82,10 +111,7 @@ const handleSubmit = async (question) => {
 
   scrollToBottom();
 
-  setTimeout(() => {
-    onRegenerate({ question, sessionId })
-  }, 2 * 1000)
-  
+  setTimeout(() => onRegenerate({ question, sessionId }), 2 * 1000);
 }
 </script>
 
@@ -117,12 +143,9 @@ const handleSubmit = async (question) => {
         :sub-title="[
           '期待与您一同规划和完成未来的工作。有任何重点或需讨论的事项,随时告诉我。'
         ]" 
-        :card-content="[
-          '帮我做一份如何快速入手污水处理厂的相关工作的学习计划?',
-          '硝化作用的速度快慢与哪些因素有关?',
-          '污泥回流比如何计算?'
-        ]"
+        :card-content="recommendList"
         v-if="!chatDataSource.length"
+        @on-click="handleWelcomeRecommend"
       />
 
       <div class="conversation-item" v-if="chatDataSource.length">