From 7789aeaad9032763805da324d743bc664bddd2e8 Mon Sep 17 00:00:00 2001
From: xiangpei <xiangpei@timesnew.cn>
Date: 星期五, 18 四月 2025 17:17:26 +0800
Subject: [PATCH] 改为对接java而不是直接调langchain

---
 src/components/AiChat.vue |  323 +++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 258 insertions(+), 65 deletions(-)

diff --git a/src/components/AiChat.vue b/src/components/AiChat.vue
index 41a0fd7..486e8cf 100644
--- a/src/components/AiChat.vue
+++ b/src/components/AiChat.vue
@@ -1,80 +1,148 @@
 <template>
-  <div style="position: relative;height: 800px;width: 100%;display: flex;justify-content: center">
+  <div style="height:calc(100vh - 20px);width: 100%;display: flex;justify-content: center">
     <!-- 鑱婂ぉ娑堟伅鍒楄〃 -->
     <div class="chat-messages">
       <div
           v-for="(message, index) in messages"
-          :key="index"
+          :key="message + index"
           :class="['message', message.role]"
+          @mouseover="handleMouseEnter(message)"
       >
         <div class="avatar">
           <img :src="getAvatar(message.role)" alt="avatar" />
         </div>
-        <div class="content">
-<!--          <div class="name">{{ getRoleName(message.role) }}</div>-->
-          <div class="text">{{ message.content }}</div>
+        <div v-if="!message.content && message.role == 'assistant'" v-loading="!message.content && message.role == 'assistant'">
+
+        </div>
+        <div v-else class="content">
+          <div class="text" :ref="'msg' + index" v-html="getHtml(message.content)"></div>
+          <div v-if="message.role === 'assistant'">
+            <div v-show="msgIndex === message" class="msg-op-list">
+              <el-button class="copy" v-show="false"/>
+              <el-tooltip class="item" effect="dark" content="澶嶅埗" placement="top">
+                <i class="el-icon-copy-document msg-op msg-copy" @click="copyText(message.content)"/>
+              </el-tooltip>
+              <el-tooltip class="item" effect="dark" content="閲嶆柊鐢熸垚" placement="top">
+                <i class="el-icon-refresh msg-op msg-re" @click="reAnswer"/>
+              </el-tooltip>
+            </div>
+          </div>
+        </div>
+        <div v-if="message.role === 'user'">
+          <div v-show="msgIndex === message" style="display: flex">
+            <el-tooltip class="item" effect="dark" content="澶嶅埗" placement="top">
+              <i class="el-icon-copy-document msg-op msg-copy" @click="copyText(message.content)"/>
+            </el-tooltip>
+          </div>
         </div>
       </div>
     </div>
 
     <!-- 杈撳叆妗� -->
     <div class="chat-input">
+      <div style="position: relative;width: 800px;box-sizing: border-box;">
         <el-input
             v-model="inputMessage"
+            type="textarea"
+            resize="none"
+            :autofocus="true"
+            :autosize="true"
             placeholder="璇疯緭鍏ユ秷鎭紝鎸変笅鍥炶溅鍙戦��"
             @keyup.native.enter="sendMessage"
+            @keydown.enter.native.prevent="handleEnterKey"
         >
-          <div slot="append">
-            <el-button type="primary" @click="sendMessage" icon="el-icon-thumb"></el-button>
-          </div>
         </el-input>
-<!--      <div class="send-but" @click="sendMessage">-->
-<!--        <svg t="1742455190051" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1160" width="32" height="32"><path d="M417.016025 991.681197h266.763504l286.972859-930.353884z" fill="#6E6E96" opacity=".2" p-id="1161"></path><path d="M318.168022 667.620127l106.879202 307.873376L985.008068 34.974312z" fill="#E5C0A8" p-id="1162"></path><path d="M425.051265 993.681924a18.180337 18.180337 0 0 1-17.186036-12.222619l-106.87516-307.873376a18.188421 18.188421 0 0 1 4.664319-19.15847L972.498476 21.777602a18.184379 18.184379 0 0 1 28.14355 22.497056L440.67714 984.797891a18.192463 18.192463 0 0 1-15.625875 8.884033z m-85.849345-320.940746l90.210525 259.868071L902.064828 138.741273 339.20192 672.741178z" fill="#6E6E96" p-id="1163"></path><path d="M51.287304 548.991206L985.008068 34.974312 318.168022 667.620127z" fill="#FFDAC1" p-id="1164"></path><path d="M318.16398 685.808548c-2.497876 0-5.01192-0.513318-7.384499-1.568246l-266.876675-118.628922a18.188421 18.188421 0 0 1-10.783713-15.848177 18.204588 18.204588 0 0 1 9.401393-16.713137L976.237207 19.041256a18.188421 18.188421 0 1 1 21.292578 29.129766L330.689739 680.816837a18.184379 18.184379 0 0 1-12.525759 4.991711z m-226.005273-138.555347l222.302919 98.811627L869.915784 119.097779 92.158707 547.253201z" fill="#6E6E96" p-id="1165"></path><path d="M985.008068 34.974312L425.281652 711.656315l429.48924 166.755482z" fill="#FFDAC1" p-id="1166"></path><path d="M854.770892 896.60426a18.196504 18.196504 0 0 1-6.584208-1.232771l-429.48924-166.759524a18.176295 18.176295 0 0 1-7.42896-28.551779L970.998942 23.382225a18.196504 18.196504 0 0 1 31.995453 14.368853l-130.245259 843.445569a18.224798 18.224798 0 0 1-8.87595 12.978448 18.325844 18.325844 0 0 1-9.102294 2.429165z m-399.409634-192.781092l384.887191 149.44819L956.969607 97.417181 455.361258 703.823168z" fill="#6E6E96" p-id="1167"></path><path d="M425.281652 993.681924a18.192463 18.192463 0 0 1-18.188421-18.188421v-263.837188a18.188421 18.188421 0 0 1 36.376842 0v263.837188a18.188421 18.188421 0 0 1-18.188421 18.188421z" fill="#6E6E96" p-id="1168"></path><path d="M985.008068 34.974312L518.147686 728.203736l-62.786428-24.380568zM417.016025 711.656315l78.937746 109.18711 32.504729-54.593555z" fill="#6E6E96" opacity=".2" p-id="1169"></path></svg>-->
-<!--      </div>-->
+        <div class="send-op-list">
+          <div class="send-warp">
+            <div class="add-option">
+              <div :class="{'add-but': true, 'add-but-active': netSearchEnable}" @click="changeNetEnable">
+                <svg t="1743144032689" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2731" width="18" height="18">
+                  <path fill="#575757" d="M909.989 343.281c-21.76-51.446-52.904-97.643-92.568-137.307-39.663-39.664-85.86-70.808-137.306-92.568-53.28-22.536-109.858-33.962-168.164-33.962S397.067 90.87 343.787 113.406c-51.446 21.76-97.643 52.904-137.307 92.568-39.664 39.664-70.808 85.86-92.568 137.307-22.536 53.28-33.962 109.858-33.962 168.164s11.426 114.884 33.962 168.164c21.76 51.445 52.904 97.643 92.568 137.306 39.664 39.664 85.86 70.809 137.307 92.568 53.28 22.535 109.858 33.962 168.164 33.962s114.884-11.427 168.164-33.962c51.445-21.76 97.643-52.904 137.306-92.568 39.664-39.663 70.809-85.86 92.568-137.306 22.535-53.28 33.962-109.858 33.962-168.164s-11.427-114.884-33.962-168.164zM543.951 376.03c17.57-0.243 35.095-0.683 52.536-1.368a1990.762 1990.762 0 0 0 91.131-5.678c10.157 34.378 16.643 71.314 19.389 110.46H543.951V376.03z m0-64.069V163.41c17.617 12.176 39.372 29.845 61.074 54.197 23.42 26.28 43.109 56.138 58.784 89.019-41.937 3.147-82.117 4.783-119.858 5.335z m-64-144.763v144.705a2038.572 2038.572 0 0 1-103.41-4.309 1929.6 1929.6 0 0 1-11.183-0.786c15.195-31.915 34.175-60.991 56.676-86.711 20.284-23.185 40.722-40.49 57.917-52.899zM370.842 371.354a2124.301 2124.301 0 0 0 109.109 4.606v103.484H322.25c2.742-39.079 9.208-75.96 19.335-110.285 9.453 0.776 19.21 1.512 29.257 2.195z m-112.731 108.09H145.342c3.916-45.757 16.208-89.778 36.139-130.276 20.257 3.578 52.836 8.759 95.407 13.641-9.902 36.699-16.182 75.682-18.777 116.635z m-0.859 64c1.975 39.144 9.712 78.242 23.101 116.799-44.093 4.976-77.797 10.319-98.621 13.992-20.08-40.64-32.458-84.841-36.39-130.792h111.91z m64.098 0h158.601v104.048c-36.37 0.602-72.818 2.13-109.109 4.601-8.41 0.573-16.612 1.182-24.609 1.821-14.322-36.523-22.659-73.543-24.883-110.47z m158.601 168.103v131.712c-16.974-15.877-36.574-35.962-56.038-59.735-18.064-22.064-33.892-44.698-47.379-67.668a2038.38 2038.38 0 0 1 103.417-4.309z m64 136.665V711.487c34.387 0.503 70.782 1.903 108.68 4.527-13.467 22.915-29.263 45.496-47.286 67.509-21.547 26.32-43.262 48.119-61.394 64.689z m52.536-199.426a2063.187 2063.187 0 0 0-52.536-1.384V543.444h163.958c-2.227 36.975-10.582 74.045-24.939 110.615a1994.972 1994.972 0 0 0-86.483-5.273z m175.521-105.342H878.56c-3.928 45.898-16.283 90.051-36.322 130.652a1822.84 1822.84 0 0 0-93.476-13.439c13.478-38.691 21.264-77.929 23.246-117.213z m-0.859-64c-2.605-41.111-8.924-80.238-18.891-117.061a1821.491 1821.491 0 0 0 90.233-13.075c19.89 40.46 32.158 84.432 36.07 130.136H771.149z m1.017-228.215a373.188 373.188 0 0 1 34.046 39.155 1772.853 1772.853 0 0 1-75.117 9.98c-19.529-46.785-45.825-88.91-78.291-125.338a423.863 423.863 0 0 0-5.386-5.927c46.185 18.263 88.573 45.955 124.748 82.13zM384.81 165.923a420.788 420.788 0 0 0-8.355 9.102c-32.552 36.525-58.904 78.775-78.449 125.71-32.357-3.484-59.48-7.244-80.226-10.469a373.386 373.386 0 0 1 33.956-39.037c38.338-38.338 83.654-67.152 133.074-85.306zM251.736 771.659a373.444 373.444 0 0 1-33.584-38.548c22.526-3.498 52.529-7.614 88.626-11.328 18.229 35.542 41.31 70.356 68.867 103.808a678.068 678.068 0 0 0 35.727 39.995c-59.757-16.875-114.524-48.814-159.636-93.927z m368.249 91.745a677.032 677.032 0 0 0 33.629-37.814c27.469-33.344 50.487-68.043 68.689-103.466a1780.315 1780.315 0 0 1 83.528 10.88 373.34 373.34 0 0 1-33.665 38.654c-43.23 43.232-95.324 74.373-152.181 91.746z" p-id="2732"></path></svg>
+                鑱旂綉鎼滅储
+              </div>
+            </div>
+            <div class="send-but-warp">
+              <div class="send-but" @click="sendMessage">
+                <svg t="1743144485359" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1261" width="36" height="36">
+                  <path fill="#409eff" d="M512 85.333333c234.666667 0 426.666667 192 426.666667 426.666667s-192 426.666667-426.666667 426.666667S85.333333 746.666667 85.333333 512 277.333333 85.333333 512 85.333333z m-6.4 234.666667c-4.266667 2.133333-6.4 2.133333-12.8 8.533333l-153.6 153.6c-12.8 12.8-12.8 32 0 44.8 12.8 12.8 32 12.8 44.8 0l96-96V682.666667c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V430.933333l96 96c12.8 12.8 32 12.8 44.8 0s12.8-32 0-44.8l-153.6-153.6c-6.4-6.4-8.533333-8.533333-12.8-8.533333s-8.533333-2.133333-12.8 0z" p-id="1262"></path>
+                </svg>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
     </div>
   </div>
 </template>
 
 <script>
-import {sendKbMsg} from "@/api/chat";
+import ClipboardJS from 'clipboard';
+import {Message} from "element-ui";
+const markdownIt = require('markdown-it')();
 
 export default {
   name: 'AiChat',
   data() {
     return {
-      messages: [
-        {
-          role: 'assistant',
-          content: '浣犲ソ锛佹垜鏄綘鐨� AI 鍔╂墜锛屾湁浠�涔堝彲浠ュ府浣犵殑鍚楋紵',
-        },
-      ],
+      chatId: null,
+      messages: [], // 鐢ㄤ簬椤甸潰灞曠ず鐨勫璇濆垪琛�
+      netSearchEnable: false,
       inputMessage: '',
       sendMsgForm: {
         query: "",
         mode: "local_kb",
-        kb_name: "samples",
-        top_k: 3,
-        score_threshold: 2,
+        kbName: "SouthWest_Neclear_Develepment_KB",
+        topK: 3,
+        scoreThreshold: 2,
         history: [
-          {
-            "content": "鎴戜滑鏉ョ帺鎴愯鎺ラ緳锛屾垜鍏堟潵锛岀敓榫欐椿铏�",
-            "role": "user"
-          },
-          {
-            "content": "铏庡ご铏庤剳",
-            "role": "assistant"
-          }
+
         ],
         stream: true,
-        model: "qwen:7b",
-        temperature: 0.7,
-        max_tokens: 0,
-        prompt_name: "default",
-        return_direct: false
-      }
+        model: "Qwen25-32B-Instruct",
+        temperature: 1.15,
+        maxTokens: 512,
+        promptName: "default",
+        returnDirect: false
+      },
+      msgIndex: null,
+      msgRole: null,
+      chunkRef: null, // ai鍥炵瓟涓存椂鏁版嵁
+      haveThinkStart: false, // ai鍥炵瓟鍝嶅簲鏁版嵁鏄惁鏈塼hink鏍囩
+      haveThinkEnd: false, // ai鍥炵瓟鍝嶅簲鏁版嵁鏄惁鏈塼hink鏍囩
+      controller: null, // ai鍥炵瓟璇锋眰鎺у埗鍣紝鍙互鏍规嵁涓氬姟涓柇璇锋眰
     };
   },
   methods: {
+    // md娓叉煋涓篽tml
+    getHtml(md) {
+      return markdownIt.render(md)
+    },
+    changeNetEnable() {
+      this.netSearchEnable = ! this.netSearchEnable
+    },
+    // 闃绘鍥炶溅鎹㈣
+    handleEnterKey(e) {
+      e.preventDefault(); // 闃绘榛樿鎹㈣琛屼负
+    },
+    handleMouseEnter(msgIndex) {
+      this.msgIndex = msgIndex
+    },
+    handleMouseLeave() {
+      this.msgIndex = null
+    },
+    // 閲嶆柊鐢熸垚
+    reAnswer() {
+    },
+    // 澶嶅埗鍐呭
+    copyText(content) {
+      const clipboard = new ClipboardJS('.copy', {
+        text: () => content
+      });
+      // 瑙﹀彂澶嶅埗锛堥渶瑕佷竴涓殣钘忕殑鎸夐挳锛�
+      document.querySelector('.copy').click();
+      clipboard.destroy();
+      Message.success("澶嶅埗鎴愬姛")
+    },
     // 鑾峰彇瑙掕壊澶村儚
     getAvatar(role) {
       const avatars = {
@@ -93,40 +161,117 @@
       return roleNames[role];
     },
 
-    // 鍙戦�佹秷鎭�
     async sendMessage() {
       if (this.inputMessage.trim() === '') return;
 
-      // 娣诲姞鐢ㄦ埛娑堟伅
-      this.messages.push({
-        role: 'user',
-        content: this.inputMessage,
-      });
-      this.sendMsgForm.query = this.inputMessage
-      // 娓呯┖杈撳叆妗�
-      this.inputMessage = '';
+      // 鍒濆鍖栫姸鎬�
+      this.chunkRef = '';
+      this.haveThinkStart = false;
+      this.haveThinkEnd = false;
+      this.controller = new AbortController();
 
-      sendKbMsg(this.sendMsgForm).then(res => {
-        console.log(res, "鎷垮埌鍥炲浜嗭紒锛�")
-        this.messages.push({
-          role: 'assistant',
-          content: '杩欐槸涓�鏉� AI 鍥炲銆�',
+      // 娣诲姞鐢ㄦ埛娑堟伅鍜屽崰浣嶅姪鐞嗘秷鎭�
+      this.messages.push(
+          { role: 'user', content: this.inputMessage },
+          { role: 'assistant', content: '' }
+      );
+      this.sendMsgForm.query = this.inputMessage;
+      this.inputMessage = '';
+      const assistantIndex = this.messages.length - 1;
+      try {
+        const response = await fetch('/api/chat/send/msg', {
+          method: 'POST',
+          headers: {
+            'Content-Type': 'application/json',
+            "Accept": 'text/event-stream',
+            'Cache-Control': 'no-cache, no-transform'
+          },
+          signal: this.controller.signal,
+          body: JSON.stringify(this.sendMsgForm)
         });
-      })
-    },
+
+        if (!response.ok) throw new Error(`HTTP ${response.status}`);
+
+        const reader = response.body.getReader();
+        const decoder = new TextDecoder();
+        let jsonBuffer = '';
+
+        // eslint-disable-next-line no-constant-condition
+        while (true) {
+          const { done, value } = await reader.read();
+          if (done) {
+            this.sendMsgForm.history.push({ role: 'user', content: this.inputMessage })
+            this.sendMsgForm.history.push({ role: 'assistant', content: this.messages[assistantIndex].content })
+            break;
+          }
+
+          const chunk = decoder.decode(value, { stream: true });
+          const lines = chunk.split('\n');
+
+          for (const line of lines) {
+            if (!line.startsWith('data:')) continue;
+
+            const rawData = line.slice(5).trim();
+            if (rawData === '[DONE]') continue;
+
+            try {
+              jsonBuffer += rawData;
+              const data = JSON.parse(jsonBuffer);
+              jsonBuffer = '';
+              let content = '';
+              if (data.docs) {
+                for (let i = 0; i < data.docs.length; i++) {
+                  content += data.docs[i]
+                }
+              } else {
+                content = data?.choices?.[0]?.delta?.content || '';
+              }
+              if (!content) continue;
+              // 瀹炴椂鏇存柊
+              this.messages[assistantIndex].content += content;
+              this.messages[assistantIndex].content = this.messages[assistantIndex].content.replace(/<\/?think>/g, '');
+              await this.$nextTick();
+            } catch {
+              // JSON涓嶅畬鏁达紝绛夊緟涓嬫鏁版嵁
+              continue;
+            }
+          }
+        }
+      } catch (error) {
+        if (error.name !== 'AbortError') {
+          console.error('Request failed:', error);
+          this.messages[assistantIndex].content = '璇锋眰寮傚父';
+        }
+      }
+    }
+
   },
 };
 </script>
 
 <style scoped>
-.send-but {
+.add-but {
+  width: 90px;
+  height: 80%;
+  border: 1px solid #E4E7ED;
   display: flex;
-  justify-content: center;
   align-items: center;
-  width: 58px;
-  height: 54px;
-  background-color: #f6f6f6;
-  margin-left: 5px;
+  justify-content: center;
+  border-radius: 8px;
+  background-color: #DCDFE6;
+}
+.add-but:hover {
+  cursor: pointer;
+  background-color: #409EFF;
+}
+.add-but-active {
+  background-color: #409EFF;
+}
+.send-but-warp {
+  flex:1;
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
 }
 .send-but:hover {
   cursor: pointer;
@@ -140,18 +285,18 @@
 
 .chat-messages {
   padding: 16px;
-  margin-top: 54px;
-  overflow-y: auto;
+  margin-top: 14px;
   width: 800px;
-  height: 680px;
 }
 
 .message {
   display: flex;
-  margin-bottom: 16px;
+  margin-bottom: 18px;
+  min-height: 75px;
 }
 
 .message.user {
+  align-items: center;
   flex-direction: row-reverse;
 }
 
@@ -167,7 +312,7 @@
   padding: 8px 12px;
   border-radius: 8px;
   background-color: #fff;
-  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  /*box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);*/
 }
 
 .message.user .content {
@@ -190,16 +335,64 @@
 }
 
 .chat-input {
-  padding: 16px;
   background-color: #fff;
-  width: 800px;
+  width: 100%;
   display: flex;
   flex-direction: row;
   justify-content: center;
   align-items: center;
   position: absolute;
-  bottom: 70px;
+  bottom: 50px;
   left: 50%; /* 灏� div 鐨勫乏杈圭Щ鍔ㄥ埌鐖跺鍣ㄧ殑 50% 浣嶇疆 */
   transform: translateX(-50%); /* 灏� div 鍚戝乏绉诲姩鑷韩瀹藉害鐨� 50% */
 }
+
+.msg-op-list {
+  margin-top: 15px;
+}
+.msg-copy {
+  font-size: 18px;
+}
+.msg-re {
+  font-size: 20px;
+}
+.msg-op {
+  color: gray;
+  margin-right: 5px;
+}
+.msg-op:hover {
+  cursor: pointer;
+}
+
+.send-op-list {
+  position: absolute;
+  bottom: 0px;
+  width: 100%;
+  height: 40px;
+  display: flex;
+  align-items: center;
+  user-select: none;
+  opacity: 1;
+  z-index: 2;
+}
+
+.send-warp {
+  display: flex;
+  width: 100%;
+  height: 100%;
+  padding: 0 15px;
+  box-sizing: border-box
+}
+
+.add-option {
+  flex:1;
+  display: flex;
+  justify-content: flex-start;
+}
+
+::v-deep(.el-textarea__inner) {
+  min-height: 100px !important;
+  max-height: 400px !important;
+  padding-bottom: 85px !important;
+}
 </style>

--
Gitblit v1.8.0