Переглянути джерело

[feat][ai问答][ai聊天界面]

hizhangling 1 тиждень тому
батько
коміт
e268946d7d

+ 2 - 1
package.json

@@ -36,8 +36,9 @@
     "simple-keyboard-layouts": "^3.4.114",
     "splitpanes": "4.0.4",
     "video.js": "^8.23.3",
-    "vue": "3.5.16",
+    "vue": "3.5.17",
     "vue-cropper": "1.1.1",
+    "vue-element-plus-x": "^1.3.2",
     "vue-router": "4.5.1",
     "vuedraggable": "4.1.0"
   },

+ 15 - 3
src/api/xjc-integratedmachine/common/aiChat.js

@@ -1,9 +1,21 @@
 import request from '@/utils/request'
 
-export function getAiChatRecordList(data) {
+// 获取ai聊天记录
+export function aiChatRecordList(data) {
     return request({
-        url: '/ai/chat/list',
+        url: '/ai/chat/record/list',
         method: 'get',
         params : data
     })
-}
+}
+
+// 增加ai聊天记录
+export function aiChatRecordAdd(data) {
+    return request({
+        url: '/ai/chat/record/add',
+        method: 'post',
+        params : data
+    })
+}
+
+// 创建ai记忆id

BIN
src/assets/images/common/ai/system.png


BIN
src/assets/images/common/ai/user.png


+ 2 - 1
src/main.js

@@ -6,7 +6,7 @@ import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
 import 'element-plus/theme-chalk/dark/css-vars.css'
 import locale from 'element-plus/es/locale/lang/zh-cn'
-
+import ElementPlusX from 'vue-element-plus-x'
 import '@/assets/styles/index.scss' // global css
 
 import App from './App'
@@ -70,6 +70,7 @@ app.use(router)
 app.use(store)
 app.use(plugins)
 app.use(elementIcons)
+app.use(ElementPlusX)
 app.component('svg-icon', SvgIcon)
 
 directive(app)

+ 59 - 25
src/views/xjc-integratedmachine/common/ai/chat.vue

@@ -15,13 +15,24 @@
         <div class="main-content">
             <div class="chat-container">
                 <div class="message-list">
-
+                    <div v-for="item in chatRecordList" :key="item.id" :class="item.isUser? 'every-message user-message' : 'message bot-message' ">
+                        <!-- 会话图标 -->
+                        <div :class="item.isUser == 1? 'user-image' : 'system-image' "></div>
+                        <span style="display: flex">
+                            <el-text v-html="item.content"></el-text>
+                        </span>
+                    </div>
                 </div>
-                <div class="content-box">
-                    <div v-html="streamHtmlData"></div>
-                    <span class="loading-dots"></span>
-                    <span class="dot"></span>
-                    <span class="dot"></span>
+                <div class="message-box">
+                    <div class="typing-message">
+                        <span>
+                            <span v-html="streamHtmlData"></span>
+                            <span class="loading-dots">
+                                <span class="dot"></span>
+                                <span class="dot"></span>
+                            </span>
+                        </span>
+                    </div>
                 </div>
                 <el-button @click="stopMessage">停止回答</el-button>
             </div>
@@ -54,14 +65,14 @@
 </template>
 
 <script setup>
-    import {getAiChatRecordList} from '@/api/xjc-integratedmachine/common/aiChat.js'
+    import {aiChatRecordList} from '@/api/xjc-integratedmachine/common/aiChat.js'
     import { getToken } from '@/utils/auth'
     import { marked } from 'marked'
 
     // 聊天记录
     let chatRecordList = ref([])
     // md流式数据
-    const streamData = ref('');
+    const streamMarkdownData = ref('');
     // html流式数据
     let streamHtmlData = ref('');
     // 流式加载状态
@@ -71,22 +82,26 @@
     // AbortController用于中止请求
     let controller = null;
     // 输入的问题
-    const inputMessage = ref('')
-
+    let inputMessage = ref('')
+    // 发送标识
+    let isSending = ref(false)
     // 查看所有聊天记录
     list();
     function list() {
-        getAiChatRecordList().then(resp =>{
+        let queryForm = {
+
+        }
+        aiChatRecordList(queryForm).then(resp =>{
             console.log(resp)
-            chatRecordList.value = resp;
+            chatRecordList.value = resp.rows;
         })
     }
 
     const sendMessage = () => {
-        if (inputMessage.value.trim()) {
+/*        if (inputMessage.value.trim()) {
             sendRequest(inputMessage.value.trim())
             inputMessage.value = ''
-        }
+        }*/
     }
 
     const sendRequest = async(message) => {
@@ -100,7 +115,7 @@
         // chatRecordList.value.push(userMessage)
         try{
             isLoading.value = true;
-            streamData.value = ''; // 清空之前的数据
+            streamMarkdownData.value = ''; // 清空之前的数据
 
             // 创建AbortController以便可以中止请求
             controller = new AbortController();
@@ -110,7 +125,7 @@
                 "content": message? message: "你是谁?"
             }
             // 发送fetch请求
-            const response = await fetch('/dev-api/ai/chat/stream', {
+            const response = await fetch('/dev-api/ai/chat/record/stream', {
                 method: 'POST',
                 headers: {
                     'Content-Type': 'application/json',
@@ -131,17 +146,17 @@
                 // 解码并追加数据
                 let chunk = decoder.decode(value, { stream: true });
                 chunk = chunk.replace(/\n\n/g, '').replace(/data:/g, '')
-                streamData.value += chunk;
-                streamHtmlData.value = marked(streamData.value)
+                streamMarkdownData.value += chunk;
+                streamHtmlData.value = marked(streamMarkdownData.value)
             }
         }catch (error) {
             // 如果是手动中止,不显示错误
             if (error.name !== 'AbortError') {
                 console.error('流式读取失败:', error);
-                streamData.value = 'Error: ' + error.message;
+                streamMarkdownData.value = 'Error: ' + error.message;
             }
         } finally {
-            streamHtmlData.value = marked(streamData.value)
+            streamHtmlData.value = marked(streamMarkdownData.value)
             isLoading.value = false;
             console.log("==============");
         }
@@ -214,9 +229,6 @@
         width: 10vw;
         background-color: #f4f4f9;
         padding: 20px;
-/*        display: flex;
-        flex-direction: column;
-        align-items: center;*/
     }
 
     .control-button-box {
@@ -245,6 +257,15 @@
         flex-direction: column;
     }
 
+    .every-message {
+        margin-bottom: 10px;
+        padding: 10px;
+        border-radius: 4px;
+        display: flex;
+        width: 40vw;
+        /* align-items: center; */
+    }
+
     .user-message {
         max-width: 70%;
         background-color: #e1f5fe;
@@ -258,10 +279,23 @@
         align-self: flex-start;
     }
 
-    /* 内容区 */
-    .content-box {
+    .system-image {
+        width: 32px;
+        height:32px;
+        background-image: url('@/assets/images/common/ai/system.png');
+    }
+
+    .user-image {
+        width: 32px;
+        height:32px;
+        background-image: url('@/assets/images/common/ai/user.png');
+    }
+
+    /* 每个消息的区域 */
+    .message-box {
         /*max-height: 300px;*/
         width: 60vw;
+        /*height: 30vh;*/
         overflow-y: auto;
         border: 1px solid #eee;
         padding: 10px;