作者 lixiang

智能助手

1 import { defHttp } from '/@/utils/http/axios'; 1 import { defHttp } from '/@/utils/http/axios';
2 import { useMessage } from '/@/hooks/web/useMessage'; 2 import { useMessage } from '/@/hooks/web/useMessage';
  3 +import { useUserStore } from '@/store/modules/user';
3 4
4 const { createMessage } = useMessage(); 5 const { createMessage } = useMessage();
5 6
@@ -12,14 +13,22 @@ enum Api { @@ -12,14 +13,22 @@ enum Api {
12 * 发送消息(流式处理) 13 * 发送消息(流式处理)
13 * @param params 14 * @param params
14 */ 15 */
15 -export const sendMessage = async (params: { questionText: string }) => { 16 +export const sendMessage = async (params: { questionText: string; code: string; codeType: number }) => {
16 try { 17 try {
17 const userStore = useUserStore(); 18 const userStore = useUserStore();
18 console.log("userStore",userStore.getUserInfo.username) 19 console.log("userStore",userStore.getUserInfo.username)
19 const token = userStore.getToken; 20 const token = userStore.getToken;
20 21
  22 + // 拼接参数
  23 + const query = new URLSearchParams({
  24 + questionText: params.questionText,
  25 + code: params.code,
  26 + codeType: String(params.codeType),
  27 + user: userStore.getUserInfo.username
  28 + }).toString();
  29 +
21 const response = await fetch( 30 const response = await fetch(
22 - `/jeecgboot/airag/zdyRag/multiStageStream?questionText=${encodeURIComponent(params.questionText)}`, 31 + `/jeecgboot/airag/zdyRag/sendStream?${query}`,
23 { 32 {
24 method: 'GET', 33 method: 'GET',
25 headers: { 34 headers: {
@@ -34,7 +43,6 @@ export const sendMessage = async (params: { questionText: string }) => { @@ -34,7 +43,6 @@ export const sendMessage = async (params: { questionText: string }) => {
34 throw new Error(`请求失败: ${response.status} ${response.statusText}`); 43 throw new Error(`请求失败: ${response.status} ${response.statusText}`);
35 } 44 }
36 45
37 - // 返回异步生成器函数  
38 return (async function* () { 46 return (async function* () {
39 const reader = response.body.getReader(); 47 const reader = response.body.getReader();
40 const decoder = new TextDecoder('utf-8'); 48 const decoder = new TextDecoder('utf-8');
@@ -71,9 +79,10 @@ export const sendMessage = async (params: { questionText: string }) => { @@ -71,9 +79,10 @@ export const sendMessage = async (params: { questionText: string }) => {
71 } catch (error) { 79 } catch (error) {
72 console.error('Error sending message:', error); 80 console.error('Error sending message:', error);
73 createMessage.error('发送消息失败'); 81 createMessage.error('发送消息失败');
74 - throw error; // 抛出错误让调用方处理 82 + throw error;
75 } 83 }
76 }; 84 };
  85 +
77 export const getButtonList = async () => { 86 export const getButtonList = async () => {
78 const res = await defHttp.get({ 87 const res = await defHttp.get({
79 url: Api.buttonList, 88 url: Api.buttonList,
@@ -16,13 +16,14 @@ @@ -16,13 +16,14 @@
16 </div> 16 </div>
17 </div> 17 </div>
18 </div> 18 </div>
19 - <!-- 流式响应时显示的临时消息 -->  
20 <div v-if="streamingMessage" class="message assistant"> 19 <div v-if="streamingMessage" class="message assistant">
21 <div class="message-content"> 20 <div class="message-content">
22 <div class="message-text" style="white-space: pre-wrap;">{{ streamingMessage }}</div> 21 <div class="message-text" style="white-space: pre-wrap;">{{ streamingMessage }}</div>
23 </div> 22 </div>
24 </div> 23 </div>
25 - <div v-if="loading" class="message assistant"> 24 +
  25 +
  26 + <div v-if="loading && !streamingMessage" class="message assistant">
26 <div class="message-content"> 27 <div class="message-content">
27 <div class="message-text">思考中...</div> 28 <div class="message-text">思考中...</div>
28 </div> 29 </div>
@@ -38,12 +39,7 @@ @@ -38,12 +39,7 @@
38 <DownloadOutlined /> 39 <DownloadOutlined />
39 <span class="btn-text">下载</span> 40 <span class="btn-text">下载</span>
40 </a-button> 41 </a-button>
41 - <a-button  
42 - type="text"  
43 - @click="closePreview"  
44 - class="close-btn"  
45 - title="关闭预览"  
46 - > 42 + <a-button type="text" @click="closePreview" class="close-btn" title="关闭预览">
47 <CloseOutlined /> 43 <CloseOutlined />
48 </a-button> 44 </a-button>
49 </div> 45 </div>
@@ -70,7 +66,8 @@ @@ -70,7 +66,8 @@
70 v-for="(btn, index) in buttonList" 66 v-for="(btn, index) in buttonList"
71 :key="index" 67 :key="index"
72 class="action-button" 68 class="action-button"
73 - @click="sendButtonMessage(btn.buttonValues)" 69 + @click="sendButtonMessage(btn)"
  70 + :disabled="buttonDisabled || loading"
74 > 71 >
75 {{ btn.buttonName }} 72 {{ btn.buttonName }}
76 </a-button> 73 </a-button>
@@ -100,10 +97,8 @@ @@ -100,10 +97,8 @@
100 <script lang="ts" name="zdy-rag-chat" setup> 97 <script lang="ts" name="zdy-rag-chat" setup>
101 import { ref, reactive, nextTick, onMounted, onBeforeUnmount } from 'vue'; 98 import { ref, reactive, nextTick, onMounted, onBeforeUnmount } from 'vue';
102 import { sendMessage as apiSendMessage, getButtonList } from './ZdyRag.api'; 99 import { sendMessage as apiSendMessage, getButtonList } from './ZdyRag.api';
103 -import { useMessage } from '/@/hooks/web/useMessage';  
104 import { CloseOutlined, DownloadOutlined } from '@ant-design/icons-vue'; 100 import { CloseOutlined, DownloadOutlined } from '@ant-design/icons-vue';
105 import * as pdfjsLib from 'pdfjs-dist'; 101 import * as pdfjsLib from 'pdfjs-dist';
106 -import * as mammoth from 'mammoth';  
107 import { renderAsync } from 'docx-preview'; 102 import { renderAsync } from 'docx-preview';
108 103
109 // 设置PDF.js worker路径 104 // 设置PDF.js worker路径
@@ -134,7 +129,8 @@ const messages = reactive([ @@ -134,7 +129,8 @@ const messages = reactive([
134 129
135 const inputMessage = ref(''); 130 const inputMessage = ref('');
136 const loading = ref(false); 131 const loading = ref(false);
137 -const streamingMessage = ref(''); // 流式响应消息 132 +const buttonDisabled = ref(false);
  133 +const streamingMessage = ref('');
138 const messagesContainer = ref<HTMLElement>(); 134 const messagesContainer = ref<HTMLElement>();
139 const previewContent = ref(''); 135 const previewContent = ref('');
140 const previewType = ref(''); 136 const previewType = ref('');
@@ -143,12 +139,10 @@ const currentFile = ref<any>(null); @@ -143,12 +139,10 @@ const currentFile = ref<any>(null);
143 const pdfPreview = ref<HTMLElement>(); 139 const pdfPreview = ref<HTMLElement>();
144 const docxPreview = ref<HTMLElement>(); 140 const docxPreview = ref<HTMLElement>();
145 141
146 -// 发送消息  
147 const sendMessage = async () => { 142 const sendMessage = async () => {
148 const text = inputMessage.value.trim(); 143 const text = inputMessage.value.trim();
149 if (!text || loading.value) return; 144 if (!text || loading.value) return;
150 145
151 - // 添加用户消息  
152 messages.push({ 146 messages.push({
153 type: 'user', 147 type: 'user',
154 text: text, 148 text: text,
@@ -164,7 +158,6 @@ const sendMessage = async () => { @@ -164,7 +158,6 @@ const sendMessage = async () => {
164 scrollToBottom(); 158 scrollToBottom();
165 159
166 try { 160 try {
167 - // 创建最终消息对象  
168 const newMessage = { 161 const newMessage = {
169 type: 'assistant', 162 type: 'assistant',
170 text: '', 163 text: '',
@@ -173,15 +166,15 @@ const sendMessage = async () => { @@ -173,15 +166,15 @@ const sendMessage = async () => {
173 fileName: '' 166 fileName: ''
174 }; 167 };
175 168
176 - // 调用API获取异步生成器  
177 - const streamGenerator = await apiSendMessage({ questionText: text }); 169 + // 🚩调用API携带正确参数
  170 + const streamGenerator = await apiSendMessage({
  171 + questionText: text,
  172 + code: 'INPUT_SEND',
  173 + codeType: 0
  174 + });
178 175
179 - // 遍历异步生成器  
180 for await (const chunk of streamGenerator) { 176 for await (const chunk of streamGenerator) {
181 if (chunk.token) { 177 if (chunk.token) {
182 - if (!streamingMessage.value){  
183 - loading.value = false;  
184 - }  
185 streamingMessage.value += chunk.token; 178 streamingMessage.value += chunk.token;
186 newMessage.text += chunk.token; 179 newMessage.text += chunk.token;
187 scrollToBottom(); 180 scrollToBottom();
@@ -192,10 +185,8 @@ const sendMessage = async () => { @@ -192,10 +185,8 @@ const sendMessage = async () => {
192 } 185 }
193 } 186 }
194 187
195 - // 将流式消息添加到消息列表  
196 messages.push(newMessage); 188 messages.push(newMessage);
197 streamingMessage.value = ''; 189 streamingMessage.value = '';
198 -  
199 } catch (error: any) { 190 } catch (error: any) {
200 console.error('发送消息失败:', error); 191 console.error('发送消息失败:', error);
201 messages.push({ 192 messages.push({
@@ -211,25 +202,25 @@ const sendMessage = async () => { @@ -211,25 +202,25 @@ const sendMessage = async () => {
211 } 202 }
212 }; 203 };
213 204
214 -const sendButtonMessage = async (buttonValue: string) => {  
215 - if (loading.value) return; 205 +const sendButtonMessage = async (btn: any) => {
  206 + if (loading.value || buttonDisabled.value) return;
216 207
217 // 添加用户消息 208 // 添加用户消息
218 messages.push({ 209 messages.push({
219 type: 'user', 210 type: 'user',
220 - text: buttonValue, 211 + text: btn.buttonValues, // 🚩使用 buttonValues 作为显示内容
221 similarity: null, 212 similarity: null,
222 fileBase64: null, 213 fileBase64: null,
223 fileName: null 214 fileName: null
224 }); 215 });
225 216
226 loading.value = true; 217 loading.value = true;
  218 + buttonDisabled.value = true;
227 streamingMessage.value = ''; 219 streamingMessage.value = '';
228 closePreview(); 220 closePreview();
229 scrollToBottom(); 221 scrollToBottom();
230 222
231 try { 223 try {
232 - // 创建最终消息对象  
233 const newMessage = { 224 const newMessage = {
234 type: 'assistant', 225 type: 'assistant',
235 text: '', 226 text: '',
@@ -238,15 +229,15 @@ const sendButtonMessage = async (buttonValue: string) => { @@ -238,15 +229,15 @@ const sendButtonMessage = async (buttonValue: string) => {
238 fileName: '' 229 fileName: ''
239 }; 230 };
240 231
241 - // 调用API获取异步生成器  
242 - const streamGenerator = await apiSendMessage({ questionText: buttonValue }); 232 + // 🚩调用API携带正确参数
  233 + const streamGenerator = await apiSendMessage({
  234 + questionText: btn.buttonValues,
  235 + code: btn.code,
  236 + codeType: 1
  237 + });
243 238
244 - // 遍历异步生成器  
245 for await (const chunk of streamGenerator) { 239 for await (const chunk of streamGenerator) {
246 if (chunk.token) { 240 if (chunk.token) {
247 - if (!streamingMessage.value){  
248 - loading.value = false;  
249 - }  
250 streamingMessage.value += chunk.token; 241 streamingMessage.value += chunk.token;
251 newMessage.text += chunk.token; 242 newMessage.text += chunk.token;
252 scrollToBottom(); 243 scrollToBottom();
@@ -257,10 +248,8 @@ const sendButtonMessage = async (buttonValue: string) => { @@ -257,10 +248,8 @@ const sendButtonMessage = async (buttonValue: string) => {
257 } 248 }
258 } 249 }
259 250
260 - // 将流式消息添加到消息列表  
261 messages.push(newMessage); 251 messages.push(newMessage);
262 streamingMessage.value = ''; 252 streamingMessage.value = '';
263 -  
264 } catch (error: any) { 253 } catch (error: any) {
265 console.error('发送按钮消息失败:', error); 254 console.error('发送按钮消息失败:', error);
266 messages.push({ 255 messages.push({
@@ -272,6 +261,7 @@ const sendButtonMessage = async (buttonValue: string) => { @@ -272,6 +261,7 @@ const sendButtonMessage = async (buttonValue: string) => {
272 }); 261 });
273 } finally { 262 } finally {
274 loading.value = false; 263 loading.value = false;
  264 + buttonDisabled.value = false;
275 scrollToBottom(); 265 scrollToBottom();
276 } 266 }
277 }; 267 };