gcz 2 weeks ago
parent
commit
fea7376e84
7 changed files with 174 additions and 31 deletions
  1. 3 2
      src/layout/index.vue
  2. 3 0
      src/styles/index.scss
  3. 26 1
      src/utils/index.js
  4. 53 6
      src/views/AIChat.vue
  5. 3 3
      src/views/DocSearch.vue
  6. 47 0
      src/views/Favorites.vue
  7. 39 19
      src/views/History.vue

+ 3 - 2
src/layout/index.vue

@@ -98,6 +98,7 @@ import { useUserStore } from '../stores/user'
 import request from '../utils/request' // 确保引入request
 import { ElMessage } from 'element-plus'
 import { get, post,put } from '../utils/request'
+import { downloadFile } from '../utils'
 
 const route = useRoute()
 const router = useRouter()
@@ -158,13 +159,13 @@ const handleHistoryClick = async (item) => {
         break;
       case 'file.download':
         try {
-          window.open(item.fileUrl, '_blank')
+          await downloadFile(item.fileUrl, item.fileName)
           // 添加文件下载记录
           await post('/admin/userHistoryLog/saveFileDownloadLog', {
             relatedId: item.relatedId
           })
         } catch (error) {
-          console.log('error', error);
+          console.log('error', error)
           ElMessage.error('下载失败,请重试文件地址:'+item.fileUrl)
         }
         // try {

+ 3 - 0
src/styles/index.scss

@@ -48,6 +48,9 @@ think{
     left: 64px;
   }
 }
+.file-download{
+  cursor: pointer;
+}
 
 @mixin multi-ellipsis($line: 1) {
   @if $line <= 0 {

+ 26 - 1
src/utils/index.js

@@ -7,4 +7,29 @@ export const getUuid = () => {
         const v = c === "x" ? r : (r & 0x3) | 0x8;
         return v.toString(16);
     });
-};
+};
+
+/**
+ * 通用文件下载方法
+ * @param {string} url - 文件URL
+ * @param {string} fileName - 文件名
+ * @returns {Promise} - 返回下载Promise
+ */
+export const downloadFile = (url, fileName) => {
+    // console.log('url',url);
+    // console.log('fileName',fileName);
+  return new Promise((resolve, reject) => {
+    try {
+      const link = document.createElement('a')
+      link.href = url
+      link.download = fileName
+      link.style.display = 'none'
+      document.body.appendChild(link)
+      link.click()
+      document.body.removeChild(link)
+      resolve()
+    } catch (error) {
+      reject(error)
+    }
+  })
+}

+ 53 - 6
src/views/AIChat.vue

@@ -118,6 +118,7 @@ const selectedKnowledge = ref([])
 const selectedModel = ref('DeepSeek')
 const sessionId = ref('')
 const chatId = ref('')
+const historyId = ref(route.query.historyId || '')
 
 // 对话模型列表
 const models = ref([
@@ -187,7 +188,8 @@ const scrollToBottom = async () => {
 }
 
 const handleSend = async () => {
-  if (selectedKnowledge.value.length === 0) {
+  // 修改条件判断,只有在非历史记录模式下才检查知识库选择
+  if (selectedKnowledge.value.length === 0 && !historyId.value) {
       ElMessage.warning('请先选择知识库')
       return
     }
@@ -425,16 +427,16 @@ const getHistoryChat = async (historyId) => {
 
           // 按照 data: 分割字符串
           const responses = answerContent.split('data:')
-          console.log('responses',responses);
+          // console.log('responses',responses);
           
           // 遍历所有分割后的响应
           for (const response of responses) {
-            console.log('response==========',response);
+            // console.log('response==========',response);
             if (response.trim() && !response.includes('"data": true')) {
-              console.log('response.trim()',response.trim());
+              // console.log('response.trim()',response.trim());
               try {
                 const jsonData = JSON.parse(response.trim())
-                console.log('jsonData',jsonData);
+                // console.log('jsonData',jsonData);
                 if (jsonData.data && jsonData.data.answer) {
                   formattedAnswer += jsonData.data.answer
                 }
@@ -449,11 +451,56 @@ const getHistoryChat = async (historyId) => {
             formattedAnswer = answerContent
           }
 
+          // 处理think标签
+          let processedText = formattedAnswer;
+          const thinkEndRegex = /([^<])<\/think>/g;
+          const matches = [...processedText.matchAll(thinkEndRegex)];
+          
+          for (const match of matches) {
+            const beforeThinkEnd = processedText.substring(0, match.index + match[1].length);
+            const afterThinkEnd = processedText.substring(match.index + match[1].length);
+            // 在对应的</think>标签前添加<think>标签
+            processedText = '<think>' + beforeThinkEnd + afterThinkEnd;
+          }
+
+          // 如果内容包含think标签,分别处理think标签内外的内容
+          let finalContent = '';
+          if (processedText.includes('<think>') && processedText.includes('</think>')) {
+            let currentPos = 0;
+            
+            while (true) {
+              const thinkStart = processedText.indexOf('<think>', currentPos);
+              const thinkEnd = processedText.indexOf('</think>', currentPos);
+              
+              if (thinkStart === -1 || thinkEnd === -1) {
+                // 处理剩余的文本
+                if (currentPos < processedText.length) {
+                  finalContent += md.render(processedText.substring(currentPos));
+                }
+                break;
+              }
+              
+              // 处理think标签之前的内容(需要markdown渲染)
+              if (thinkStart > currentPos) {
+                finalContent += md.render(processedText.substring(currentPos, thinkStart));
+              }
+              
+              // 添加think标签内的内容(原样保留)
+              const thinkContent = processedText.substring(thinkStart, thinkEnd + 8);
+              finalContent += thinkContent;
+              
+              currentPos = thinkEnd + 8;
+            }
+          } else {
+            // 如果没有think标签,进行普通的markdown渲染
+            finalContent = md.render(processedText);
+          }
+          
           // 添加AI回答
           messages.value.push({
             id: message.id,
             role: 'assistant',
-            content: md.render(formattedAnswer),
+            content: finalContent,
             time: new Date(message.createTime)
           })
         } catch (error) {

+ 3 - 3
src/views/DocSearch.vue

@@ -149,6 +149,7 @@ import { Search, View, Star, Download, Document, Files, Grid, PictureFilled, Fol
 import { get, post,put } from '../utils/request'
 import { useUserStore } from '../stores/user'
 import { useRoute, useRouter } from 'vue-router'
+import { downloadFile } from '../utils'
 const userStore = useUserStore()
 const router = useRouter()
 
@@ -270,15 +271,14 @@ const viewDocument = (doc) => {
 }
 
 const downloadDocument = async (doc) => {
-  // fileUrl是文件的地址,直接下载
   try {
-    window.open(doc.fileUrl, '_blank')
+    await downloadFile(doc.fileUrl, doc.fileName)
     // 添加文件下载记录
     await post('/admin/userHistoryLog/saveFileDownloadLog', {
       relatedId: doc.id
     })
   } catch (error) {
-    console.log('error', error);
+    console.log('error', error)
     ElMessage.error('下载失败,请重试文件地址:'+doc.fileUrl)
   }
 }

+ 47 - 0
src/views/Favorites.vue

@@ -45,6 +45,7 @@
             v-for="item in filteredFavorites"
             :key="item.id"
             class="favorite-item"
+            @click="handleItemClick(item)"
           >
             <div class="item-content">
               <el-checkbox 
@@ -141,6 +142,7 @@ import { ref, computed, nextTick, watch } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 // import { Search, Edit, Delete, Plus } from '@element-plus/icons-vue'
 import { get, post, del ,put} from '../utils/request'
+import { downloadFile } from '../utils'
 
 // 状态变量
 const currentType = ref('all')
@@ -415,6 +417,51 @@ const handleCurrentChange = (val) => {
   fetchFavorites()
 }
 
+const handleItemClick = async (item) => {
+  console.log('item', item);
+  // 处理点击
+  console.log('点击记录:', item)
+  console.log('item.relatedType',item.relatedType);
+  console.log('item.relatedType',item.relatedType=='ai.answer');
+  if (!item.relatedType) return;
+
+    switch (item.relatedType) {
+      case 'ai.answer':
+        // router.push(`/ai-chat?historyId=${item.id}`);
+        window.location.href = `/ai-chat?historyId=${item.id}`
+        break;
+      case 'scheme':
+        router.push(`/solution?historyId=${item.id}`);
+        break;
+      case 'file.download':
+        try {
+          await downloadFile(item.fileUrl, item.fileName)
+          // 添加文件下载记录
+          await post('/admin/userHistoryLog/saveFileDownloadLog', {
+            relatedId: item.relatedId
+          })
+        } catch (error) {
+          console.log('error', error)
+          ElMessage.error('下载失败,请重试文件地址:'+item.fileUrl)
+        }
+        break;
+      case 'file.search':
+        try {
+          await downloadFile(item.fileUrl, item.fileName)
+          // 添加文件下载记录
+          await post('/admin/userHistoryLog/saveFileDownloadLog', {
+            relatedId: item.relatedId
+          })
+        } catch (error) {
+          console.log('error', error)
+          ElMessage.error('下载失败,请重试文件地址:'+item.fileUrl)
+        }
+        break;
+      default:
+        break;
+    }
+}
+
 // 监听类型变化
 watch(currentType, () => {
   currentPage.value = 1 // 切换类型时重置为第一页

+ 39 - 19
src/views/History.vue

@@ -59,6 +59,7 @@
               v-for="item in filteredHistory"
               :key="item.id"
               class="favorite-item"
+              :class="{'file-download': item.relatedType === 'file.download'}"
             >
               <div class="item-content" @click="handleItemClick(item)">
                 <el-checkbox 
@@ -157,6 +158,7 @@
   import { Search, ArrowLeft } from '@element-plus/icons-vue'
   import { get, post, del ,put} from '../utils/request'
   import { useRouter } from 'vue-router'
+  import { downloadFile } from '../utils'
   
   // 将状态变量的声明移到最前面
   const searchText = ref('')
@@ -226,9 +228,9 @@
         pageSize: pageSize.value
       }
       const response = await get('admin/userHistoryLog/pageList', params)
-      console.log('response.rows', response.data.rows);
+      // console.log('response.rows', response.data.rows);
       history.value = response.data.rows || []
-      console.log('history.value', history.value);
+      // console.log('history.value', history.value);
       total.value = Number(response.data.total) || 0
     } catch (error) {
       ElMessage.error('获取列表失败')
@@ -368,18 +370,22 @@
   // 工具函数
   const getTagType = (type) => {
     const types = {
-      chat: '',
-      solution: 'success',
-      doc: 'warning'
+      'ai.answer': 'success',
+      scheme: 'success',
+      doc: 'warning',
+      'file.download': 'success',
+      'file.search': 'warning'
     }
     return types[type] || 'info'
   }
   
   const getTypeLabel = (type) => {
     const labels = {
-      chat: '对话',
-      solution: '方案',
-      doc: '文档'
+      'ai.answer': 'AI对话',
+      scheme: '方案生成',
+      doc: '文档',
+      'file.download': '下载文件',
+      'file.search': '文件检索'
     }
     return labels[type] || '未知'
   }
@@ -389,6 +395,7 @@
   }
   
   const getEmptyText = () => {
+    // console.log('currentType.value', currentType.value);
     if (searchText.value) {
       return '未找到匹配的历史'
     }
@@ -441,7 +448,7 @@
   // 类型选择相关
   const typeOptions = [
     { label: '全部', value: 'all' },
-    { label: 'AI问答', value: 'ai.answer' },
+    { label: 'AI对话', value: 'ai.answer' },
     { label: '方案生成', value: 'scheme' },
     { label: '下载文件', value: 'file.download' }
   ]
@@ -464,16 +471,17 @@
         router.push(`/solution?historyId=${item.id}`);
         break;
       case 'file.download':
-          try {
-              window.open(item.fileUrl, '_blank')
-              // 添加文件下载记录
-              await post('/admin/userHistoryLog/saveFileDownloadLog', {
-                relatedId: item.id
-              })
-            } catch (error) {
-              console.log('error', error);
-              ElMessage.error('下载失败,请重试文件地址:'+item.fileUrl)
-            }
+        try {
+          // console.log('item.fileName',item.fileName);
+          await downloadFile(item.fileUrl, item.fileName)
+          // 添加文件下载记录
+          await post('/admin/userHistoryLog/saveFileDownloadLog', {
+            relatedId: item.relatedId
+          })
+        } catch (error) {
+          console.log('error', error)
+          ElMessage.error('下载失败,请重试文件地址:'+item.fileUrl)
+        }
         // try {
         //   // 调用下载接口
         //   const response = await get(`admin/file/download/${item.id}`, {}, {
@@ -494,6 +502,18 @@
         //   ElMessage.error('文件下载失败');
         // }
         break;
+        case 'file.search':
+        try {
+          await downloadFile(item.fileUrl, item.fileName)
+          // 添加文件下载记录
+          await post('/admin/userHistoryLog/saveFileDownloadLog', {
+            relatedId: item.relatedId
+          })
+        } catch (error) {
+          console.log('error', error)
+          ElMessage.error('下载失败,请重试文件地址:'+item.fileUrl)
+        }
+        break;
       default:
         break;
     }