allen il y a 2 ans
Parent
commit
3fed81ca4a

+ 10 - 0
src/api/knowledge/search.js

@@ -7,4 +7,14 @@ export function getGraphByEntiry(query) {
     method: 'get',
     params: query
   })
+}
+
+export function question(text) {
+  return request({
+    url: '/kg/question',
+    method: 'get',
+    params: {
+      text
+    }
+  })
 }

BIN
src/assets/search/robot.png


BIN
src/assets/search/soldier.png


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

@@ -346,4 +346,7 @@
 }
 .el-select-dropdown__item {
 	color: #bfcbd9;
+}
+.el-tabs__item {
+	color: #bfcbd9;
 }

+ 200 - 10
src/views/searchV2/question/index.vue

@@ -1,20 +1,210 @@
 <template>
-    <div>
-
-    </div>
+    <div style="width:1000px; margin: auto;">
+      <div class="chat-window-wrapper" ref="chatWindowWrapper">
+        <el-card class="chat-window" ref="chatWindow">
+          <div v-for="(message, index) in messages" :key="index" class="message">
+            <img
+            :src="message.sender === 'user' ? require('@/assets/search/soldier.png') : require('@/assets/search/robot.png')"
+              alt="avatar"
+              class="avatar"
+            />
+            <div :class="['bubble', message.sender === 'user' ? 'user-bubble' : message.sender === 'ai' ? 'ai-bubble' : 'system-bubble']">
+              {{ message.content }}
+              <el-button v-if="message.sender === 'system' && requestError" size="mini" type="text" @click="retry(message.oldMsg)" style="margin-left:10px;width:80px">&nbsp;&nbsp;重&nbsp;&nbsp;试</el-button>
+            </div>
+          </div>
+          <div v-show="isWaitingForResponse" class="response-indicator" ref="aiBubble">
+              <img :src="require('@/assets/search/robot.png')" alt="avatar" class="avatar" />
+              <div class="loader"></div>
+            </div>
+        </el-card>
+      </div>
+      <div class="input-area">
+          <el-input v-model="inputMessage" type="textarea" placeholder="输入您的消息..." style="margin:5px" :disabled="isWaitingForResponse"
+                  @keyup.enter.native="sendMessage(null)"></el-input>
+      </div>
+  </div>
 </template>
+  
 <script>
+import { question } from "@/api/knowledge/search";
 export default {
     data() {
         return {
-            
-        }
+            inputMessage: "",
+            messages: [],
+            requestError: false,
+            isWaitingForResponse: false,
+        };
     },
-    created(){
-        alert('未完成。。。')
+    methods: {
+        sendMessage(msg) {
+            let inputMessage = msg ? msg :this.inputMessage.trim()
+            if (!inputMessage) return;
+            this.isWaitingForResponse = true;
+
+            if(!msg){
+              this.messages.push({
+                sender: "user",
+                content: inputMessage,
+              });
+            }
+
+            // 获取最后一行的焦点
+            const aiBubbles = this.$refs.aiBubble;
+            console.info(aiBubbles)
+            aiBubbles.focus();
+
+            let oldMsg = inputMessage;
+            this.scrollToBottom();
+            question(inputMessage).then(resp => {
+              this.messages.push({
+                  sender: "ai",
+                  content: resp.data,
+              });
+              this.scrollToBottom();
+              this.isWaitingForResponse = false;
+            }).catch(error => {
+              console.info(error)
+              this.requestError = true;
+                this.messages.push({
+                  sender: "system",
+                  content: "系统异常",
+                  oldMsg,
+              });
+              this.scrollToBottom();
+              this.isWaitingForResponse = false;
+            })
+            this.inputMessage = "";
+        },
+        retry(msg) {
+          if (this.requestError) {
+            this.requestError = false;
+            this.messages.pop(); // 移除系统提示消息
+            this.sendMessage(msg); // 重新发送
+          }
+        },
+        scrollToBottom() {
+          this.$nextTick(() => {
+            const chatWindowWrapper = this.$refs.chatWindowWrapper;
+            chatWindowWrapper.scrollTop = chatWindowWrapper.scrollHeight;
+          });
+        },
     },
-    methods:{
+};
+</script>
+  
+<style scoped>
+.chat-window {
+  overflow-y: scroll;
+  margin-bottom: 10px;
+  border: 0px;
+  border-radius: 5px;
+}
+
+.input-area {
+  display: flex;
+}
+
+.message {
+  margin-bottom: 10px;
+}
+
+.bubble {
+  display: inline-block;
+  padding: 5px 10px;
+  border-radius: 5px;
+  font-family: "Courier New", Courier, monospace;
+  font-size: 14px;
+  line-height: 1.5;
+  margin-bottom: 5px;
+  /* flex: 1; */
+}
+
+
+.message {
+  display: flex;
+  align-items: flex-start;
+  margin-bottom: 10px;
+}
+
+.avatar {
+  width: 32px;
+  height: 32px;
+  border-radius: 50%;
+  margin-right: 8px;
+}
+
+.user-bubble {
+  background-color: #45a1ff;
+  color: #fff;
+}
+
+.ai-bubble {
+  background-color: #1a1a1a;
+  color: #45a1ff;
+  border: 1px solid #45a1ff;
+}
+
+.el-input {
+  flex: 1;
+  margin-right: 10px;
+}
+
+.el-input__inner {
+  background-color: #333;
+  border-color: #45a1ff;
+  color: #45a1ff;
+}
+
+.el-input__inner::placeholder {
+  color: #45a1ff;
+}
+
+.el-button {
+  background-color: #45a1ff;
+  border-color: #45a1ff;
+  color: #1a1a1a;
+}
+.el-textarea__inner {
+  background-color: transparent;
+}
+
+
+.response-indicator {
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.loader {
+  border: 2px solid #45a1ff;
+  border-top: 2px solid #1a1a1a;
+  border-radius: 50%;
+  width: 14px;
+  height: 14px;
+  margin-left: 5px;
+  animation: spin 0.8s linear infinite;
+}
+
+@keyframes spin {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+.chat-window-wrapper {
+  border: 2px solid #45a1ff;
+  height: calc(100vh - 200px);
+  /* max-height: 500px; */
+  overflow-y: auto;
+}
 
-    }
+.chat-window {
+  width: 100%;
+  /* min-height: 100%; */
 }
-</script>
+</style>