update: 知识库页面复用弹窗、分页组件,同步修改样式

This commit is contained in:
zhuoqinglian
2026-06-09 17:22:18 +08:00
parent fe0f7dcf75
commit 2bf5725048
11 changed files with 218 additions and 135 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 B

@@ -1,5 +1,5 @@
<template> <template>
<el-dialog :title="title" :visible="dialogVisible" width="600px" class="knowledge-base-dialog" @close="handleClose"> <CustomDialog :title="title" :visible.sync="dialogVisible" width="600px" class="knowledge-base-dialog" @close="handleClose" @confirm="handleSubmit">
<el-form ref="knowledgeBaseForm" :model="form" :rules="rules" label-width="100px" size="medium"> <el-form ref="knowledgeBaseForm" :model="form" :rules="rules" label-width="100px" size="medium">
<el-form-item :label="$t('knowledgeBaseDialog.name')" prop="name"> <el-form-item :label="$t('knowledgeBaseDialog.name')" prop="name">
<el-input v-model="form.name" :placeholder="$t('knowledgeBaseDialog.namePlaceholder')" clearable></el-input> <el-input v-model="form.name" :placeholder="$t('knowledgeBaseDialog.namePlaceholder')" clearable></el-input>
@@ -16,15 +16,12 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> </CustomDialog>
<el-button @click="handleClose">{{ $t('knowledgeBaseDialog.cancel') }}</el-button>
<el-button type="primary" @click="handleSubmit">{{ $t('knowledgeBaseDialog.confirm') }}</el-button>
</div>
</el-dialog>
</template> </template>
<script> <script>
import Api from "@/apis/api"; import Api from "@/apis/api";
import CustomDialog from "./CustomDialog.vue";
export default { export default {
name: "KnowledgeBaseDialog", name: "KnowledgeBaseDialog",
@@ -93,6 +90,9 @@ export default {
} }
}; };
}, },
components: {
CustomDialog
},
watch: { watch: {
visible(val) { visible(val) {
this.dialogVisible = val; this.dialogVisible = val;
+2
View File
@@ -1351,6 +1351,7 @@ export default {
'knowledgeFileUpload.confirmParse': 'Sind Sie sicher, dass Sie dieses Dokument parsen möchten?', 'knowledgeFileUpload.confirmParse': 'Sind Sie sicher, dass Sie dieses Dokument parsen möchten?',
'knowledgeFileUpload.nameRequired': 'Bitte Dokumentnamen eingeben', 'knowledgeFileUpload.nameRequired': 'Bitte Dokumentnamen eingeben',
'knowledgeFileUpload.fileRequired': 'Bitte wählen Sie eine Datei zum Hochladen aus', 'knowledgeFileUpload.fileRequired': 'Bitte wählen Sie eine Datei zum Hochladen aus',
'knowledgeFileUpload.fileSizeExceeded': 'Dateigröße darf 10MB nicht überschreiten!',
'knowledgeFileUpload.getListFailed': 'Dokumentenliste konnte nicht abgerufen werden', 'knowledgeFileUpload.getListFailed': 'Dokumentenliste konnte nicht abgerufen werden',
'knowledgeFileUpload.parseCancelled': 'Parsen abgebrochen', 'knowledgeFileUpload.parseCancelled': 'Parsen abgebrochen',
'knowledgeFileUpload.deleteCancelled': 'Löschen abgebrochen', 'knowledgeFileUpload.deleteCancelled': 'Löschen abgebrochen',
@@ -1384,6 +1385,7 @@ export default {
'knowledgeFileUpload.comprehensiveSimilarity': 'Umfassende Ähnlichkeit', 'knowledgeFileUpload.comprehensiveSimilarity': 'Umfassende Ähnlichkeit',
'knowledgeFileUpload.content': 'Inhalt:', 'knowledgeFileUpload.content': 'Inhalt:',
'knowledgeFileUpload.testQuestionRequired': 'Bitte Testfrage eingeben', 'knowledgeFileUpload.testQuestionRequired': 'Bitte Testfrage eingeben',
'knowledgeFileUpload.sourceDocument': 'Quelldokument',
'knowledgeFileUpload.parsing': 'Analysiere...', 'knowledgeFileUpload.parsing': 'Analysiere...',
'knowledgeBaseDialog.descriptionRequired': 'Bitte Wissensbasis-Beschreibung eingeben', 'knowledgeBaseDialog.descriptionRequired': 'Bitte Wissensbasis-Beschreibung eingeben',
+2
View File
@@ -1351,6 +1351,7 @@ export default {
'knowledgeFileUpload.confirmParse': 'Are you sure you want to parse this document?', 'knowledgeFileUpload.confirmParse': 'Are you sure you want to parse this document?',
'knowledgeFileUpload.nameRequired': 'Please enter document name', 'knowledgeFileUpload.nameRequired': 'Please enter document name',
'knowledgeFileUpload.fileRequired': 'Please select a file to upload', 'knowledgeFileUpload.fileRequired': 'Please select a file to upload',
'knowledgeFileUpload.fileSizeExceeded': 'File size cannot exceed 10MB!',
'knowledgeFileUpload.getListFailed': 'Failed to get document list', 'knowledgeFileUpload.getListFailed': 'Failed to get document list',
'knowledgeFileUpload.parseCancelled': 'Parse cancelled', 'knowledgeFileUpload.parseCancelled': 'Parse cancelled',
'knowledgeFileUpload.deleteCancelled': 'Delete cancelled', 'knowledgeFileUpload.deleteCancelled': 'Delete cancelled',
@@ -1382,6 +1383,7 @@ export default {
'knowledgeFileUpload.testResult': 'Test Result:', 'knowledgeFileUpload.testResult': 'Test Result:',
'knowledgeFileUpload.noRelatedSlices': 'No related slices found', 'knowledgeFileUpload.noRelatedSlices': 'No related slices found',
'knowledgeFileUpload.comprehensiveSimilarity': 'Comprehensive Similarity', 'knowledgeFileUpload.comprehensiveSimilarity': 'Comprehensive Similarity',
'knowledgeFileUpload.sourceDocument': 'Source Document',
'knowledgeFileUpload.content': 'Content:', 'knowledgeFileUpload.content': 'Content:',
'knowledgeFileUpload.testQuestionRequired': 'Please enter test question', 'knowledgeFileUpload.testQuestionRequired': 'Please enter test question',
'knowledgeFileUpload.parsing': 'Parsing...', 'knowledgeFileUpload.parsing': 'Parsing...',
+2
View File
@@ -1351,6 +1351,7 @@ export default {
'knowledgeFileUpload.confirmParse': 'Tem certeza de que deseja analisar este documento?', 'knowledgeFileUpload.confirmParse': 'Tem certeza de que deseja analisar este documento?',
'knowledgeFileUpload.nameRequired': 'Por favor, insira o nome do documento', 'knowledgeFileUpload.nameRequired': 'Por favor, insira o nome do documento',
'knowledgeFileUpload.fileRequired': 'Por favor, selecione um arquivo para enviar', 'knowledgeFileUpload.fileRequired': 'Por favor, selecione um arquivo para enviar',
'knowledgeFileUpload.fileSizeExceeded': 'O tamanho do arquivo não pode exceder 10MB!',
'knowledgeFileUpload.getListFailed': 'Falha ao obter lista de documentos', 'knowledgeFileUpload.getListFailed': 'Falha ao obter lista de documentos',
'knowledgeFileUpload.parseCancelled': 'Análise cancelada', 'knowledgeFileUpload.parseCancelled': 'Análise cancelada',
'knowledgeFileUpload.deleteCancelled': 'Exclusão cancelada', 'knowledgeFileUpload.deleteCancelled': 'Exclusão cancelada',
@@ -1384,6 +1385,7 @@ export default {
'knowledgeFileUpload.comprehensiveSimilarity': 'Similaridade Geral', 'knowledgeFileUpload.comprehensiveSimilarity': 'Similaridade Geral',
'knowledgeFileUpload.content': 'Conteúdo:', 'knowledgeFileUpload.content': 'Conteúdo:',
'knowledgeFileUpload.testQuestionRequired': 'Por favor, insira a pergunta de teste', 'knowledgeFileUpload.testQuestionRequired': 'Por favor, insira a pergunta de teste',
'knowledgeFileUpload.sourceDocument': 'Documento de origem',
'knowledgeFileUpload.parsing': 'Analisando...', 'knowledgeFileUpload.parsing': 'Analisando...',
'knowledgeBaseDialog.descriptionRequired': 'Por favor, insira a descrição da base de conhecimento', 'knowledgeBaseDialog.descriptionRequired': 'Por favor, insira a descrição da base de conhecimento',
+2
View File
@@ -1351,6 +1351,7 @@ export default {
'knowledgeFileUpload.confirmParse': 'Bạn có chắc chắn muốn phân tích cú pháp tài liệu này?', 'knowledgeFileUpload.confirmParse': 'Bạn có chắc chắn muốn phân tích cú pháp tài liệu này?',
'knowledgeFileUpload.nameRequired': 'Vui lòng nhập tên tài liệu', 'knowledgeFileUpload.nameRequired': 'Vui lòng nhập tên tài liệu',
'knowledgeFileUpload.fileRequired': 'Vui lòng chọn tệp để tải lên', 'knowledgeFileUpload.fileRequired': 'Vui lòng chọn tệp để tải lên',
'knowledgeFileUpload.fileSizeExceeded': 'Kích thước tệp không được vượt quá 10MB!',
'knowledgeFileUpload.getListFailed': 'Không thể lấy danh sách tài liệu', 'knowledgeFileUpload.getListFailed': 'Không thể lấy danh sách tài liệu',
'knowledgeFileUpload.parseCancelled': 'Đã hủy phân tích cú pháp', 'knowledgeFileUpload.parseCancelled': 'Đã hủy phân tích cú pháp',
'knowledgeFileUpload.deleteCancelled': 'Đã hủy xóa', 'knowledgeFileUpload.deleteCancelled': 'Đã hủy xóa',
@@ -1384,6 +1385,7 @@ export default {
'knowledgeFileUpload.comprehensiveSimilarity': 'Độ tương đồng toàn diện', 'knowledgeFileUpload.comprehensiveSimilarity': 'Độ tương đồng toàn diện',
'knowledgeFileUpload.content': 'Nội dung:', 'knowledgeFileUpload.content': 'Nội dung:',
'knowledgeFileUpload.testQuestionRequired': 'Vui lòng nhập câu hỏi kiểm tra', 'knowledgeFileUpload.testQuestionRequired': 'Vui lòng nhập câu hỏi kiểm tra',
'knowledgeFileUpload.sourceDocument': 'Tài liệu nguồn',
'knowledgeFileUpload.parsing': 'Đang phân tích...', 'knowledgeFileUpload.parsing': 'Đang phân tích...',
'knowledgeBaseDialog.descriptionRequired': 'Vui lòng nhập mô tả cơ sở kiến thức', 'knowledgeBaseDialog.descriptionRequired': 'Vui lòng nhập mô tả cơ sở kiến thức',
+3 -1
View File
@@ -1347,10 +1347,11 @@ export default {
'knowledgeFileUpload.parseFailed': '文档解析失败', 'knowledgeFileUpload.parseFailed': '文档解析失败',
'knowledgeFileUpload.deleteSuccess': '文档删除成功', 'knowledgeFileUpload.deleteSuccess': '文档删除成功',
'knowledgeFileUpload.deleteFailed': '文档删除失败', 'knowledgeFileUpload.deleteFailed': '文档删除失败',
'knowledgeFileUpload.confirmDelete': '确定要删除此文吗?', 'knowledgeFileUpload.confirmDelete': '确定要删除此文吗?',
'knowledgeFileUpload.confirmParse': '确定要解析该文档吗?', 'knowledgeFileUpload.confirmParse': '确定要解析该文档吗?',
'knowledgeFileUpload.nameRequired': '请输入文档名称', 'knowledgeFileUpload.nameRequired': '请输入文档名称',
'knowledgeFileUpload.fileRequired': '请选择要上传的文件', 'knowledgeFileUpload.fileRequired': '请选择要上传的文件',
'knowledgeFileUpload.fileSizeExceeded': '文件大小不能超过10MB!',
'knowledgeFileUpload.getListFailed': '获取文档列表失败', 'knowledgeFileUpload.getListFailed': '获取文档列表失败',
'knowledgeFileUpload.parseCancelled': '已取消解析', 'knowledgeFileUpload.parseCancelled': '已取消解析',
'knowledgeFileUpload.deleteCancelled': '已取消删除', 'knowledgeFileUpload.deleteCancelled': '已取消删除',
@@ -1382,6 +1383,7 @@ export default {
'knowledgeFileUpload.testResult': '测试结果:', 'knowledgeFileUpload.testResult': '测试结果:',
'knowledgeFileUpload.noRelatedSlices': '未找到相关切片', 'knowledgeFileUpload.noRelatedSlices': '未找到相关切片',
'knowledgeFileUpload.comprehensiveSimilarity': '综合相似度', 'knowledgeFileUpload.comprehensiveSimilarity': '综合相似度',
'knowledgeFileUpload.sourceDocument': '来源文档',
'knowledgeFileUpload.content': '内容:', 'knowledgeFileUpload.content': '内容:',
'knowledgeFileUpload.testQuestionRequired': '请输入测试问题', 'knowledgeFileUpload.testQuestionRequired': '请输入测试问题',
'knowledgeBaseDialog.descriptionRequired': '请输入知识库描述', 'knowledgeBaseDialog.descriptionRequired': '请输入知识库描述',
+2
View File
@@ -1351,6 +1351,7 @@ export default {
'knowledgeFileUpload.confirmParse': '確定要解析該文檔嗎?', 'knowledgeFileUpload.confirmParse': '確定要解析該文檔嗎?',
'knowledgeFileUpload.nameRequired': '請輸入文檔名稱', 'knowledgeFileUpload.nameRequired': '請輸入文檔名稱',
'knowledgeFileUpload.fileRequired': '請選擇要上傳的文件', 'knowledgeFileUpload.fileRequired': '請選擇要上傳的文件',
'knowledgeFileUpload.fileSizeExceeded': '文件大小不能超過10MB!',
'knowledgeFileUpload.getListFailed': '獲取文檔列表失敗', 'knowledgeFileUpload.getListFailed': '獲取文檔列表失敗',
'knowledgeFileUpload.parseCancelled': '已取消解析', 'knowledgeFileUpload.parseCancelled': '已取消解析',
'knowledgeFileUpload.deleteCancelled': '已取消删除', 'knowledgeFileUpload.deleteCancelled': '已取消删除',
@@ -1384,6 +1385,7 @@ export default {
'knowledgeFileUpload.comprehensiveSimilarity': '綜合相似度', 'knowledgeFileUpload.comprehensiveSimilarity': '綜合相似度',
'knowledgeFileUpload.content': '內容:', 'knowledgeFileUpload.content': '內容:',
'knowledgeFileUpload.testQuestionRequired': '請輸入測試問題', 'knowledgeFileUpload.testQuestionRequired': '請輸入測試問題',
'knowledgeFileUpload.sourceDocument': '來源文檔',
'knowledgeFileUpload.parsing': '正在解析中...', 'knowledgeFileUpload.parsing': '正在解析中...',
'knowledgeBaseDialog.descriptionRequired': '請輸入知识库描述', 'knowledgeBaseDialog.descriptionRequired': '請輸入知识库描述',
@@ -3,7 +3,7 @@
<div class="doc-section-header"> <div class="doc-section-header">
<div class="doc-section-left"> <div class="doc-section-left">
<div class="doc-section-title">{{`${$t('knowledgeBaseManagement.currentKnowledgeBaseDocuments')}${kb?.name}`}}</div> <div class="doc-section-title">{{`${$t('knowledgeBaseManagement.currentKnowledgeBaseDocuments')}${kb?.name}`}}</div>
<div class="doc-section-count" v-if="kb">{{ $t('knowledgeBaseManagement.totalDocuments', { total: documents.length }) }}</div> <div class="doc-section-count" v-if="kb">{{ $t('knowledgeBaseManagement.totalDocuments', { total }) }}</div>
</div> </div>
<div class="doc-section-actions"> <div class="doc-section-actions">
<el-input <el-input
@@ -61,7 +61,7 @@
> >
{{ $t('knowledgeFileUpload.parse') }} {{ $t('knowledgeFileUpload.parse') }}
</el-button> </el-button>
<el-button type="text" class="delete-btn" @click.stop="handleDelete(doc)"> <el-button type="text" class="delete-btn" :loading="deleteLoadingMap[doc.id]" @click.stop="handleDelete(doc)">
{{ $t('knowledgeBaseManagement.delete') }} {{ $t('knowledgeBaseManagement.delete') }}
</el-button> </el-button>
</div> </div>
@@ -71,11 +71,20 @@
<el-empty :description="$t('knowledgeBaseManagement.noData')"></el-empty> <el-empty :description="$t('knowledgeBaseManagement.noData')"></el-empty>
</div> </div>
</div> </div>
<CustomPagination
:total="total"
:current-page="currentPage"
:page-size="pageSize"
:page-size-options="pageSizeOptions"
@page-change="handlePageChange"
@size-change="handleSizeChange"
/>
</div> </div>
</template> </template>
<script> <script>
import Api from "@/apis/api"; import Api from "@/apis/api";
import CustomPagination from "@/components/CustomPagination.vue";
const iconMap = { const iconMap = {
pdf: require('@/assets/knowledge-base/pdf.png'), pdf: require('@/assets/knowledge-base/pdf.png'),
@@ -91,6 +100,7 @@ const iconMap = {
export default { export default {
name: 'LibraryItem', name: 'LibraryItem',
components: { CustomPagination },
props: { props: {
kb: { kb: {
type: Object, type: Object,
@@ -101,16 +111,23 @@ export default {
return { return {
documents: [], documents: [],
docLoading: false, docLoading: false,
deleteLoadingMap: {},
searchDocName: '', searchDocName: '',
total: 0,
currentPage: 1,
pageSize: 10,
pageSizeOptions: [10, 20, 50, 100],
}; };
}, },
watch: { watch: {
kb: { kb: {
handler(newKb) { handler(newKb) {
if (newKb && newKb.datasetId) { if (newKb && newKb.datasetId) {
this.currentPage = 1;
this.fetchDocuments(); this.fetchDocuments();
} else { } else {
this.documents = []; this.documents = [];
this.total = 0;
} }
}, },
immediate: true immediate: true
@@ -121,8 +138,8 @@ export default {
if (!this.kb || !this.kb.datasetId) return; if (!this.kb || !this.kb.datasetId) return;
this.docLoading = true; this.docLoading = true;
const params = { const params = {
page: 1, page: this.currentPage,
page_size: 100, page_size: this.pageSize,
name: this.searchDocName name: this.searchDocName
}; };
Api.knowledgeBase.getDocumentList( Api.knowledgeBase.getDocumentList(
@@ -132,10 +149,12 @@ export default {
this.docLoading = false; this.docLoading = false;
if (data && data.code === 0) { if (data && data.code === 0) {
this.documents = data.data.list || []; this.documents = data.data.list || [];
this.total = data.data.total || 0;
this.fetchSliceCountsForDocuments(); this.fetchSliceCountsForDocuments();
} else { } else {
this.$message.error(data?.msg || '获取文档列表失败'); this.$message.error(data?.msg || '获取文档列表失败');
this.documents = []; this.documents = [];
this.total = 0;
} }
}, },
(err) => { (err) => {
@@ -146,11 +165,24 @@ export default {
this.$message.error('获取文档列表失败'); this.$message.error('获取文档列表失败');
} }
this.documents = []; this.documents = [];
this.total = 0;
} }
); );
}, },
handleSearch() { handleSearch() {
this.currentPage = 1;
this.fetchDocuments();
},
handlePageChange(page) {
this.currentPage = page;
this.fetchDocuments();
},
handleSizeChange(size) {
this.pageSize = size;
this.currentPage = 1;
this.fetchDocuments(); this.fetchDocuments();
}, },
@@ -173,14 +205,20 @@ export default {
type: 'warning' type: 'warning'
} }
).then(() => { ).then(() => {
this.$set(this.deleteLoadingMap, doc.id, true);
Api.knowledgeBase.deleteDocument(this.kb.datasetId, doc.id, (res) => { Api.knowledgeBase.deleteDocument(this.kb.datasetId, doc.id, (res) => {
this.$set(this.deleteLoadingMap, doc.id, false);
if (res.data && res.data.code === 0) { if (res.data && res.data.code === 0) {
if (this.documents.length === 1 && this.currentPage > 1) {
this.currentPage--;
}
this.fetchDocuments(); this.fetchDocuments();
this.$message.success(this.$t('knowledgeFileUpload.deleteSuccess')); this.$message.success(this.$t('knowledgeFileUpload.deleteSuccess'));
} else { } else {
this.$message.error(res.data?.msg || this.$t('knowledgeFileUpload.deleteFailed')); this.$message.error(res.data?.msg || this.$t('knowledgeFileUpload.deleteFailed'));
} }
}, () => { }, () => {
this.$set(this.deleteLoadingMap, doc.id, false);
this.$message.error(this.$t('knowledgeFileUpload.deleteFailed')); this.$message.error(this.$t('knowledgeFileUpload.deleteFailed'));
}); });
}).catch(() => {}); }).catch(() => {});
@@ -301,7 +339,7 @@ export default {
getProgressText(doc) { getProgressText(doc) {
const code = doc.parseStatusCode; const code = doc.parseStatusCode;
if (code === 3) return ''; if (code === 3) return '100%';
if (code === 1) { if (code === 1) {
if (doc.chunkNum && doc.totalChunkNum) { if (doc.chunkNum && doc.totalChunkNum) {
return Math.round((doc.chunkNum / doc.totalChunkNum) * 100) + '%'; return Math.round((doc.chunkNum / doc.totalChunkNum) * 100) + '%';
@@ -377,13 +415,12 @@ export default {
.doc-section-actions { .doc-section-actions {
display: flex; display: flex;
gap: 8px;
align-items: center; align-items: center;
} }
.doc-search-input { .doc-search-input {
width: 200px; width: 200px;
margin-left: 8px; margin-right: 10px;
} }
/* ========== Document Grid ========== */ /* ========== Document Grid ========== */
@@ -399,7 +436,7 @@ export default {
} }
.doc-card { .doc-card {
height: 120px; max-height: 120px;
border-radius: 8px; border-radius: 8px;
border: 1px solid transparent; border: 1px solid transparent;
padding: 18px; padding: 18px;
@@ -117,20 +117,18 @@
/> />
<!-- Slice Dialog --> <!-- Slice Dialog -->
<el-dialog <CustomDialog
:title="`${$t('knowledgeFileUpload.viewSlices')} - ${currentDocumentName}`" :title="`${$t('knowledgeFileUpload.viewSlices')} - ${currentDocumentName}`"
:visible.sync="sliceDialogVisible" :visible.sync="sliceDialogVisible"
width="900px" width="1200px"
:close-on-click-modal="false" :footer="false"
> >
<div class="slice-management"> <div class="slice-management">
<div v-loading="sliceLoading" class="slice-content-container"> <div v-loading="sliceLoading" class="slice-content-container">
<div v-if="sliceList.length > 0" class="slice-cards-container"> <div v-if="sliceList.length > 0" class="slice-cards-container">
<div v-for="(slice, index) in sliceList" :key="index" class="slice-card"> <div v-for="(slice, index) in sliceList" :key="index" class="slice-card">
<div class="slice-header-info">
<p><strong>{{ $t('knowledgeFileUpload.slice') }} {{ (sliceCurrentPage - 1) * slicePageSize + index + 1 }}</strong></p>
</div>
<div class="slice-card-content"> <div class="slice-card-content">
<span class="clice-index">{{ $t('knowledgeFileUpload.slice') }} {{ (sliceCurrentPage - 1) * slicePageSize + index + 1 }}</span>
<div class="content-text">{{ slice.content }}</div> <div class="content-text">{{ slice.content }}</div>
</div> </div>
</div> </div>
@@ -140,30 +138,31 @@
</div> </div>
</div> </div>
<div class="slice-pagination"> <div class="slice-pagination">
<el-select v-model="slicePageSize" @change="handleSliceSizeChange" class="slice-page-size-select"> <CustomPagination
<el-option v-for="item in [10, 20, 50]" :key="item" :label="`${item}${$t('knowledgeFileUpload.itemsPerPage')}`" :value="item"></el-option> :total="parseInt(sliceTotal)"
</el-select> :current-page="sliceCurrentPage"
<button class="slice-page-btn" :disabled="sliceCurrentPage === 1" @click="sliceCurrentPage = 1; fetchSlices()">{{ $t('knowledgeBaseManagement.firstPage') }}</button> :page-size="slicePageSize"
<button class="slice-page-btn" :disabled="sliceCurrentPage === 1" @click="sliceCurrentPage--; fetchSlices()">{{ $t('knowledgeBaseManagement.prevPage') }}</button> :page-size-options="[10, 20, 50]"
<button v-for="page in sliceVisiblePages" :key="page" class="slice-page-btn" :class="{ active: page === sliceCurrentPage }" @click="sliceCurrentPage = page; fetchSlices()">{{ page }}</button> @size-change="handleSliceSizeChange"
<button class="slice-page-btn" :disabled="sliceCurrentPage >= slicePageCount" @click="sliceCurrentPage++; fetchSlices()">{{ $t('knowledgeBaseManagement.nextPage') }}</button> @page-change="handleSlicePageChange"
<span class="slice-total-text"> {{ $t('knowledgeBaseManagement.totalRecords', { total: sliceTotal }) }}</span> />
</div> </div>
</div> </div>
</el-dialog> </CustomDialog>
<!-- Upload Dialog --> <!-- Upload Dialog -->
<el-dialog <CustomDialog
:title="$t('knowledgeFileUpload.uploadDocument')" :title="$t('knowledgeFileUpload.uploadDocument')"
:visible.sync="uploadDialogVisible" :visible.sync="uploadDialogVisible"
width="800px" width="800px"
:close-on-click-modal="false"
@close="handleUploadDialogClose" @close="handleUploadDialogClose"
@confirm="handleBatchUploadSubmit"
> >
<el-upload <el-upload
ref="uploadRef" ref="uploadRef"
action="#" action="#"
:auto-upload="false" :auto-upload="false"
:show-file-list="false"
:on-change="handleFileChange" :on-change="handleFileChange"
multiple multiple
accept=".doc,.docx,.pdf,.txt,.md,.mdx,.csv,.xls,.xlsx,.ppt,.pptx" accept=".doc,.docx,.pdf,.txt,.md,.mdx,.csv,.xls,.xlsx,.ppt,.pptx"
@@ -189,48 +188,61 @@
</div> </div>
</div> </div>
</div> </div>
<span slot="footer"> </CustomDialog>
<el-button @click="uploadDialogVisible = false">{{ $t('knowledgeFileUpload.cancel') }}</el-button>
<el-button type="primary" @click="handleBatchUploadSubmit" :loading="uploading" :disabled="selectedFilesList.length === 0">
{{ $t('knowledgeFileUpload.confirm') }} ({{ selectedFilesList.length }})
</el-button>
</span>
</el-dialog>
<!-- Retrieval Test Dialog --> <!-- Retrieval Test Dialog -->
<el-dialog <CustomDialog
:title="$t('knowledgeFileUpload.retrievalTest')" :title="$t('knowledgeFileUpload.retrievalTest')"
:visible.sync="retrievalTestDialogVisible" :visible.sync="retrievalTestDialogVisible"
width="900px" width="900px"
:close-on-click-modal="false" :close-on-click-modal="false"
:confirm-text="$t('knowledgeFileUpload.executeTest')"
:confirmLoading="retrievalTestLoading"
@confirm="runRetrievalTest"
> >
<div class="retrieval-test-form"> <div class="retrieval-test-form">
<el-form :model="retrievalTestForm" label-width="100px"> <el-form :model="retrievalTestForm" label-width="100px">
<el-form-item :label="$t('knowledgeFileUpload.testQuestion')" required> <el-form-item :label="$t('knowledgeFileUpload.testQuestion')" required>
<el-input <el-input
v-model="retrievalTestForm.question" v-model="retrievalTestForm.question"
type="textarea" @keyup.enter.native="runRetrievalTest"
:rows="3" clearable
:placeholder="$t('knowledgeFileUpload.testQuestionPlaceholder')" :placeholder="$t('knowledgeFileUpload.testQuestionPlaceholder')"
/> />
</el-form-item> </el-form-item>
</el-form> </el-form>
<div style="text-align: center; margin-top: 20px;">
<el-button type="primary" @click="runRetrievalTest" :loading="retrievalTestLoading">{{ $t('knowledgeFileUpload.executeTest') }}</el-button>
<el-button @click="retrievalTestDialogVisible = false">{{ $t('knowledgeFileUpload.cancel') }}</el-button>
</div>
<div v-if="retrievalTestResult" class="retrieval-test-result" style="margin-top: 20px;"> <div v-if="retrievalTestResult" class="retrieval-test-result" style="margin-top: 20px;">
<h4 style="margin-bottom: 12px;">{{ $t('knowledgeFileUpload.testResult') }}</h4> <div class="result-chunks" v-if="retrievalTestResult.chunks.length">
<div class="result-chunks">
<div v-for="(chunk, index) in retrievalTestResult.chunks" :key="index" class="result-chunk"> <div v-for="(chunk, index) in retrievalTestResult.chunks" :key="index" class="result-chunk">
<p><strong>{{ $t('knowledgeFileUpload.slice') }} {{ index + 1 }}</strong></p> <div class="chunk-left">
<div style="margin: 8px 0; font-size: 12px; color: #409eff;">{{ $t('knowledgeFileUpload.comprehensiveSimilarity') }}: {{ (chunk.similarity || 0).toFixed(4) }}</div> <div class="chunk-similarity">
<div class="chunk-content">{{ chunk.content }}</div> <p class="similarity-label">{{ $t('knowledgeFileUpload.comprehensiveSimilarity') }}</p>
<p class="similarity-value">{{ (chunk.similarity || 0).toFixed(4) }}</p>
<el-progress
:percentage="Math.round((chunk.similarity || 0) * 100)"
:stroke-width="6"
:show-text="false"
class="similarity-progress"
/>
</div>
</div>
<el-divider direction="vertical"></el-divider>
<div class="chunk-right">
<div class="chunk-content">
<div class="chunk-right-header">
<span class="chunk-source">{{ $t('knowledgeFileUpload.sourceDocument') }}{{ chunk.document_keyword || '-' }}</span>
<span class="chunk-index">{{ $t('knowledgeFileUpload.slice') }} {{ index + 1 }}</span>
</div>
<p>{{ chunk.content }}</p>
</div>
</div>
</div> </div>
</div> </div>
<el-empty v-else :description="$t('knowledgeFileUpload.noSliceData')"></el-empty>
</div> </div>
</div> </div>
</el-dialog> </CustomDialog>
<el-footer> <el-footer>
<version-footer /> <version-footer />
@@ -245,9 +257,11 @@ import VersionFooter from "@/components/VersionFooter.vue";
import KnowledgeBaseDialog from "@/components/KnowledgeBaseDialog.vue"; import KnowledgeBaseDialog from "@/components/KnowledgeBaseDialog.vue";
import KnowledgeBaseItem from "./KnowledgeBaseItem.vue"; import KnowledgeBaseItem from "./KnowledgeBaseItem.vue";
import ManualIcon from "@/components/ManualIcon.vue"; import ManualIcon from "@/components/ManualIcon.vue";
import CustomDialog from "@/components/CustomDialog.vue";
import CustomPagination from "@/components/CustomPagination.vue";
export default { export default {
components: { HeaderBar, VersionFooter, KnowledgeBaseDialog, KnowledgeBaseItem, ManualIcon }, components: { HeaderBar, VersionFooter, KnowledgeBaseDialog, KnowledgeBaseItem, ManualIcon, CustomDialog, CustomPagination },
data() { data() {
return { return {
knowledgeBases: [], knowledgeBases: [],
@@ -290,22 +304,6 @@ export default {
kb.name.toLowerCase().includes(keyword) kb.name.toLowerCase().includes(keyword)
); );
}, },
slicePageCount() {
return Math.ceil(this.sliceTotal / this.slicePageSize);
},
sliceVisiblePages() {
const pages = [];
const maxVisible = 3;
let start = Math.max(1, this.sliceCurrentPage - 1);
let end = Math.min(this.slicePageCount, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
},
}, },
created() { created() {
this.fetchKnowledgeBases(); this.fetchKnowledgeBases();
@@ -478,6 +476,12 @@ export default {
handleFileChange(file) { handleFileChange(file) {
if (!file || !file.raw) return; if (!file || !file.raw) return;
// 文件上传前的验证
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
this.$message.error(this.$t('knowledgeFileUpload.fileSizeExceeded'));
return;
}
this.selectedFilesList.push({ this.selectedFilesList.push({
name: file.name, name: file.name,
size: file.size, size: file.size,
@@ -564,6 +568,7 @@ export default {
}, },
runRetrievalTest() { runRetrievalTest() {
if (this.retrievalTestLoading) return;
if (!this.retrievalTestForm.question.trim()) { if (!this.retrievalTestForm.question.trim()) {
this.$message.error(this.$t('knowledgeFileUpload.testQuestionRequired')); this.$message.error(this.$t('knowledgeFileUpload.testQuestionRequired'));
return; return;
@@ -648,6 +653,11 @@ export default {
this.fetchSlices(); this.fetchSlices();
}, },
handleSlicePageChange(page) {
this.sliceCurrentPage = page;
this.fetchSlices();
},
getCardColorClass(index) { getCardColorClass(index) {
const colors = ['blue', 'green', 'purple', 'orange', 'pink', 'cyan']; const colors = ['blue', 'green', 'purple', 'orange', 'pink', 'cyan'];
return colors[index % colors.length]; return colors[index % colors.length];
@@ -1009,7 +1019,6 @@ export default {
.slice-management { .slice-management {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
max-height: 65vh;
} }
.slice-content-container { .slice-content-container {
@@ -1018,7 +1027,7 @@ export default {
} }
.slice-cards-container { .slice-cards-container {
max-height: 50vh; max-height: 60vh;
overflow-y: auto; overflow-y: auto;
padding-right: 4px; padding-right: 4px;
@@ -1033,9 +1042,6 @@ export default {
.slice-card { .slice-card {
background: #f8f9fa; background: #f8f9fa;
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 14px;
margin-bottom: 12px; margin-bottom: 12px;
&:last-child { &:last-child {
@@ -1057,10 +1063,9 @@ export default {
.slice-card-content { .slice-card-content {
background: #fff; background: #fff;
border: 1px solid #f0f0f0; border: 1px solid #f0f0f0;
border-radius: 4px; border-radius: 8px;
padding: 12px; padding: 12px;
max-height: 200px; text-align: left;
overflow-y: auto;
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 4px; width: 4px;
@@ -1069,6 +1074,13 @@ export default {
background: #e0e0e0; background: #e0e0e0;
border-radius: 2px; border-radius: 2px;
} }
.clice-index {
color: #467afe;
display: inline-block;
padding: 4px 6px;
background: #e6f0fe;
border-radius: 4px;
}
} }
.content-text { .content-text {
@@ -1088,51 +1100,9 @@ export default {
.slice-pagination { .slice-pagination {
display: flex; display: flex;
align-items: center; justify-content: center;
gap: 6px;
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid #e5e7eb;
} }
.slice-page-size-select {
width: 100px;
margin-right: 8px;
}
.slice-page-btn {
min-width: 32px;
height: 28px;
padding: 0 8px;
border-radius: 4px;
border: 1px solid #e5e7eb;
background: #fff;
color: #606266;
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
&:hover {
background: #f5f7fa;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
&.active {
background: #6b80eb;
color: #fff;
border-color: #6b80eb;
}
}
.slice-total-text {
margin-left: 8px;
color: #9ca3af;
font-size: 13px;
}
/* ========== Selected Files List ========== */ /* ========== Selected Files List ========== */
.selected-files-section { .selected-files-section {
@@ -1216,28 +1186,92 @@ export default {
} }
.result-chunk { .result-chunk {
background: #f8f9fa;
border: 1px solid #e5e7eb; border: 1px solid #e5e7eb;
border-radius: 8px; border-radius: 8px;
padding: 14px; padding: 14px;
margin-bottom: 12px; margin-bottom: 12px;
display: flex;
align-items: center;
gap: 14px;
p { .chunk-left {
margin: 0 0 8px 0; flex-shrink: 0;
font-size: 14px; display: flex;
align-items: center;
.chunk-similarity {
// font-size: 12px;
color: #409eff;
white-space: nowrap;
.similarity-label {
// font-size: 12px;
color: #333;
margin-bottom: 4px;
}
.similarity-value {
text-align: left;
margin-top: 4px;
font-size: 16px;
font-weight: 600;
margin-bottom: 12px;
}
.similarity-progress {
margin-top: 8px;
::v-deep(.el-progress-bar__outer) {
border-radius: 3px;
background-color: rgba(64, 158, 255, 0.1);
}
::v-deep(.el-progress-bar__inner) {
border-radius: 3px;
background: #4a7cfd;
}
}
}
} }
.chunk-content { .chunk-right {
background: #fff; flex: 1;
border: 1px solid #f0f0f0; min-width: 0;
border-radius: 4px;
padding: 12px; .chunk-right-header {
max-height: 150px; display: flex;
overflow-y: auto; justify-content: space-between;
font-size: 14px; align-items: center;
line-height: 1.6; margin-bottom: 8px;
white-space: pre-wrap;
word-break: break-word; .chunk-source {
font-size: 14px;
font-weight: 600;
}
.chunk-index {
color: #467afe;
display: inline-block;
padding: 4px 6px;
background: #e6f0fe;
border-radius: 4px;
}
}
.chunk-content {
text-align: left;
white-space: normal;
background: #fff;
border-radius: 4px;
// max-height: 150px;
// overflow-y: auto;
font-size: 14px;
line-height: 1.6;
// white-space: pre-wrap;
// word-break: break-word;
> p {
margin: 0;
}
}
} }
} }
} }