yangg 1 周之前
父節點
當前提交
4a064e264b

文件差異過大導致無法顯示
+ 0 - 0
dist/index.html


+ 0 - 0
dist/static/css/chunk-23d4d74d.e30e9787.css → dist/static/css/chunk-10bfc27e.6044f932.css


文件差異過大導致無法顯示
+ 0 - 0
dist/static/css/chunk-1c9b561d.80eea3be.css


文件差異過大導致無法顯示
+ 0 - 0
dist/static/css/chunk-f1038fe8.d708b27b.css


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/app.41ac9d19.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-10bfc27e.f2897e86.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-138f7583.d1cdd274.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-171bf614.3733cb86.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-171bf614.6c8878ea.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-1c9b561d.8ffee320.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-4ac7f82e.249ab73a.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-4ac7f82e.acb6c0ff.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-72e66010.12d246ef.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-72e66010.f8f3d288.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-730da84c.2d902ef3.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-730da84c.45f1f86b.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-89c9d7c6.102eb27f.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-9235c1ae.b59b6efa.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-9235c1ae.cef9beab.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-a23e70e2.1394c2e4.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-c6c6dd96.6cde946f.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-c6c6dd96.6d49c8d0.js


文件差異過大導致無法顯示
+ 0 - 0
dist/static/js/chunk-f1038fe8.8d8c52c4.js


+ 21 - 0
src/api/document.js

@@ -193,4 +193,25 @@ export function searchlistDocTemplate(data) {
     method: 'post',
     data
   })
+}
+
+/* 获取文档内容查询 - 问答对 */
+export function queryDocumentContent(data) {
+  return request({
+    url: '/document/content/query',
+    method: 'get',
+    params: data
+  })
+}
+
+/* 导出问答对到Excel */
+export function exportQaToExcel(data) {
+  return request({
+    url: '/document/content/export_qa_to_excel/',
+    method: 'post',
+    data,
+    headers: {
+      'Content-Type': 'application/json'
+    }
+  })
 }

+ 7 - 0
src/router/index.js

@@ -400,6 +400,13 @@ export const asyncRoutes = [
         name: 'InputFormList',
         meta: { title: '输入信息表', noCache: true,roles: ['admin'] }//输入项目
       },
+      /* 查看问答对 */
+   /*    {
+        path: 'category/qa',
+        component: () => import('@/views/knowledgeMenu/category/qa'),
+        name: 'qa',
+        meta: { title: '问答对', noCache: true,roles: ['admin'] }
+      }, */
     ]
   },
     {

+ 103 - 0
src/utils/data-validator.js

@@ -0,0 +1,103 @@
+/**
+ * 数据验证和格式化工具
+ */
+
+/**
+ * 清理对象中的空值
+ * @param {Object} obj - 需要清理的对象
+ * @param {Boolean} removeEmptyArrays - 是否移除空数组
+ * @returns {Object} - 清理后的对象
+ */
+export function cleanEmptyValues(obj, removeEmptyArrays = false) {
+  const cleaned = {};
+  
+  Object.keys(obj).forEach(key => {
+    const value = obj[key];
+    
+    if (value === null || value === undefined || value === '') {
+      // 跳过空值
+      return;
+    }
+    
+    if (removeEmptyArrays && Array.isArray(value) && value.length === 0) {
+      // 可选:跳过空数组
+      return;
+    }
+    
+    // 递归处理嵌套对象
+    if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
+      const cleanedNested = cleanEmptyValues(value, removeEmptyArrays);
+      if (Object.keys(cleanedNested).length > 0) {
+        cleaned[key] = cleanedNested;
+      }
+    } else {
+      cleaned[key] = value;
+    }
+  });
+  
+  return cleaned;
+}
+
+/**
+ * 验证导出参数格式
+ * @param {Object} params - 导出参数
+ * @returns {Object} - 包含验证结果和清理后的参数
+ */
+export function validateExportParams(params) {
+  const errors = [];
+  const warnings = [];
+  
+  // 必填字段验证
+  if (!params.kb_id) {
+    errors.push('kb_id 是必填字段');
+  }
+  
+  // 类型验证
+  if (params.document_ids && !Array.isArray(params.document_ids)) {
+    errors.push('document_ids 必须是数组类型');
+  }
+  
+  if (params.doc_type_id !== undefined && params.doc_type_id !== null && typeof params.doc_type_id !== 'number') {
+    warnings.push('doc_type_id 应该是数字类型');
+  }
+  
+  // 清理参数
+  const cleanedParams = cleanEmptyValues(params);
+  
+  return {
+    isValid: errors.length === 0,
+    errors,
+    warnings,
+    cleanedParams
+  };
+}
+
+/**
+ * 格式化JSON用于日志输出
+ * @param {Object} data - 要格式化的数据
+ * @param {String} title - 日志标题
+ */
+export function logJSON(data, title = 'Data') {
+  console.group(`📋 ${title}`);
+  console.log('JSON字符串:', JSON.stringify(data, null, 2));
+  console.log('对象结构:', data);
+  console.groupEnd();
+}
+
+/**
+ * 检查JSON数据大小
+ * @param {Object} data - 要检查的数据
+ * @returns {Object} - 包含大小信息的对象
+ */
+export function checkJSONSize(data) {
+  const jsonString = JSON.stringify(data);
+  const sizeInBytes = new Blob([jsonString]).size;
+  const sizeInKB = (sizeInBytes / 1024).toFixed(2);
+  
+  return {
+    bytes: sizeInBytes,
+    kb: sizeInKB,
+    string: jsonString,
+    isLarge: sizeInBytes > 1024 * 1024 // 超过1MB认为是大数据
+  };
+} 

+ 355 - 0
src/views/knowledgeMenu/category/knowledgeSet.vue

@@ -89,6 +89,9 @@
             <i data-feather="stop-circle" class="icons"></i>
             终止所有任务
           </el-button>
+          <el-button type="primary" @click="exportData()">
+            <i data-feather="download" class="icons"></i> 导出
+          </el-button>
         </div>
         <div class="selection-control" v-if="selectedRows.length > 0">
           <el-alert
@@ -272,6 +275,21 @@
                       >
                       </el-button>
                     </el-tooltip>
+                    <el-tooltip
+                      class="item"
+                      effect="dark"
+                      content="查看问答对"
+                      placement="top"
+                    >
+                      <el-button
+                        type="text"
+                        size="small"
+                        circle
+                        icon="el-icon-receiving"
+                        @click="getQa(scope.row.id)"
+                        style="font-size: 15px"
+                      ></el-button>
+                    </el-tooltip>
                     <el-tooltip
                       class="item"
                       effect="dark"
@@ -463,6 +481,67 @@
             >
           </span>
         </el-dialog>
+
+        <!-- 问答对弹窗 -->
+        <el-dialog
+          title="问答对"
+          :visible.sync="qaDialogVisible"
+          width="80%"
+          :before-close="handleQaDialogClose"
+        >
+          <div class="qa-dialog-content">
+            <div class="qa-dialog-header">
+              <el-tag>文档ID: {{ currentDocumentId }}</el-tag>
+              <el-tag type="success" v-if="qaList.length">
+                共 {{ qaList.length }} 个问答对
+              </el-tag>
+            </div>
+
+            <div class="qa-content" v-loading="qaLoading">
+              <div v-if="qaList.length === 0 && !qaLoading" class="empty-state">
+                <i class="el-icon-document"></i>
+                <p>暂无问答对数据</p>
+              </div>
+
+              <div v-else class="qa-list">
+                <div 
+                  v-for="(item, index) in qaList" 
+                  :key="index" 
+                  class="qa-item"
+                >
+                  <div class="qa-question">
+                    <div class="qa-label">问:</div>
+                    <div class="qa-text">{{ item.question }}</div>
+                  </div>
+                  <div class="qa-answer">
+                    <div class="qa-label">答:</div>
+                    <div class="qa-text">{{ item.answer }}</div>
+                  </div>
+                  <div class="qa-meta" v-if="item.confidence || item.source">
+                    <el-tag size="mini" v-if="item.confidence">
+                      置信度: {{ item.confidence }}
+                    </el-tag>
+                    <el-tag size="mini" type="info" v-if="item.source">
+                      来源: {{ item.source }}
+                    </el-tag>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <span slot="footer" class="dialog-footer">
+            <el-button 
+              type="primary" 
+              icon="el-icon-refresh" 
+              @click="fetchQAData"
+              :loading="qaLoading"
+            >
+              刷新数据
+            </el-button>
+            <el-button @click="handleQaDialogClose">关闭</el-button>
+          </span>
+        </el-dialog>
       </div>
     </div>
   </div>
@@ -492,6 +571,8 @@ import {
   resultTask,
   endClear,
 } from "@/api/knowledge";
+import { exportQaToExcel,queryDocumentContent } from "@/api/document";
+import { validateExportParams, logJSON, checkJSONSize } from "@/utils/data-validator";
 export default {
   components: {
     dataSearch,
@@ -574,6 +655,11 @@ export default {
       selectedRows: [], // 已选择的行
       allSelectedRows: new Map(), // 存储所有选中的行
       isAllDataSelected: false, // 是否选中所有数据
+      /* 问答对弹窗 */
+      qaDialogVisible: false,
+      currentDocumentId: null,
+      qaList: [],
+      qaLoading: false,
     };
   },
   mounted() {
@@ -589,6 +675,185 @@ export default {
     Object.values(this.analysisStatusCheckers).forEach(clearTimeout);
   },
   methods: {
+    /* 导出 */
+    async exportData() {
+      try {
+        // 显示加载状态
+        const loading = this.$loading({
+          lock: true,
+          text: '正在导出,请稍候...',
+          spinner: 'el-icon-loading',
+          background: 'rgba(0, 0, 0, 0.7)'
+        });
+
+        // 准备导出参数
+        const exportParams = {
+          kb_id: this.queryForm.bucket_id, // 知识库ID
+          doc_type_id: this.queryForm.doc_type_id === 0 ? null : this.queryForm.doc_type_id, // 目录ID,0表示全部
+        };
+
+        // 如果有选中的文档,只导出选中的
+        if (this.selectedRows.length > 0) {
+          exportParams.document_ids = this.selectedRows.map(row => row.id);
+        }
+
+        // 验证和清理导出参数
+        const validation = validateExportParams(exportParams);
+        
+        if (!validation.isValid) {
+          loading.close();
+          this.$message.error(`参数验证失败: ${validation.errors.join(', ')}`);
+          return;
+        }
+
+        if (validation.warnings.length > 0) {
+          console.warn('参数警告:', validation.warnings);
+        }
+
+        // 使用清理后的参数
+        const finalParams = validation.cleanedParams;
+        
+        // 检查JSON数据大小
+        const sizeInfo = checkJSONSize(finalParams);
+        if (sizeInfo.isLarge) {
+          console.warn(`导出数据较大: ${sizeInfo.kb}KB`);
+        }
+
+        // 日志输出
+        logJSON(finalParams, '最终导出参数');
+
+        // 调用导出接口
+        const response = await exportQaToExcel(finalParams);
+        // 检查响应格式
+        if (response && response.status === 200 ) {
+          const exportData = response.data.data;
+          console.log(exportData);
+          // 如果任务正在处理中,显示提示信息
+          if (exportData.status === 'processing') {
+            loading.close();
+           /*  this.$message.info('导出任务已提交,正在处理中...'); */
+            
+            // 如果有下载链接,直接下载
+            if (exportData.download_url) {
+              this.downloadFile(exportData.download_url, exportData.file_name);
+            }
+            return;
+          }
+          
+          // 如果任务已完成,直接下载
+          if (exportData.download_url) {
+            this.downloadFile(exportData.download_url, exportData.file_name);
+            loading.close();
+            this.$message.success('导出成功!');
+          } else {
+            loading.close();
+            this.$message.error('获取下载链接失败');
+          }
+        } else {
+          // 兼容原来的直接返回文件流的情况
+          const blob = new Blob([response], {
+            type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+          });
+          
+          const url = window.URL.createObjectURL(blob);
+          const link = document.createElement('a');
+          link.href = url;
+          
+          // 设置文件名
+          const fileName = `问答对导出_${new Date().toISOString().slice(0, 10)}.xlsx`;
+          link.download = fileName;
+          
+          // 触发下载
+          document.body.appendChild(link);
+          link.click();
+          document.body.removeChild(link);
+          window.URL.revokeObjectURL(url);
+
+          loading.close();
+          this.$message.success('导出成功!');
+        }
+        
+      } catch (error) {
+        console.error('导出失败:', error);
+        this.$message.error('导出失败,请稍后重试');
+        if (loading) loading.close();
+      }
+    },
+
+    // 下载文件的通用方法
+    downloadFile(downloadUrl, fileName) {
+      try {
+        const link = document.createElement('a');
+        link.href = downloadUrl;
+        link.download = fileName || `问答对导出_${new Date().toISOString().slice(0, 10)}.xlsx`;
+        link.target = '_blank'; // 在新窗口打开,避免跨域问题
+        
+        // 触发下载
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+        
+        this.$message.success('文件下载已开始!');
+      } catch (error) {
+        console.error('下载失败:', error);
+        this.$message.error('文件下载失败,请稍后重试');
+      }
+    },
+    /* 查看问答对 */
+    getQa(id) {
+      this.currentDocumentId = id;
+      this.qaDialogVisible = true;
+      this.fetchQAData();
+    },
+    
+    /* 获取问答对数据 */
+    async fetchQAData() {
+      if (!this.currentDocumentId) return;
+      
+      this.qaLoading = true;
+      try {
+        const response = await queryDocumentContent({
+          document_id: this.currentDocumentId
+        });
+        
+        if (response.status === 200 ) {
+          // 根据接口返回的数据结构调整
+          this.qaList = response.data[0].qa_pairs || response.result || [];
+          
+          // 如果接口返回的不是数组格式,进行处理
+          if (!Array.isArray(this.qaList)) {
+            if (this.qaList.items) {
+              this.qaList = this.qaList.items;
+            } else if (this.qaList.qa_pairs) {
+              this.qaList = this.qaList.qa_pairs;
+              console.log(this.qaList);
+            } else {
+              this.qaList = [];
+            }
+          }
+          
+          if (this.qaList.length === 0) {
+            this.$message.info('该文档暂无问答对数据');
+          }
+        } else {
+          this.$message.error(response.message || '获取问答对数据失败');
+          this.qaList = [];
+        }
+      } catch (error) {
+        console.error('获取问答对数据失败:', error);
+        this.$message.error('获取问答对数据失败,请稍后重试');
+        this.qaList = [];
+      } finally {
+        this.qaLoading = false;
+      }
+    },
+    
+    /* 关闭问答对弹窗 */
+    handleQaDialogClose() {
+      this.qaDialogVisible = false;
+      this.currentDocumentId = null;
+      this.qaList = [];
+    },
     // 获取行的唯一标识
     getRowKey(row) {
       return row.id;
@@ -1755,5 +2020,95 @@ export default {
   justify-content: flex-end;
   flex-wrap: wrap;
 }
+/* 问答对弹窗样式 */
+.qa-dialog-content {
+  padding: 0;
+}
+
+.qa-dialog-header {
+  margin-bottom: 20px;
+  padding-bottom: 12px;
+  border-bottom: 1px solid #e8e8e8;
+  display: flex;
+  gap: 8px;
+  align-items: center;
+}
+
+.qa-content {
+  min-height: 300px;
+  max-height: 500px;
+  overflow-y: auto;
+}
+
+.empty-state {
+  text-align: center;
+  padding: 60px 0;
+  color: #999;
+}
+
+.empty-state i {
+  font-size: 48px;
+  color: #ddd;
+  margin-bottom: 16px;
+  display: block;
+}
+
+.empty-state p {
+  margin: 0;
+  font-size: 16px;
+}
+
+.qa-list {
+  space-y: 20px;
+}
+
+.qa-item {
+  background: #f8f9fa;
+  border-radius: 8px;
+  padding: 20px;
+  margin-bottom: 16px;
+  border-left: 4px solid #409eff;
+  transition: all 0.3s ease;
+}
+
+.qa-item:hover {
+  box-shadow: 0 2px 12px rgba(64, 158, 255, 0.1);
+}
+
+.qa-question, .qa-answer {
+  display: flex;
+  margin-bottom: 12px;
+  align-items: flex-start;
+}
+
+.qa-answer {
+  margin-bottom: 16px;
+}
+
+.qa-label {
+  flex-shrink: 0;
+  width: 40px;
+  font-weight: bold;
+  color: #409eff;
+  margin-right: 8px;
+}
+
+.qa-question .qa-label {
+  color: #e6a23c;
+}
+
+.qa-text {
+  flex: 1;
+  line-height: 1.6;
+  color: #333;
+  word-break: break-word;
+}
+
+.qa-meta {
+  display: flex;
+  gap: 8px;
+  padding-top: 8px;
+  border-top: 1px solid #eee;
+}
 </style>
     

+ 218 - 0
src/views/knowledgeMenu/category/qa.vue

@@ -0,0 +1,218 @@
+<template>
+  <div class="qa-container">
+    <div class="qa-header">
+      <h1>问答对</h1>
+      <div class="header-info">
+        <el-tag>文档ID: {{ documentId }}</el-tag>
+        <el-tag type="success" v-if="qaList.length">
+          共 {{ qaList.length }} 个问答对
+        </el-tag>
+      </div>
+    </div>
+
+    <div class="qa-content" v-loading="loading">
+      <div v-if="qaList.length === 0 && !loading" class="empty-state">
+        <i class="el-icon-document"></i>
+        <p>暂无问答对数据</p>
+      </div>
+
+      <div v-else class="qa-list">
+        <div 
+          v-for="(item, index) in qaList" 
+          :key="index" 
+          class="qa-item"
+        >
+          <div class="qa-question">
+            <div class="qa-label">问:</div>
+            <div class="qa-text">{{ item.question }}</div>
+          </div>
+          <div class="qa-answer">
+            <div class="qa-label">答:</div>
+            <div class="qa-text">{{ item.answer }}</div>
+          </div>
+          <div class="qa-meta" v-if="item.confidence || item.source">
+            <el-tag size="mini" v-if="item.confidence">
+              置信度: {{ item.confidence }}
+            </el-tag>
+            <el-tag size="mini" type="info" v-if="item.source">
+              来源: {{ item.source }}
+            </el-tag>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 刷新按钮 -->
+    <div class="qa-actions">
+      <el-button 
+        type="primary" 
+        icon="el-icon-refresh" 
+        @click="fetchQAData"
+        :loading="loading"
+      >
+        刷新数据
+      </el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import { queryDocumentContent } from '@/api/document'
+
+export default {
+  name: "Qa",
+  data() {
+    return {
+      documentId: 8559, // 文档ID
+      qaList: [], // 问答对列表
+      loading: false // 加载状态
+    }
+  },
+  mounted() {
+    this.fetchQAData()
+  },
+  methods: {
+    // 获取问答对数据
+    async fetchQAData() {
+      this.loading = true
+      try {
+        const response = await queryDocumentContent({
+          document_id: this.documentId
+        })
+        
+        if (response.code === 20000 || response.success) {
+          // 根据接口返回的数据结构调整
+          this.qaList = response.data || response.result || []
+          
+          // 如果接口返回的不是数组格式,进行处理
+          if (!Array.isArray(this.qaList)) {
+            if (this.qaList.items) {
+              this.qaList = this.qaList.items
+            } else if (this.qaList.qa_pairs) {
+              this.qaList = this.qaList.qa_pairs
+            } else {
+              this.qaList = []
+            }
+          }
+        } else {
+          this.$message.error(response.message || '获取问答对数据失败')
+        }
+      } catch (error) {
+        console.error('获取问答对数据失败:', error)
+        this.$message.error('获取问答对数据失败,请稍后重试')
+        this.qaList = []
+      } finally {
+        this.loading = false
+      }
+    }
+  }
+};
+</script>
+
+<style scoped>
+.qa-container {
+  padding: 20px;
+  background: #fff;
+  min-height: 100vh;
+}
+
+.qa-header {
+  margin-bottom: 24px;
+  padding-bottom: 16px;
+  border-bottom: 1px solid #e8e8e8;
+}
+
+.qa-header h1 {
+  margin: 0 0 12px 0;
+  color: #333;
+  font-size: 24px;
+  font-weight: 500;
+}
+
+.header-info {
+  display: flex;
+  gap: 8px;
+  align-items: center;
+}
+
+.qa-content {
+  min-height: 300px;
+}
+
+.empty-state {
+  text-align: center;
+  padding: 60px 0;
+  color: #999;
+}
+
+.empty-state i {
+  font-size: 48px;
+  color: #ddd;
+  margin-bottom: 16px;
+}
+
+.empty-state p {
+  margin: 0;
+  font-size: 16px;
+}
+
+.qa-list {
+  space-y: 20px;
+}
+
+.qa-item {
+  background: #f8f9fa;
+  border-radius: 8px;
+  padding: 20px;
+  margin-bottom: 16px;
+  border-left: 4px solid #409eff;
+  transition: all 0.3s ease;
+}
+
+.qa-item:hover {
+  box-shadow: 0 2px 12px rgba(64, 158, 255, 0.1);
+}
+
+.qa-question, .qa-answer {
+  display: flex;
+  margin-bottom: 12px;
+  align-items: flex-start;
+}
+
+.qa-answer {
+  margin-bottom: 16px;
+}
+
+.qa-label {
+  flex-shrink: 0;
+  width: 40px;
+  font-weight: bold;
+  color: #409eff;
+  margin-right: 8px;
+}
+
+.qa-question .qa-label {
+  color: #e6a23c;
+}
+
+.qa-text {
+  flex: 1;
+  line-height: 1.6;
+  color: #333;
+  word-break: break-word;
+}
+
+.qa-meta {
+  display: flex;
+  gap: 8px;
+  padding-top: 8px;
+  border-top: 1px solid #eee;
+}
+
+.qa-actions {
+  margin-top: 24px;
+  text-align: center;
+  padding-top: 20px;
+  border-top: 1px solid #e8e8e8;
+}
+</style>

部分文件因文件數量過多而無法顯示