|
|
@@ -1,124 +1,106 @@
|
|
|
<template>
|
|
|
- <div class="p-2">
|
|
|
+ <div style="width: 100%; height: auto;">
|
|
|
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
|
|
<div v-show="showSearch" class="mb-[10px]">
|
|
|
- <el-card shadow="hover">
|
|
|
+ <div class="p-4 bg-white rounded">
|
|
|
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
|
|
- <el-form-item label="知识库ID" prop="knowledgeCode">
|
|
|
- <el-input v-model="queryParams.knowledgeCode" placeholder="请输入知识库ID" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
<el-form-item label="知识库名称" prop="knowledgeName">
|
|
|
<el-input v-model="queryParams.knowledgeName" placeholder="请输入知识库名称" clearable @keyup.enter="handleQuery" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="描述" prop="description">
|
|
|
<el-input v-model="queryParams.description" placeholder="请输入描述" clearable @keyup.enter="handleQuery" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="用户ID" prop="userId">
|
|
|
- <el-input v-model="queryParams.userId" placeholder="请输入用户ID" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="是否公开知识库" prop="share">
|
|
|
- <el-input v-model="queryParams.share" placeholder="请输入是否公开知识库" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="知识分隔符" prop="knowledgeSeparator">
|
|
|
- <el-input v-model="queryParams.knowledgeSeparator" placeholder="请输入知识分隔符" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="提问分隔符" prop="questionSeparator">
|
|
|
- <el-input v-model="queryParams.questionSeparator" placeholder="请输入提问分隔符" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="重叠字符数" prop="overlapChar">
|
|
|
- <el-input v-model="queryParams.overlapChar" placeholder="请输入重叠字符数" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="知识库中检索的条数" prop="retrieveLimit">
|
|
|
- <el-input v-model="queryParams.retrieveLimit" placeholder="请输入知识库中检索的条数" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="文本块大小" prop="textBlockSize">
|
|
|
- <el-input v-model="queryParams.textBlockSize" placeholder="请输入文本块大小" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="向量库" prop="vectorModelName">
|
|
|
- <el-input v-model="queryParams.vectorModelName" placeholder="请输入向量库" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="向量模型" prop="embeddingModelName">
|
|
|
- <el-input v-model="queryParams.embeddingModelName" placeholder="请输入向量模型" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="系统提示词" prop="systemPrompt">
|
|
|
- <el-input v-model="queryParams.systemPrompt" placeholder="请输入系统提示词" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
<el-form-item>
|
|
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
- </el-card>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</transition>
|
|
|
|
|
|
- <el-card shadow="never">
|
|
|
- <template #header>
|
|
|
- <el-row :gutter="10" class="mb8">
|
|
|
- <el-col :span="1.5">
|
|
|
- <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:knowledgeInfo:add']">新增</el-button>
|
|
|
- </el-col>
|
|
|
- <el-col :span="1.5">
|
|
|
- <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:knowledgeInfo:edit']">修改</el-button>
|
|
|
- </el-col>
|
|
|
- <el-col :span="1.5">
|
|
|
- <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:knowledgeInfo:remove']">删除</el-button>
|
|
|
- </el-col>
|
|
|
- <el-col :span="1.5">
|
|
|
- <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:knowledgeInfo:export']">导出</el-button>
|
|
|
- </el-col>
|
|
|
+ <div class="bg-white p-4 rounded">
|
|
|
+ <div class="flex justify-between items-center mb-4">
|
|
|
+ <h2 class="text-xl font-bold">知识库列表</h2>
|
|
|
+ <div class="flex space-x-2">
|
|
|
+ <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:knowledgeInfo:add']">新增</el-button>
|
|
|
+ <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:knowledgeInfo:remove']">删除</el-button>
|
|
|
+ <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:knowledgeInfo:export']">导出</el-button>
|
|
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
|
|
- </el-row>
|
|
|
- </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
|
|
|
- <el-table v-loading="loading" border :data="knowledgeInfoList" @selection-change="handleSelectionChange">
|
|
|
+ <el-table
|
|
|
+ v-loading="loading"
|
|
|
+ :data="knowledgeInfoList"
|
|
|
+ @selection-change="handleSelectionChange"
|
|
|
+ class="compact-table"
|
|
|
+ >
|
|
|
<el-table-column type="selection" width="55" align="center" />
|
|
|
- <el-table-column label="" align="center" prop="id" v-if="true" />
|
|
|
- <el-table-column label="知识库ID" align="center" prop="knowledgeCode" />
|
|
|
+ <el-table-column label="知识库编号" align="center" prop="knowledgeCode" />
|
|
|
<el-table-column label="知识库名称" align="center" prop="knowledgeName" />
|
|
|
- <el-table-column label="描述" align="center" prop="description" />
|
|
|
- <el-table-column label="用户ID" align="center" prop="userId" />
|
|
|
- <el-table-column label="是否公开知识库" align="center" prop="share" />
|
|
|
- <el-table-column label="知识分隔符" align="center" prop="knowledgeSeparator" />
|
|
|
- <el-table-column label="提问分隔符" align="center" prop="questionSeparator" />
|
|
|
- <el-table-column label="重叠字符数" align="center" prop="overlapChar" />
|
|
|
- <el-table-column label="知识库中检索的条数" align="center" prop="retrieveLimit" />
|
|
|
- <el-table-column label="文本块大小" align="center" prop="textBlockSize" />
|
|
|
- <el-table-column label="向量库" align="center" prop="vectorModelName" />
|
|
|
- <el-table-column label="向量模型" align="center" prop="embeddingModelName" />
|
|
|
- <el-table-column label="系统提示词" align="center" prop="systemPrompt" />
|
|
|
- <el-table-column label="备注" align="center" prop="remark" />
|
|
|
+ <el-table-column label="描述" align="center" prop="description" width="600" ellipsis />
|
|
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
|
|
<template #default="scope">
|
|
|
- <el-tooltip content="修改" placement="top">
|
|
|
- <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:knowledgeInfo:edit']"></el-button>
|
|
|
- </el-tooltip>
|
|
|
- <el-tooltip content="删除" placement="top">
|
|
|
- <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:knowledgeInfo:remove']"></el-button>
|
|
|
- </el-tooltip>
|
|
|
+ <div class="flex space-x-2 items-center">
|
|
|
+ <el-tooltip content="上传文件" placement="top">
|
|
|
+ <el-upload
|
|
|
+ :action="uploadUrl"
|
|
|
+ :headers="headers"
|
|
|
+ :show-file-list="false"
|
|
|
+ accept=".txt,.pdf,.docx,.pptx,.xlsx,.xls,.csv,.json"
|
|
|
+ :data="{ knowledgeCode: scope.row.knowledgeCode }"
|
|
|
+ :before-upload="beforeUpload"
|
|
|
+ :on-success="handleUploadSuccess"
|
|
|
+ :on-error="handleUploadError"
|
|
|
+ >
|
|
|
+ <el-button link type="primary" icon="Upload"></el-button>
|
|
|
+ </el-upload>
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip content="查看附件" placement="top">
|
|
|
+ <el-button link type="primary" icon="Folder" @click="handleAttachment(scope.row)"></el-button>
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip content="删除" placement="top">
|
|
|
+ <el-button
|
|
|
+ link
|
|
|
+ type="danger"
|
|
|
+ icon="Delete"
|
|
|
+ @click="handleDelete(scope.row)"
|
|
|
+ v-hasPermi="['system:knowledgeInfo:remove']"
|
|
|
+ ></el-button>
|
|
|
+ </el-tooltip>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
|
|
|
- <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
|
|
- </el-card>
|
|
|
+ <div class="flex justify-center mt-4">
|
|
|
+ <el-pagination
|
|
|
+ v-show="total > 0"
|
|
|
+ :total="total"
|
|
|
+ v-model:current-page="queryParams.pageNum"
|
|
|
+ v-model:page-size="queryParams.pageSize"
|
|
|
+ @current-change="getList"
|
|
|
+ layout="prev, pager, next"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+
|
|
|
<!-- 添加或修改知识库对话框 -->
|
|
|
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
|
|
- <el-form ref="knowledgeInfoFormRef" :model="form" :rules="rules" label-width="80px">
|
|
|
- <el-form-item label="知识库ID" prop="knowledgeCode">
|
|
|
- <el-input v-model="form.knowledgeCode" placeholder="请输入知识库ID" />
|
|
|
- </el-form-item>
|
|
|
+ <el-form ref="knowledgeInfoFormRef" :model="form" :rules="rules" label-width="100px">
|
|
|
<el-form-item label="知识库名称" prop="knowledgeName">
|
|
|
<el-input v-model="form.knowledgeName" placeholder="请输入知识库名称" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="描述" prop="description">
|
|
|
<el-input v-model="form.description" placeholder="请输入描述" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="用户ID" prop="userId">
|
|
|
- <el-input v-model="form.userId" placeholder="请输入用户ID" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="是否公开知识库" prop="share">
|
|
|
- <el-input v-model="form.share" placeholder="请输入是否公开知识库" />
|
|
|
+ <el-form-item label="是否公开" prop="share">
|
|
|
+ <el-radio-group v-model="form.share">
|
|
|
+ <el-radio :label="1">是</el-radio>
|
|
|
+ <el-radio :label="0">否</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="知识分隔符" prop="knowledgeSeparator">
|
|
|
<el-input v-model="form.knowledgeSeparator" placeholder="请输入知识分隔符" />
|
|
|
@@ -127,25 +109,26 @@
|
|
|
<el-input v-model="form.questionSeparator" placeholder="请输入提问分隔符" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="重叠字符数" prop="overlapChar">
|
|
|
- <el-input v-model="form.overlapChar" placeholder="请输入重叠字符数" />
|
|
|
+ <el-input-number v-model="form.overlapChar" placeholder="请输入重叠字符数" style="width: 100%" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="知识库中检索的条数" prop="retrieveLimit">
|
|
|
- <el-input v-model="form.retrieveLimit" placeholder="请输入知识库中检索的条数" />
|
|
|
+ <el-form-item label="检索条数" prop="retrieveLimit">
|
|
|
+ <el-input-number v-model="form.retrieveLimit" placeholder="请输入知识库中检索的条数" style="width: 100%" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="文本块大小" prop="textBlockSize">
|
|
|
- <el-input v-model="form.textBlockSize" placeholder="请输入文本块大小" />
|
|
|
+ <el-input-number v-model="form.textBlockSize" placeholder="请输入文本块大小" style="width: 100%" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="向量库" prop="vectorModelName">
|
|
|
- <el-input v-model="form.vectorModelName" placeholder="请输入向量库" />
|
|
|
+ <el-select v-model="form.vectorModelName" placeholder="请选择向量库" style="width: 100%">
|
|
|
+ <el-option label="weaviate" value="weaviate" />
|
|
|
+ <el-option label="milvus" value="milvus" />
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="向量模型" prop="embeddingModelName">
|
|
|
- <el-input v-model="form.embeddingModelName" placeholder="请输入向量模型" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="系统提示词" prop="systemPrompt">
|
|
|
- <el-input v-model="form.systemPrompt" placeholder="请输入系统提示词" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="备注" prop="remark">
|
|
|
- <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
|
|
+ <el-select v-model="form.embeddingModelName" placeholder="请选择向量模型" style="width: 100%">
|
|
|
+ <el-option label="text-embedding-3-small" value="text-embedding-3-small" />
|
|
|
+ <el-option label="quentinz/bge-large-zh-v1.5" value="quentinz/bge-large-zh-v1.5" />
|
|
|
+ <el-option label="baai/bge-m3" value="baai/bge-m3" />
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
@@ -155,14 +138,131 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <!-- 附件管理对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ title="知识库附件"
|
|
|
+ v-model="fileVisible"
|
|
|
+ width="80%"
|
|
|
+ append-to-body
|
|
|
+ >
|
|
|
+ <div class="flex justify-between items-center mb-4">
|
|
|
+ <div class="text-lg font-semibold">知识库: {{ currentKnowledgeName }}</div>
|
|
|
+ <div>
|
|
|
+ <el-upload
|
|
|
+ :action="uploadUrl"
|
|
|
+ :headers="headers"
|
|
|
+ :show-file-list="false"
|
|
|
+ accept=".txt,.pdf,.docx,.pptx,.xlsx,.xls,.csv,.json"
|
|
|
+ multiple
|
|
|
+ :data="{ knowledgeCode: currentKnowledgeCode }"
|
|
|
+ :on-success="handleUploadSuccess"
|
|
|
+ :before-upload="beforeUpload"
|
|
|
+ :on-error="handleUploadError"
|
|
|
+ >
|
|
|
+ <el-button type="primary" icon="Upload">文件上传</el-button>
|
|
|
+ </el-upload>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-table :data="fileData" v-loading="fileLoading" class="attachment-table">
|
|
|
+ <el-table-column prop="docName" label="文档名称" width="250" />
|
|
|
+ <el-table-column prop="docType" label="文档类型" width="120" />
|
|
|
+ <el-table-column label="状态" width="150">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-tag :type="getStatusTagType(scope.row.vectorStatus)">
|
|
|
+ {{ getStatusText(scope.row.vectorStatus) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="大小" width="120">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ formatFileSize(scope.row.fileSize) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="上传时间" width="180">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ formatDateTime(scope.row.createTime) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="200">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ size="small"
|
|
|
+ icon="Delete"
|
|
|
+ @click="handleDeleteFile(scope.row)"
|
|
|
+ >删除</el-button>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ icon="Document"
|
|
|
+ @click="handleFragment(scope.row)"
|
|
|
+ >片段</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <div class="mt-4 text-sm text-gray-500">
|
|
|
+ <div class="flex items-center mb-2">
|
|
|
+ <div class="w-3 h-3 bg-blue-500 rounded-full mr-2"></div>
|
|
|
+ <span>支持的文件类型: .txt, .pdf, .docx, .pptx, .xlsx, .xls, .csv, .json</span>
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center">
|
|
|
+ <div class="w-3 h-3 bg-blue-500 rounded-full mr-2"></div>
|
|
|
+ <span>最大文件大小: 10MB</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 知识片段对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ title="知识片段"
|
|
|
+ v-model="fragmentVisible"
|
|
|
+ width="80%"
|
|
|
+ append-to-body
|
|
|
+ >
|
|
|
+ <div class="mb-4">
|
|
|
+ <div class="text-lg font-semibold">{{ currentDocumentName }}</div>
|
|
|
+ <div class="text-sm text-gray-500">共 {{ fragmentData.length }} 个片段</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-table :data="fragmentData" class="fragment-table">
|
|
|
+ <el-table-column label="序号" width="80">
|
|
|
+ <template #default="scope">
|
|
|
+ <div class="text-center">{{ scope.$index + 1 }}</div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="content" label="片段内容">
|
|
|
+ <template #default="scope">
|
|
|
+ <div class="fragment-content">
|
|
|
+ {{ scope.row.content }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="120">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ size="small"
|
|
|
+ icon="Delete"
|
|
|
+ @click="handleDeleteFragment(scope.row)"
|
|
|
+ >删除</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup name="KnowledgeInfo" lang="ts">
|
|
|
-import { listKnowledgeInfo, getKnowledgeInfo, delKnowledgeInfo, addKnowledgeInfo, updateKnowledgeInfo } from '@/api/system/knowledgeInfo';
|
|
|
-import { KnowledgeInfoVO, KnowledgeInfoQuery, KnowledgeInfoForm } from '@/api/system/knowledgeInfo/types';
|
|
|
+import { listKnowledgeInfo, getKnowledgeInfo, delKnowledgeInfo, addKnowledgeInfo, updateKnowledgeInfo,listKnowledgeAttach, delKnowledgeAttach, listKnowledgeFragment, delKnowledgeFragment } from '@/api/system/knowledgeInfo';
|
|
|
+import { KnowledgeInfoVO, KnowledgeInfoQuery, KnowledgeInfoForm, KnowledgeFragmentVO, KnowledgeAttachVO } from '@/api/system/knowledgeInfo/types';
|
|
|
+import { getCurrentInstance, ComponentInternalInstance } from 'vue';
|
|
|
+import { useUserStore } from '@/store/modules/user';
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
+const userStore = useUserStore();
|
|
|
|
|
|
const knowledgeInfoList = ref<KnowledgeInfoVO[]>([]);
|
|
|
const buttonLoading = ref(false);
|
|
|
@@ -173,6 +273,21 @@ const single = ref(true);
|
|
|
const multiple = ref(true);
|
|
|
const total = ref(0);
|
|
|
|
|
|
+// 附件相关状态
|
|
|
+const fileVisible = ref(false);
|
|
|
+const fragmentVisible = ref(false);
|
|
|
+const fileLoading = ref(false);
|
|
|
+const fileData = ref<KnowledgeAttachVO[]>([]);
|
|
|
+const fragmentData = ref<KnowledgeFragmentVO[]>([]);
|
|
|
+const currentKnowledgeCode = ref('');
|
|
|
+const currentKnowledgeName = ref('');
|
|
|
+const currentDocumentName = ref('');
|
|
|
+const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + '/system/knowledgeAttach/upload');
|
|
|
+const headers = ref({
|
|
|
+ Authorization: 'Bearer ' + userStore.token,
|
|
|
+ clientId: import.meta.env.VITE_APP_CLIENT_ID
|
|
|
+});
|
|
|
+
|
|
|
const queryFormRef = ref<ElFormInstance>();
|
|
|
const knowledgeInfoFormRef = ref<ElFormInstance>();
|
|
|
|
|
|
@@ -220,6 +335,12 @@ const data = reactive<PageData<KnowledgeInfoForm, KnowledgeInfoQuery>>({
|
|
|
}
|
|
|
},
|
|
|
rules: {
|
|
|
+ knowledgeName: [{ required: true, message: "知识库名称不能为空", trigger: "blur" }],
|
|
|
+ retrieveLimit: [{ required: true, message: "检索条数不能为空", trigger: "blur" }],
|
|
|
+ textBlockSize: [{ required: true, message: "文本块大小不能为空", trigger: "blur" }],
|
|
|
+ vectorModelName: [{ required: true, message: "向量库不能为空", trigger: "change" }],
|
|
|
+ embeddingModelName: [{ required: true, message: "向量模型不能为空", trigger: "change" }],
|
|
|
+ share: [{ required: true, message: "是否公开不能为空", trigger: "change" }]
|
|
|
}
|
|
|
});
|
|
|
|
|
|
@@ -228,10 +349,16 @@ const { queryParams, form, rules } = toRefs(data);
|
|
|
/** 查询知识库列表 */
|
|
|
const getList = async () => {
|
|
|
loading.value = true;
|
|
|
- const res = await listKnowledgeInfo(queryParams.value);
|
|
|
- knowledgeInfoList.value = res.rows;
|
|
|
- total.value = res.total;
|
|
|
- loading.value = false;
|
|
|
+ try {
|
|
|
+ const res = await listKnowledgeInfo(queryParams.value);
|
|
|
+ knowledgeInfoList.value = res.rows;
|
|
|
+ total.value = res.total;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取知识库列表失败:', error);
|
|
|
+ proxy?.$modal.msgError("获取知识库列表失败");
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/** 取消按钮 */
|
|
|
@@ -268,6 +395,16 @@ const handleSelectionChange = (selection: KnowledgeInfoVO[]) => {
|
|
|
/** 新增按钮操作 */
|
|
|
const handleAdd = () => {
|
|
|
reset();
|
|
|
+ form.value = {
|
|
|
+ ...initFormData,
|
|
|
+ share: 0, // 默认选"否"
|
|
|
+ overlapChar: 30, // 默认重叠字符数为30
|
|
|
+ knowledgeCode: generateRandomString(), //随机编号
|
|
|
+ textBlockSize: 300,
|
|
|
+ retrieveLimit: 5,
|
|
|
+ vectorModelName: "weaviate",
|
|
|
+ embeddingModelName: "quentinz/bge-large-zh-v1.5",
|
|
|
+ };
|
|
|
dialog.visible = true;
|
|
|
dialog.title = "添加知识库";
|
|
|
}
|
|
|
@@ -276,10 +413,15 @@ const handleAdd = () => {
|
|
|
const handleUpdate = async (row?: KnowledgeInfoVO) => {
|
|
|
reset();
|
|
|
const _id = row?.id || ids.value[0]
|
|
|
- const res = await getKnowledgeInfo(_id);
|
|
|
- Object.assign(form.value, res.data);
|
|
|
- dialog.visible = true;
|
|
|
- dialog.title = "修改知识库";
|
|
|
+ try {
|
|
|
+ const res = await getKnowledgeInfo(_id);
|
|
|
+ Object.assign(form.value, res.data);
|
|
|
+ dialog.visible = true;
|
|
|
+ dialog.title = "修改知识库";
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取知识库详情失败:', error);
|
|
|
+ proxy?.$modal.msgError("获取知识库详情失败");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/** 提交按钮 */
|
|
|
@@ -287,25 +429,48 @@ const submitForm = () => {
|
|
|
knowledgeInfoFormRef.value?.validate(async (valid: boolean) => {
|
|
|
if (valid) {
|
|
|
buttonLoading.value = true;
|
|
|
- if (form.value.id) {
|
|
|
- await updateKnowledgeInfo(form.value).finally(() => buttonLoading.value = false);
|
|
|
- } else {
|
|
|
- await addKnowledgeInfo(form.value).finally(() => buttonLoading.value = false);
|
|
|
+ try {
|
|
|
+ if (form.value.id) {
|
|
|
+ await updateKnowledgeInfo(form.value);
|
|
|
+ } else {
|
|
|
+ await addKnowledgeInfo(form.value);
|
|
|
+ }
|
|
|
+ proxy?.$modal.msgSuccess("操作成功");
|
|
|
+ dialog.visible = false;
|
|
|
+ await getList();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('操作失败:', error);
|
|
|
+ proxy?.$modal.msgError("操作失败");
|
|
|
+ } finally {
|
|
|
+ buttonLoading.value = false;
|
|
|
}
|
|
|
- proxy?.$modal.msgSuccess("操作成功");
|
|
|
- dialog.visible = false;
|
|
|
- await getList();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-/** 删除按钮操作 */
|
|
|
const handleDelete = async (row?: KnowledgeInfoVO) => {
|
|
|
- const _ids = row?.id || ids.value;
|
|
|
- await proxy?.$modal.confirm('是否确认删除知识库编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
|
|
|
- await delKnowledgeInfo(_ids);
|
|
|
- proxy?.$modal.msgSuccess("删除成功");
|
|
|
- await getList();
|
|
|
+ // 获取要删除的ID数组 - 使用主键id而不是knowledgeCode
|
|
|
+ const deleteIds = row ? [row.id] : ids.value;
|
|
|
+
|
|
|
+ // 确认删除提示
|
|
|
+ const message = row
|
|
|
+ ? `是否确认删除知识库名称为"${row.knowledgeName}"的数据项?`
|
|
|
+ : `是否确认删除选中的${deleteIds.length}项数据?`;
|
|
|
+
|
|
|
+ try {
|
|
|
+ await proxy?.$modal.confirm(message);
|
|
|
+
|
|
|
+ // 调用删除API,传递ID数组
|
|
|
+ await delKnowledgeInfo(deleteIds);
|
|
|
+
|
|
|
+ proxy?.$modal.msgSuccess("删除成功");
|
|
|
+ await getList();
|
|
|
+ } catch (error) {
|
|
|
+ if (error !== 'cancel') {
|
|
|
+ console.error('删除失败:', error);
|
|
|
+ proxy?.$modal.msgError("删除失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/** 导出按钮操作 */
|
|
|
@@ -315,7 +480,257 @@ const handleExport = () => {
|
|
|
}, `knowledgeInfo_${new Date().getTime()}.xlsx`)
|
|
|
}
|
|
|
|
|
|
+/** 查看附件 */
|
|
|
+const handleAttachment = async (row: KnowledgeInfoVO) => {
|
|
|
+ currentKnowledgeCode.value = row.knowledgeCode;
|
|
|
+ currentKnowledgeName.value = row.knowledgeName;
|
|
|
+ fileLoading.value = true;
|
|
|
+ try {
|
|
|
+ const res = await listKnowledgeAttach({ knowledgeCode: row.knowledgeCode });
|
|
|
+ fileData.value = res.rows;
|
|
|
+ fileVisible.value = true;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取附件列表失败:', error);
|
|
|
+ proxy?.$modal.msgError("获取附件列表失败");
|
|
|
+ } finally {
|
|
|
+ fileLoading.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 删除附件 */
|
|
|
+const handleDeleteFile = async (row: KnowledgeAttachVO) => {
|
|
|
+ try {
|
|
|
+ await proxy?.$modal.confirm('是否确认删除文档"' + row.docName + '"?');
|
|
|
+ // 使用主键id而不是docCode
|
|
|
+ await delKnowledgeAttach(row.id);
|
|
|
+ proxy?.$modal.msgSuccess("删除成功");
|
|
|
+ // 刷新附件列表
|
|
|
+ await handleAttachment({ knowledgeCode: currentKnowledgeCode.value } as KnowledgeInfoVO);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('删除失败:', error);
|
|
|
+ proxy?.$modal.msgError("删除失败");
|
|
|
+ }
|
|
|
+}
|
|
|
+/** 查看知识片段 */
|
|
|
+const handleFragment = async (row: KnowledgeAttachVO) => {
|
|
|
+ currentDocumentName.value = row.docName;
|
|
|
+ try {
|
|
|
+ const res = await listKnowledgeFragment({ docCode: row.docCode });
|
|
|
+ fragmentData.value = res.rows;
|
|
|
+ fragmentVisible.value = true;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取知识片段失败:', error);
|
|
|
+ proxy?.$modal.msgError("获取知识片段失败");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 删除知识片段 */
|
|
|
+const handleDeleteFragment = async (row: KnowledgeFragmentVO) => {
|
|
|
+ try {
|
|
|
+ await proxy?.$modal.confirm('是否确认删除该知识片段?');
|
|
|
+ // 使用主键id而不是fragmentCode
|
|
|
+ await delKnowledgeFragment(row.id);
|
|
|
+ proxy?.$modal.msgSuccess("删除成功");
|
|
|
+ // 刷新片段列表
|
|
|
+ await handleFragment({ docCode: row.docCode } as KnowledgeAttachVO);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('删除失败:', error);
|
|
|
+ proxy?.$modal.msgError("删除失败");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 文件上传成功处理 */
|
|
|
+const handleUploadSuccess = (response: any) => {
|
|
|
+ if (response.code === 200) {
|
|
|
+ proxy?.$modal.msgSuccess("上传成功");
|
|
|
+ // 刷新附件列表
|
|
|
+ if (fileVisible.value) {
|
|
|
+ handleAttachment({ knowledgeCode: currentKnowledgeCode.value } as KnowledgeInfoVO);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ proxy?.$modal.msgError(response.msg || "上传失败");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 文件上传失败处理 */
|
|
|
+const handleUploadError = (error: any) => {
|
|
|
+ let errMsg = '上传失败';
|
|
|
+ if (error.response) {
|
|
|
+ const res = error.response;
|
|
|
+ errMsg = res.data.msg || `请求错误:${res.status} ${res.statusText}`;
|
|
|
+ } else if (error.message) {
|
|
|
+ errMsg = error.message;
|
|
|
+ }
|
|
|
+ proxy?.$modal.msgError(errMsg);
|
|
|
+}
|
|
|
+
|
|
|
+/** 文件上传前校验 */
|
|
|
+const beforeUpload = (file: File) => {
|
|
|
+ const allowedExtensions = ['.txt', '.pdf', '.docx', '.pptx', '.xlsx', '.xls', '.csv', '.json'];
|
|
|
+ const fileExtension = file.name.substring(file.name.lastIndexOf('.')).toLowerCase();
|
|
|
+
|
|
|
+ if (!allowedExtensions.includes(fileExtension)) {
|
|
|
+ proxy?.$modal.msgError('上传文件格式不支持! 仅支持以下格式:' + allowedExtensions.join(', '));
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ const isLt10M = file.size / 1024 / 1024 < 10;
|
|
|
+ if (!isLt10M) {
|
|
|
+ proxy?.$modal.msgError('上传文件大小不能超过 10MB!');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/** 获取状态标签类型 */
|
|
|
+const getStatusTagType = (status: number) => {
|
|
|
+ switch (status) {
|
|
|
+ case 10: return 'info'; // 未开始
|
|
|
+ case 20: return 'warning'; // 进行中
|
|
|
+ case 30: return 'success'; // 已完成
|
|
|
+ default: return 'info';
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 获取状态文本 */
|
|
|
+const getStatusText = (status: number) => {
|
|
|
+ switch (status) {
|
|
|
+ case 10: return '未开始';
|
|
|
+ case 20: return '处理中';
|
|
|
+ case 30: return '已完成';
|
|
|
+ default: return '未知状态';
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 格式化文件大小 */
|
|
|
+const formatFileSize = (bytes: number) => {
|
|
|
+ if (bytes === 0) return '0 Bytes';
|
|
|
+ const k = 1024;
|
|
|
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
|
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
|
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
|
+}
|
|
|
+
|
|
|
+/** 格式化日期时间 */
|
|
|
+const formatDateTime = (dateString: string) => {
|
|
|
+ if (!dateString) return '';
|
|
|
+ const date = new Date(dateString);
|
|
|
+ return date.toLocaleString();
|
|
|
+}
|
|
|
+
|
|
|
+//随机生成16位字符串
|
|
|
+function generateRandomString() {
|
|
|
+ // 可选字符集合
|
|
|
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
|
+ let result = '';
|
|
|
+
|
|
|
+ // 生成16位随机字符
|
|
|
+ for (let i = 0; i < 10; i++) {
|
|
|
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
getList();
|
|
|
});
|
|
|
+
|
|
|
+
|
|
|
</script>
|
|
|
+
|
|
|
+<style>
|
|
|
+/* 全局生效 */
|
|
|
+#app, body, html {
|
|
|
+ width: 100%;
|
|
|
+ height: auto !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 紧凑表格行间距 */
|
|
|
+.el-table.compact-table .el-table__body tr td {
|
|
|
+ padding-top: 12px !important;
|
|
|
+ padding-bottom: 12px !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 紧凑操作按钮间距 */
|
|
|
+.el-button--text {
|
|
|
+ margin-top: -2px !important;
|
|
|
+ margin-bottom: -2px !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 紧凑分页控件上下间距 */
|
|
|
+.flex.justify-center.mt-4 {
|
|
|
+ margin-top: 10px !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 紧凑表格行高 */
|
|
|
+.el-table__row {
|
|
|
+ line-height: 1.2 !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 按钮悬停效果 */
|
|
|
+.el-button--primary:hover {
|
|
|
+ background-color: transparent !important;
|
|
|
+ color: #006BE6 !important;
|
|
|
+ border-color: #006BE6 !important;
|
|
|
+}
|
|
|
+
|
|
|
+.el-button--danger:hover {
|
|
|
+ background-color: transparent !important;
|
|
|
+ color: #e74c3c !important;
|
|
|
+ border-color: #c0392b !important;
|
|
|
+}
|
|
|
+
|
|
|
+.el-button--success:hover {
|
|
|
+ background-color: transparent !important;
|
|
|
+ color: #9b59b6 !important;
|
|
|
+ border-color: #8e44ad !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 标题栏样式 */
|
|
|
+.text-xl.font-bold {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+/* 表单卡片样式 */
|
|
|
+.bg-white.rounded {
|
|
|
+ border-radius: 4px;
|
|
|
+ box-shadow: 0 1px 2px 0 rgba(0,0,0,0.05);
|
|
|
+}
|
|
|
+
|
|
|
+/* 附件表格样式 */
|
|
|
+.attachment-table .el-table__row {
|
|
|
+ height: 60px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 片段表格样式 */
|
|
|
+.fragment-table .el-table__row {
|
|
|
+ height: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.fragment-content {
|
|
|
+ padding: 10px;
|
|
|
+ background-color: #f9f9f9;
|
|
|
+ border-radius: 4px;
|
|
|
+ white-space: pre-wrap;
|
|
|
+ line-height: 1.5;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+/* 状态标签样式 */
|
|
|
+.el-tag {
|
|
|
+ padding: 0 8px;
|
|
|
+ height: 24px;
|
|
|
+ line-height: 24px;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 上传按钮样式 */
|
|
|
+.upload-button {
|
|
|
+ margin-left: 10px;
|
|
|
+}
|
|
|
+</style>
|