Browse Source

问答版本

xueyiming 2 weeks ago
parent
commit
b9af5fc20d
2 changed files with 205 additions and 14 deletions
  1. 188 13
      src/views/KnowledgeContent.vue
  2. 17 1
      src/views/QAndA.vue

+ 188 - 13
src/views/KnowledgeContent.vue

@@ -1,9 +1,11 @@
 <template>
   <el-container style="height: 100vh;">
     <!-- 上方的 Header -->
-    <el-header style="height: 50px; background: linear-gradient(to right, #6db3f2, #1e69e8); color: white; display: flex; align-items: center; padding: 0 20px;">
+    <el-header
+        style="height: 50px; background: linear-gradient(to right, #6db3f2, #1e69e8); color: white; display: flex; align-items: center; padding: 0 20px;">
       <!-- 返回按钮 -->
-      <el-button @click="goBack" style="margin-right: 20px; background-color: #fff; color: #409EFF; border: 1px solid #409EFF;">
+      <el-button @click="goBack"
+                 style="margin-right: 20px; background-color: #fff; color: #409EFF; border: 1px solid #409EFF;">
         返回
       </el-button>
 
@@ -13,25 +15,56 @@
       </div>
 
       <!-- 添加新知识按钮 -->
-      <el-button @click="openDialog" style="border-radius: 20px; background-color: #fff; color: #409EFF; border: 1px solid #409EFF;">
+      <el-button @click="openDialog"
+                 style="border-radius: 20px; background-color: #fff; color: #409EFF; border: 1px solid #409EFF;">
         添加新知识
       </el-button>
     </el-header>
 
     <el-container>
       <!-- 左侧菜单栏 -->
-      <el-aside width="330px" style="background-color: #f4f4f4; padding: 10px; display: flex; flex-direction: column; overflow-y: auto; height: calc(100vh - 60px);">
+      <el-aside
+          width="330px"
+          style="background-color: #f4f4f4; padding: 10px; display: flex; flex-direction: column; overflow-y: auto; height: calc(100vh - 60px);"
+      >
         <!-- 左侧标题,居中显示 -->
-        <div style="font-size: 18px; font-weight: bold; margin-bottom: 10px; text-align: center; width: 100%;">
+        <div
+            style="font-size: 18px; font-weight: bold; margin-bottom: 10px; text-align: center; width: 100%;"
+        >
           内容列表
         </div>
 
-        <el-menu :default-active="activeItem" @select="handleSelect" style="border-right: none; flex-grow: 1;">
-          <el-menu-item v-for="item in currentPageItems" :key="item.doc_id" :index="item.doc_id">
-            {{ item.text.substring(0, 20) + '...' }}
+        <!-- 菜单,只有在有数据时才渲染 -->
+        <el-menu
+            v-if="currentPageItems && currentPageItems.length > 0"
+            :default-active="activeItem"
+            @select="handleSelect"
+            style="border-right: none; flex-grow: 1;"
+        >
+          <el-menu-item
+              v-for="item in currentPageItems"
+              :key="item.doc_id"
+              :index="String(item.doc_id)"
+              style="display: flex; justify-content: space-between; align-items: center;"
+          >
+            <!-- 左边文字:固定宽度 + 超出省略号 -->
+            <span class="menu-text">{{ item.title || item.text }}</span>
+
+            <!-- 删除按钮 -->
+            <el-icon
+                style="cursor: pointer; font-size: 18px; color: #f56c6c; display: flex; align-items: center; justify-content: center;"
+                @click.stop="confirmDelete(item.doc_id, item.title)"
+            >
+              <Delete/>
+            </el-icon>
           </el-menu-item>
         </el-menu>
 
+        <!-- 没有数据时的占位 -->
+        <div v-else style="text-align: center; color: #999; padding: 20px;">
+          暂无内容
+        </div>
+
         <!-- 分页 -->
         <el-pagination
             v-model:current-page="pageIndex"
@@ -45,10 +78,61 @@
       </el-aside>
 
       <!-- 右侧内容区域 -->
-      <el-main style="padding: 20px; overflow-y: auto; height: calc(100vh - 60px);">
+      <el-main style="padding: 20px; overflow-y: auto; height: calc(100vh - 60px); position: relative;">
+        <!-- 右上角按钮 -->
+        <!-- 右上角按钮:有内容时才显示 -->
+        <el-button
+            v-if="selectedContent"
+            size="small"
+            type="primary"
+            style="position: absolute; top: 10px; right: 10px;"
+            @click="openChunkDialog"
+        >
+          展示分段
+        </el-button>
+
         <h2>{{ selectedTitle }}</h2>
         <p>{{ selectedContent }}</p>
       </el-main>
+
+      <!-- 分段展示弹窗 -->
+      <el-dialog
+          title="文档分段"
+          v-model="chunkDialogVisible"
+          width="80%"
+          :destroy-on-close="true"
+          :top="'5vh'"
+      >
+        <div style="max-height: 80%; overflow-y: auto;">
+          <!-- 延迟渲染,避免动画阶段触发 ResizeObserver -->
+          <el-table
+              v-if="chunkDialogVisible && chunkList.length > 0"
+              :data="chunkList"
+              style="width: 100%; min-height: 100px;"
+          >
+            <el-table-column prop="chunk_id" label="分段ID" width="100" header-align="center" align="center"/>
+            <el-table-column prop="summary" label="总结" width="300" header-align="center"/>
+            <el-table-column prop="text" label="内容" header-align="center"/>
+          </el-table>
+
+
+          <!-- 分页器 -->
+          <div style="margin-top: 10px; text-align: center;">
+            <el-pagination
+                v-model:current-page="chunkPage"
+                :page-size="chunkPageSize"
+                :total="chunkTotal"
+                layout="prev, pager, next"
+                @current-change="fetchChunks"
+            />
+          </div>
+        </div>
+      </el-dialog>
+
+
+
+
+
     </el-container>
 
     <!-- 弹窗 -->
@@ -73,11 +157,11 @@
 </template>
 
 <script lang="ts">
-import { defineComponent, onMounted, ref, computed } from 'vue';
-import { useRouter } from 'vue-router'; // 引入 vue-router 用于路由跳转
+import {defineComponent, onMounted, ref, computed} from 'vue';
+import {useRouter} from 'vue-router'; // 引入 vue-router 用于路由跳转
 import axios from 'axios';
-import { ElMessage } from 'element-plus'; // 导入 ElMessage
-
+import {ElMessage, ElMessageBox} from 'element-plus';
+import {Delete} from '@element-plus/icons-vue';
 
 // 定义接口类型
 interface Item {
@@ -114,6 +198,13 @@ export default defineComponent({
 
     const dialogVisible = ref(false); // 控制弹窗的显示和隐藏
 
+    // 分段弹窗
+    const chunkDialogVisible = ref(false);
+    const chunkList = ref<any[]>([]);
+    const chunkPage = ref(1);
+    const chunkPageSize = ref(10);
+    const chunkTotal = ref(0);
+
     // 获取API数据并填充项
     const fetchData = async (datasetId: number) => {
       console.log(datasetId);
@@ -226,6 +317,71 @@ export default defineComponent({
       }
     };
 
+    // 确认删除
+    const confirmDelete = (doc_id: string, title: string | null) => {
+      ElMessageBox.confirm(
+          `确定要删除文档 "${title || doc_id}" 吗?`,
+          '删除确认',
+          {
+            confirmButtonText: '删除',
+            cancelButtonText: '取消',
+            type: 'warning',
+          }
+      ).then(() => {
+        deleteDoc(doc_id);
+      }).catch(() => {
+        // 用户取消,不处理
+      });
+    };
+
+    // 删除文档方法
+    const deleteDoc = async (doc_id: string) => {
+      try {
+        await axios.post('http://192.168.100.31:8001/api/delete', {
+          level: 'doc',
+          params: {doc_id}
+        });
+        ElMessage.success('删除成功');
+        if (datasetId.value !== null) {
+          fetchData(datasetId.value); // 刷新列表
+        }
+      } catch (error) {
+        console.error(error);
+        ElMessage.error('删除失败');
+      }
+    };
+
+    // 打开分段弹窗
+    const openChunkDialog = async () => {
+      if (!activeItem.value) {
+        ElMessage.warning("请先选择一个文档");
+        return;
+      }
+      chunkDialogVisible.value = true;
+      await fetchChunks(chunkPage.value);
+    };
+
+// 获取分段数据
+    const fetchChunks = async (page: number) => {
+      if (!activeItem.value) return;
+      try {
+        const response = await axios.get('http://61.48.133.26:8001/api/chunk/list', {
+          params: {
+            page,
+            pageSize: chunkPageSize.value,
+            docId: activeItem.value,
+          }
+        });
+        const data = response.data.data;
+        chunkList.value = data.entities;
+        chunkTotal.value = data.total_count;
+        chunkPage.value = data.page;
+      } catch (err) {
+        console.error(err);
+        ElMessage.error("获取分段失败");
+      }
+    };
+
     return {
       activeItem,
       selectedTitle,
@@ -243,6 +399,16 @@ export default defineComponent({
       openDialog,
       submitData,
       resetForm,
+      confirmDelete,
+      deleteDoc,
+      Delete,
+      chunkDialogVisible,
+      chunkList,
+      chunkPage,
+      chunkPageSize,
+      chunkTotal,
+      openChunkDialog,
+      fetchChunks,
     };
   },
 });
@@ -274,4 +440,13 @@ export default defineComponent({
   overflow: hidden; /* 超出部分隐藏 */
   text-overflow: ellipsis; /* 使用省略号代替超出的文本 */
 }
+
+.menu-text {
+  max-width: 200px; /* 固定宽度 */
+  display: inline-block;
+  white-space: nowrap; /* 不换行 */
+  overflow: hidden; /* 超出隐藏 */
+  text-overflow: ellipsis; /* 显示省略号 */
+}
+
 </style>

+ 17 - 1
src/views/QAndA.vue

@@ -15,6 +15,7 @@
 
     <!-- 右侧搜索框和搜索结果 -->
     <div class="search-area">
+      <!-- 搜索框 + 提问按钮 -->
       <div class="search-container">
         <el-input
             v-model="query"
@@ -23,9 +24,18 @@
             @keyup.enter="chat"
             class="search-input"
         ></el-input>
-        <el-button @click="chat" type="primary" style="margin-left: 15px">提问</el-button>
+        <!-- 当 loading 时禁用按钮 -->
+        <el-button
+            @click="chat"
+            type="primary"
+            :disabled="loading"
+            style="margin-left: 15px"
+        >
+          {{ "提问" }}
+        </el-button>
       </div>
 
+
       <!-- 搜索中的提示 -->
       <div v-if="loading" class="loading-spinner">回答中...</div>
       <!-- 展示 chat_res 数据 -->
@@ -127,6 +137,7 @@ const toggleDatasetSelection = (datasetId) => {
 
 // 执行搜索操作
 const chat = async () => {
+  if (loading.value) return; // 防止重复调用
   if (!query.value.trim()) {
     ElMessage.warning('请输入提问内容');
     return;
@@ -137,6 +148,11 @@ const chat = async () => {
     return;
   }
 
+  chatSummary.value = '';
+  searchResults.value = [];
+  selectedResult.value = {};
+  originalContent.value = '';
+
   loading.value = true; // 开始搜索时显示加载提示
   const datasetIds = selectedDatasetIds.value.join(',');
   try {