Jelajahi Sumber

feat: 打包构建

sunxiao 10 bulan lalu
induk
melakukan
66a7599027

+ 4 - 0
.env.production

@@ -0,0 +1,4 @@
+# 请求地址
+VITE_BASE_URL=http://10.0.0.28:8080
+# 请求前缀
+VITE_BASE_PREFIX='' 

+ 1 - 1
index.html

@@ -4,7 +4,7 @@
     <meta charset="UTF-8">
     <link rel="icon" href="/favicon.ico">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Vite App</title>
+    <title>LibraAI人工智能运营体</title>
   </head>
   <body>
     <div id="app"></div>

+ 64 - 0
package-lock.json

@@ -9,10 +9,12 @@
       "version": "0.0.0",
       "dependencies": {
         "@traptitech/markdown-it-katex": "^3.6.0",
+        "@vueuse/core": "^10.9.0",
         "axios": "^1.6.8",
         "highlight.js": "^11.9.0",
         "load-awesome": "^1.1.0",
         "markdown-it": "^14.1.0",
+        "markdown-it-latex": "^0.2.0",
         "markdown-it-link-attributes": "^4.0.1",
         "naive-ui": "^2.38.2",
         "pinia": "^2.1.7",
@@ -1407,6 +1409,11 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/web-bluetooth": {
+      "version": "0.0.20",
+      "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
+      "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
+    },
     "node_modules/@vitejs/plugin-vue": {
       "version": "5.0.4",
       "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==",
@@ -1699,6 +1706,33 @@
       "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
       "dev": true
     },
+    "node_modules/@vueuse/core": {
+      "version": "10.9.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-10.9.0.tgz",
+      "integrity": "sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==",
+      "dependencies": {
+        "@types/web-bluetooth": "^0.0.20",
+        "@vueuse/metadata": "10.9.0",
+        "@vueuse/shared": "10.9.0",
+        "vue-demi": ">=0.14.7"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/metadata": {
+      "version": "10.9.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.9.0.tgz",
+      "integrity": "sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA=="
+    },
+    "node_modules/@vueuse/shared": {
+      "version": "10.9.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.9.0.tgz",
+      "integrity": "sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==",
+      "dependencies": {
+        "vue-demi": ">=0.14.7"
+      }
+    },
     "node_modules/ansi-regex": {
       "version": "6.0.1",
       "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.0.1.tgz",
@@ -1817,6 +1851,11 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/asciimath-to-latex": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmmirror.com/asciimath-to-latex/-/asciimath-to-latex-0.3.2.tgz",
+      "integrity": "sha512-+80CDzm6nXdtsYwUdxBOLo7FA6ZO5CfRyYHgqLpUZgavPYqodXHamOQuLZJksV+4CUteJs1h7cBaF63d2qa97Q=="
+    },
     "node_modules/assign-symbols": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/assign-symbols/-/assign-symbols-1.0.0.tgz",
@@ -4150,11 +4189,36 @@
         "markdown-it": "bin/markdown-it.mjs"
       }
     },
+    "node_modules/markdown-it-latex": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmmirror.com/markdown-it-latex/-/markdown-it-latex-0.2.0.tgz",
+      "integrity": "sha512-vCaS6Dws9faA8lZel7Slfa1IYEnbYOjHzian/tDBulONBS+f9vRkFfQ4S0eFnTGtDTTXqE7zAnoezkv18b9IIA==",
+      "dependencies": {
+        "asciimath-to-latex": "^0.3.2",
+        "katex": "^0.9.0-alpha2"
+      }
+    },
+    "node_modules/markdown-it-latex/node_modules/katex": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmmirror.com/katex/-/katex-0.9.0.tgz",
+      "integrity": "sha512-lp3x90LT1tDZBW2tjLheJ98wmRMRjUHwk4QpaswT9bhqoQZ+XA4cPcjcQBxgOQNwaOSt6ZeL/a6GKQ1of3LFxQ==",
+      "dependencies": {
+        "match-at": "^0.1.1"
+      },
+      "bin": {
+        "katex": "cli.js"
+      }
+    },
     "node_modules/markdown-it-link-attributes": {
       "version": "4.0.1",
       "resolved": "https://registry.npmmirror.com/markdown-it-link-attributes/-/markdown-it-link-attributes-4.0.1.tgz",
       "integrity": "sha512-pg5OK0jPLg62H4k7M9mRJLT61gUp9nvG0XveKYHMOOluASo9OEF13WlXrpAp2aj35LbedAy3QOCgQCw0tkLKAQ=="
     },
+    "node_modules/match-at": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmmirror.com/match-at/-/match-at-0.1.1.tgz",
+      "integrity": "sha512-h4Yd392z9mST+dzc+yjuybOGFNOZjmXIPKWjxBd1Bb23r4SmDOsk2NYCU2BMUBGbSpZqwVsZYNq26QS3xfaT3Q=="
+    },
     "node_modules/mdn-data": {
       "version": "2.0.14",
       "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.14.tgz",

+ 2 - 0
package.json

@@ -12,10 +12,12 @@
   },
   "dependencies": {
     "@traptitech/markdown-it-katex": "^3.6.0",
+    "@vueuse/core": "^10.9.0",
     "axios": "^1.6.8",
     "highlight.js": "^11.9.0",
     "load-awesome": "^1.1.0",
     "markdown-it": "^14.1.0",
+    "markdown-it-latex": "^0.2.0",
     "markdown-it-link-attributes": "^4.0.1",
     "naive-ui": "^2.38.2",
     "pinia": "^2.1.7",

TEMPAT SAMPAH
public/favicon.ico


+ 3 - 2
src/api/chat.js

@@ -14,12 +14,13 @@ export const chatApi = {
 
   getAnswerHistoryDetail: params => http.get('/front/bigModel/qa/qaListBySessionId', { params }),
 
-  getChatStream: ({ data, onDownloadProgress }) => http.post('/grpc/inferStreamRag', data, { onDownloadProgress }),
+  getChatStream: ({ data, onDownloadProgress, signal }) => http.post('/grpc/inferStreamRag', data, { onDownloadProgress, signal }),
 
   getChatSessionTag: params =>http.get('/front/bigModel/chat/generateSessionId', { params }),
 
   getWelcomeRecommend: params => http.get('/front/bigModel/home/recommendQAList/' + params),
 
   deleteHistory: params => http.delete('/front/bigModel/chat/deleteOneChtById/' + params),
-}
 
+  putIsSatisfiedAnswer: data => http.put(`/front/bigModel/chat/isSatisfiedAnswer`, data),
+}

+ 1 - 0
src/assets/styles/github-markdown.scss

@@ -196,6 +196,7 @@ html {
 }
 
 .markdown-body img {
+  margin-top: 20px;
   border-style: none;
   max-width: 100%;
   box-sizing: content-box;

+ 2 - 3
src/components/BaseInput/index.vue

@@ -1,6 +1,6 @@
 <script setup>
 import { ref } from 'vue';
-import { NSelect, NInput, NSwitch } from 'naive-ui';
+import { NInput, NSwitch } from 'naive-ui';
 import SvgIcon from '@/components/SvgIcon';
 
 import 'load-awesome/css/ball-running-dots.min.css';
@@ -8,7 +8,6 @@ import 'load-awesome/css/ball-running-dots.min.css';
 const inputInstRef = ref(null);
 const inputValue = ref(null);
 const isFocusState = ref(false);
-const value = ref();
 
 const focusInput = _ => isFocusState.value = true;
 
@@ -28,7 +27,7 @@ const handleInpFocus = () => {
     </div>
     <div class="submit-btn">
       <button class="btn bg-[#1A2029] hover:bg-[#3C4148]">
-        <!-- <SvgIcon name="tool-send-plane" size="22"></SvgIcon> -->
+        <SvgIcon name="tool-send-plane" size="22"></SvgIcon>
           <div style="color: #fff" class="la-ball-running-dots la-sm">
             <div v-for="item in 5" :key="item"></div>
           </div>

+ 58 - 16
src/components/Chat/ChatAnswer.vue

@@ -1,14 +1,22 @@
 <script setup>
-import { computed, ref } from 'vue';
+import { computed, unref } from 'vue';
+import { useMessage } from 'naive-ui';
+import { useClipboard } from '@vueuse/core'
 import MarkdownIt from 'markdown-it';
 import hljs from 'highlight.js';
 import mila from 'markdown-it-link-attributes';
+// import markdownItLatex from 'markdown-it-latex'
 import mdKatex from '@traptitech/markdown-it-katex';
 import { SvgIcon } from '@/components';
+import { chatApi } from "@/api/chat"
 
-// import "highlight.js/styles/atom-one-light.min.css";
+// import 'markdown-it-latex/dist/index.css'
 
 const props = defineProps({
+  id: {
+    type: String || Number,
+    default: ''
+  },
   content: {
     type: String,
     default: ''
@@ -20,9 +28,18 @@ const props = defineProps({
   delayLoading: {
     type: Boolean,
     default: false
+  },
+  isSatisfied: {
+    type: Number,
+    default: 2
   }
 })
 
+const emit = defineEmits(['on-click-icon']);
+
+const { copy } = useClipboard();
+const message = useMessage();
+
 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>`
 }
@@ -44,6 +61,7 @@ const mdi = new MarkdownIt({
 
 mdi.use(mila, { attrs: { target: '_blank', rel: 'noopener' } })
 mdi.use(mdKatex, { blockClass: 'katexmath-block rounded-md p-[10px]', errorColor: ' #cc0000' })
+// mdi.use(markdownItLatex)
 
 const text = computed(() => {
   const value = props.content ?? ""
@@ -52,15 +70,28 @@ const text = computed(() => {
   return value
 })
 
+const handlLeToggleLike = async (state) => {
+  const { id } = unref(props);
+  const isSatisfied = props.isSatisfied === state ? 2 : state;
+  const params = { id, isSatisfied };
+  
+  await chatApi.putIsSatisfiedAnswer(params);
+
+  isSatisfied < 2 ? message.success('感谢您的反馈') : message.success('已取消反馈');
+
+  emit('on-click-icon', params)
+}
+
+const handleCopy = () => {
+  copy(props.content).then(() => {
+    message.success('复制成功');
+  })
+}
 </script>
 
 <template>
   <div class="answer-inner">
-    <div :class="[
-        'answer-card', 'px-[20px]', 'py-[20px]',
-        // loading && delayLoading ? 'pb-[20px]' : 'pb-[4px]'
-      ]"
-    >
+    <div :class="[ 'answer-card', 'px-[20px]', 'py-[20px]']">
       <div class="chat-answer_icon relative flex-shrink-0">
         <SvgIcon name="common-logo" class="chat-logo " size="30" :style="{ scale: loading ? 0 : 1 }" />
         <div style="color: #2454FF" class="la-ball-circus la-dark la-sm flex-shrink-0" v-show="loading">
@@ -71,35 +102,45 @@ const text = computed(() => {
         <template v-if="loading && delayLoading">
           <p class="font-bold text-[#1A2029] leading-[24px]">内容生成中...</p>
         </template>
-        <!-- <template > -->
           <div class="markdown-body text-[15px]" v-if="content">
             <div v-html="text"></div>
           </div>
-        <!-- </template> -->
         <template>
           <slot></slot>
         </template>
       </div>
     </div>
     <ul class="answer-btn-group" v-if="!loading">
-      <li class="btn">
+      <li class="btn" @click="handleCopy">
         <SvgIcon name="chat-icon-copy" size="16" />
       </li>
       <li class="line"></li>
-      <li class="btn">
-        <SvgIcon name="chat-icon-yes" size="16" />
+      <li :class="['btn', { btn_active: isSatisfied == 1 }]">
+        <SvgIcon name="chat-icon-yes" size="16" @click="handlLeToggleLike(1)" />
       </li>
       <li class="line"></li>
-      <li class="btn">
-        <SvgIcon name="chat-icon-no" size="16" />
+      <li :class="['btn', { btn_active: isSatisfied == 0 }]">
+        <SvgIcon name="chat-icon-no" size="16" @click="handlLeToggleLike(0)" />
       </li>
     </ul>
   </div>
 
 </template>
 
-
 <style lang="scss">
+.markdown-body p:last-child {
+  margin-bottom: 0;
+}
+
+.markdown-body  {
+  p:last-child {
+    margin-bottom: 0;
+  }
+  img {
+    margin-top: 20px;
+  }
+}
+
 .chat-logo {
   position: absolute;
   transition: all 1s;
@@ -125,10 +166,11 @@ const text = computed(() => {
       color: #89909B;
       cursor: pointer;
 
-      &:hover {
+      &:hover, &_active {
         background: #DBEFFF;
         color: #2454FF;
       }
+
     }
 
     .line {

+ 24 - 2
src/components/Chat/ChatInput.vue

@@ -6,7 +6,9 @@ import SvgIcon from '@/components/SvgIcon';
 import 'load-awesome/css/ball-running-dots.min.css';
 
 const emit = defineEmits(['onClick', 'onEnter']);
+
 const modelLoading = defineModel('loading');
+const switchStatus = defineModel('switch');
 
 const message = useMessage();
 
@@ -53,7 +55,15 @@ const handleInpEnter = (event) => {
 
 const handleBtnClick = () => {
   commonEmitEvent("onClick")
-} 
+}
+
+const clearInpVal = () => {
+  inpVal.value = '';
+}
+
+defineExpose({
+  clearInpVal
+})
 
 </script>
 <template>
@@ -82,9 +92,12 @@ const handleBtnClick = () => {
     </div>
   </div>
   <div class="switch-inner pt-[8px] space-x-[6px]">
-    <NSwitch size="small"></NSwitch>
+    <NSwitch size="small" v-model:value="switchStatus"></NSwitch>
     <span class="text-[12px] text-[#9E9E9E]">使用搜索增强</span>
   </div>
+  <div class="masking-inner">
+    
+  </div>
 </template>
 
 <style scoped lang="scss">
@@ -112,4 +125,13 @@ const handleBtnClick = () => {
     }
   }
 }
+
+.masking-inner {
+  position: absolute;
+  top: -30px;
+  left: 0;
+  width: 100%;
+  height: 30px;
+  background: linear-gradient(180deg, rgba(232, 241, 250, 0) 0%, #E7F0FA 95%);
+}
 </style>

+ 9 - 11
src/components/Layout/TheChatView.vue

@@ -47,15 +47,16 @@ defineExpose({ targetScrollDom });
           <span class="text-[#272D35] text-[12px]">我的昵称</span>
         </div>
       </div>
-      <main class="chat-main w-[800px] h-full m-auto flex flex-col justify-between">
+      <main class="chat-main h-full m-auto flex flex-col justify-between">
         <div class="chat-scroll" ref="targetScrollDom">
-          <slot></slot>
-        </div>
-
-        <div class="chat-footer">
-          <slot name="footer"/>
+          <div class="w-[800px] m-auto pb-[20px]">
+            <slot></slot>
+          </div>
         </div>
       </main>
+      <footer class="chat-footer relative w-[800px] m-auto pb-[30px]">
+        <slot name="footer"/>
+      </footer>
     </div>
   </div>
 </template>
@@ -63,13 +64,14 @@ defineExpose({ targetScrollDom });
 <style scoped lang="scss">
 .chat-container {
   padding: 20px 20px 20px 0;
+  overflow: hidden;
 
   .chat-wrapper {
     border: 1px solid #fff;
     background: linear-gradient(180deg, rgba(238, 253, 255, 0.5) 0%, rgba(231, 243, 252, 0.5) 100%);
 
     .chat-main {
-      height: calc(100% - 82px);
+      height: calc(100% - 212px);
       color: #1A2029;
 
       .chat-scroll {
@@ -87,10 +89,6 @@ defineExpose({ targetScrollDom });
           overflow-y: scroll;
         }
       }
-
-      .chat-footer {
-        padding-bottom: 30px;
-      }
     }
   }
 }

+ 7 - 2
src/composables/useChat.js

@@ -19,7 +19,11 @@ export const useChat = () => {
 
   const clearChat = () => {
     chatDataSource.value = [];
-    console.log("clearChat", clearChat);
+  }
+
+  const updateById = params => {
+    const i = chatDataSource.value.findIndex(({ id }) => id === params.id);
+    chatDataSource.value[i] = { ...chatDataSource.value[i], ...params};
   }
 
   return {
@@ -27,6 +31,7 @@ export const useChat = () => {
     createChat,
     addChat,
     updateChat,
-    clearChat
+    clearChat,
+    updateById
   }
 }

+ 13 - 8
src/utils/request.ts

@@ -52,13 +52,12 @@ export class Request {
 
     this.instance.interceptors.request.use((config: InternalAxiosRequestConfig<Result>) => {
       const { token } = useStore.userInfo;
-
-      if (config.method === "get" && config.params) {
-        let url = config.url + '?' + tansParams(config.params);
-        url = url.slice(0, -1);
-        config.params = {};
-        config.url = url;
-      }
+      // if (config.method === "get" && config.params) {
+      //   let url = config.url + '?' + tansParams(config.params);
+      //   url = url.slice(0, -1);
+      //   config.params = {};
+      //   config.url = url;
+      // }
       
       token && (config.headers.Authorization = 'Bearer ' + token);
 
@@ -76,8 +75,14 @@ export class Request {
       return code === 200 ? res.data : Promise.reject(res.data);
 
     }, (error: AxiosError) => {
+      // if (error.code === 'ERR_ABORTED') {
+      //   console.log( "取消了请求", error.code );
+      //   return
+      // }
+      // error.code === 'ERR_ABORTED'
+      // console.log("取消了请求");
       const errorMessage = errorCode[error.response?.status as number] || error.message ||'未知错误';
-      showNotification("error", errorMessage);
+      // showNotification("error", errorMessage);
       return Promise.reject(error);
     })
   }

+ 23 - 0
src/utils/tools.ts

@@ -71,4 +71,27 @@ export const getQueryParamsAsObject = (url?: string) => {
   });
 
   return paramsObj;
+}
+
+/**
+ * 复制文本
+* @param options
+ */
+export function copyText(options: { text: string; origin?: boolean }) {
+  const props = { origin: true, ...options }
+
+  let input: HTMLInputElement | HTMLTextAreaElement
+
+  if (props.origin) {
+    input = document.createElement('textarea')
+  } else {
+    input = document.createElement('input')
+  }
+  input.setAttribute('readonly', 'readonly')
+  input.value = props.text
+  document.body.appendChild(input)
+  input.select()
+  if (document.execCommand('copy'))
+    document.execCommand('copy')
+  document.body.removeChild(input)
 }

+ 51 - 15
src/views/answer/AnswerView.vue

@@ -10,15 +10,22 @@ import { useScroll } from '@/composables/useScroll';
 import { useChat } from '@/composables/useChat';
 import { useRecommend } from '@/composables/useRecommend';
 
+const ANSWER_ID_KEY = '@@id@@';
+
+let controller = new AbortController();
+
 // TODO: 如果这里的key不一样,将会在拆一层组件出来 - list
 const { recordList, isFetching, onScrolltolower, onReset, addHistoryRecord } = useInfinite({model: 0});
 const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
-const { chatDataSource, addChat, updateChat, clearChat } = useChat();
+const { chatDataSource, addChat, updateChat, clearChat, updateById } = useChat();
 const { recommendList } = useRecommend({type: 0});
 
 const message = useMessage();
 
+const switchActive = ref(true);
+
 const isLoading = ref(false);
+const inputRef = ref(null);
 
 const currenSessionId = ref(null);
 
@@ -35,35 +42,44 @@ const handleCreateDialog = async () => {
   if (!unref(chatDataSource).length) {
     return message.info('已切换最新会话');
   }
-  
+
+  inputRef.value.clearInpVal();
+
+  currenSessionId.value = null;
+
   clearChat();
 }
 
 // 查询对话详情
 const handleChatDetail = async ({ sessionId }) => {
   isLoading.value = false;
+
+  controller.abort();
+
   const { data } = await chatApi.getAnswerHistoryDetail({ sessionId });
-  chatDataSource.value = data.map(({ createTime, sessionId, question, answer }) => ({ 
-    createTime, 
-    sessionId, 
-    question, 
-    answer, 
-    loading: false 
-  }));
+
+  chatDataSource.value = data.map(item => ({ ...item, loading: false,  }));
   currenSessionId.value = sessionId;
+
   scrollToBottom();
 }
 
 const onRegenerate = async ({ question, realQuestion }) => {
+  controller = new AbortController();
+
   const sessionId = unref(currenSessionId);
   const params = {
     data: {
       sessionId,
       question: realQuestion || question,
+      module: 0,
+      isStrong: Number(unref(switchActive))
     },
+    signal: controller.signal,
     onDownloadProgress: ({ event }) => {
       const xhr = event.target;
-      const { responseText: answer } = xhr;
+      const { responseText } = xhr;
+      const [ answer ] = responseText.split(ANSWER_ID_KEY);
 
       updateChat({
         sessionId,
@@ -78,8 +94,12 @@ const onRegenerate = async ({ question, realQuestion }) => {
   }
 
   try {
-    const { data: answer } = await chatApi.getChatStream(params);
+    const { data } = await chatApi.getChatStream(params);
+
+    const [ answer, id ] = data.split(ANSWER_ID_KEY);
+
     updateChat({
+      id,
       sessionId,
       question,
       answer,
@@ -87,8 +107,12 @@ const onRegenerate = async ({ question, realQuestion }) => {
       delayLoading: false
     })
   }
+  catch {
+    console.log("取消了请求 - catch");
+  }
   finally {
     isLoading.value = false;
+    onReset();
   }
 }
 
@@ -100,7 +124,7 @@ const handleSubmit = async (question, realQuestion = '') => {
     const { data: sessionId } = await chatApi.getChatSessionTag();
     currenSessionId.value = sessionId;
   }
-
+  
   isLoading.value = true;
 
   addChat({
@@ -134,7 +158,6 @@ const handeChatDelete = async (id) => {
 <template>
   <section class="flex items-start h-full">
     <TheSubMenu title="历史记录" @scrollToLower="onScrolltolower" :loading="isFetching">
-
       <template #top>
         <div class="create-btn px-[11px] pb-[22px]">
           <BaseButton @click="handleCreateDialog">新建对话</BaseButton>
@@ -168,12 +191,25 @@ const handeChatDelete = async (id) => {
       <div class="conversation-item" v-if="chatDataSource.length">
         <template v-for="item in chatDataSource" :key="item.id">
           <ChatAsk :content="item.question"></ChatAsk>
-          <ChatAnswer :content="item.answer" :loading="item.loading" :delay-loading="item.delayLoading"></ChatAnswer>
+          <ChatAnswer
+            :id="item.id"
+            :content="item.answer"
+            :loading="item.loading"
+            :delay-loading="item.delayLoading"
+            :isSatisfied="item.isSatisfied"
+            @on-click-icon=" params => updateById(params)"
+          ></ChatAnswer>
         </template>
       </div>
 
       <template #footer>
-        <ChatInput @on-click="handleSubmit" @on-enter="handleSubmit" v-model:loading="isLoading"></ChatInput>
+        <ChatInput
+          ref="inputRef"
+          v-model:loading="isLoading"
+          v-model:switch="switchActive"
+          @on-click="handleSubmit"
+          @on-enter="handleSubmit"
+        ></ChatInput>
       </template>
     </TheChatView>
   </section>