2025-11-04 16:21:01 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="welcome">
|
|
|
|
|
|
<HeaderBar />
|
|
|
|
|
|
<div class="operation-bar">
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<h2 class="page-title">{{ $t('knowledgeBaseManagement.title') }}</h2>
|
2025-11-04 16:21:01 +08:00
|
|
|
|
<div class="right-operations">
|
2026-06-03 16:49:08 +08:00
|
|
|
|
<el-input
|
2026-06-05 17:37:21 +08:00
|
|
|
|
:placeholder="$t('knowledgeBaseManagement.searchPlaceholder')"
|
2026-06-03 16:49:08 +08:00
|
|
|
|
v-model="searchKbName"
|
|
|
|
|
|
class="search-input"
|
|
|
|
|
|
@keyup.enter.native="handleSearchKb"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
prefix-icon="el-icon-search"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<el-button class="btn-upload" @click="handleSearchKb">
|
|
|
|
|
|
<i class="el-icon-search"></i>
|
2026-06-05 17:37:21 +08:00
|
|
|
|
{{ $t('knowledgeBaseManagement.search') }}
|
2026-06-03 16:49:08 +08:00
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button class="btn-new-kb" @click="showAddDialog">
|
|
|
|
|
|
<i class="el-icon-plus"></i>
|
2026-06-05 17:37:21 +08:00
|
|
|
|
{{ $t('knowledgeBaseManagement.addKnowledgeBase') }}
|
2026-06-03 16:49:08 +08:00
|
|
|
|
</el-button>
|
2025-11-04 16:21:01 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="main-wrapper">
|
|
|
|
|
|
<div class="content-panel">
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<div class="content-area" v-loading="loading" :element-loading-text="$t('knowledgeBaseManagement.loading')">
|
2026-06-03 16:49:08 +08:00
|
|
|
|
<!-- Knowledge Base Cards Section -->
|
|
|
|
|
|
<div class="kb-section">
|
|
|
|
|
|
<div class="kb-section-header">
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<div class="kb-section-title">{{ $t('knowledgeBaseManagement.switchKnowledgeBase') }}</div>
|
2025-11-04 16:21:01 +08:00
|
|
|
|
</div>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
<div class="kb-cards-wrapper">
|
|
|
|
|
|
<div class="kb-arrow left" @click="scrollCards(-1)" v-if="filteredKnowledgeBases.length > 0">
|
|
|
|
|
|
<i class="el-icon-arrow-left"></i>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="kb-cards" ref="kbCards">
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-for="(kb, index) in filteredKnowledgeBases"
|
|
|
|
|
|
:key="kb.datasetId"
|
|
|
|
|
|
class="kb-card"
|
|
|
|
|
|
:class="{ active: selectedKb && selectedKb.datasetId === kb.datasetId }"
|
|
|
|
|
|
@click="selectKnowledgeBase(kb)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="kb-card-actions-top">
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<button class="kb-action-icon" :title="$t('knowledgeBaseManagement.edit')" @click.stop="editKnowledgeBase(kb)">
|
2026-06-03 16:49:08 +08:00
|
|
|
|
<i class="el-icon-edit"></i>
|
|
|
|
|
|
</button>
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<button class="kb-action-icon delete" :title="$t('knowledgeBaseManagement.delete')" @click.stop="deleteKnowledgeBase(kb)">
|
2026-06-03 16:49:08 +08:00
|
|
|
|
<i class="el-icon-delete"></i>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="kb-card-top">
|
|
|
|
|
|
<div class="kb-card-icon" :class="getCardColorClass(index)">
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<ManualIcon :color="getIconColor(index)" />
|
2026-06-03 16:49:08 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="kb-card-info">
|
|
|
|
|
|
<div class="kb-card-name">{{ kb.name }}</div>
|
|
|
|
|
|
<div class="kb-card-bottom">
|
|
|
|
|
|
<div class="kb-card-meta">
|
|
|
|
|
|
<span>{{`文档 ${kb.documentCount || 0}` }}</span>
|
|
|
|
|
|
<el-divider direction="vertical" />
|
|
|
|
|
|
<span>{{ formatDate(kb.createdAt) }}</span>
|
|
|
|
|
|
<el-divider direction="vertical" />
|
|
|
|
|
|
<el-switch
|
|
|
|
|
|
v-model="kb.status"
|
|
|
|
|
|
:active-value="1"
|
|
|
|
|
|
:inactive-value="0"
|
|
|
|
|
|
active-color="#13ce66"
|
|
|
|
|
|
inactive-color="#ff4949"
|
|
|
|
|
|
@click.native.stop
|
|
|
|
|
|
@change="handleStatusChange(kb)"
|
|
|
|
|
|
></el-switch>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="kb-card-desc">{{ kb.description || '-' }}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div v-if="filteredKnowledgeBases.length === 0 && !loading" class="kb-empty">
|
|
|
|
|
|
<i class="el-icon-folder-opened"></i>
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<p>{{ $t('knowledgeBaseManagement.noData') }}</p>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="kb-arrow right" @click="scrollCards(1)" v-if="filteredKnowledgeBases.length > 0">
|
|
|
|
|
|
<i class="el-icon-arrow-right"></i>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-11-04 16:21:01 +08:00
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
<!-- Document Section -->
|
|
|
|
|
|
<KnowledgeBaseItem
|
|
|
|
|
|
ref="knowledgeBaseItem"
|
|
|
|
|
|
v-if="selectedKb"
|
|
|
|
|
|
:kb="selectedKb"
|
|
|
|
|
|
@upload="showUploadDialog"
|
|
|
|
|
|
@retrieval-test="showRetrievalTestDialog"
|
|
|
|
|
|
@view-slices="handleViewSlices"
|
|
|
|
|
|
@refresh="refreshDocuments"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<div v-else class="doc-empty-placeholder">
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<el-empty :description="$t('knowledgeBaseManagement.noData')"></el-empty>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
</div>
|
2025-11-04 16:21:01 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
<!-- Knowledge Base Dialog -->
|
2026-06-10 10:47:52 +08:00
|
|
|
|
<KnowledgeBaseDialog
|
2026-06-03 16:49:08 +08:00
|
|
|
|
ref="knowledgeBaseDialog"
|
|
|
|
|
|
:title="dialogTitle"
|
|
|
|
|
|
:visible.sync="dialogVisible"
|
|
|
|
|
|
:form="knowledgeBaseForm"
|
|
|
|
|
|
@submit="handleSubmit"
|
|
|
|
|
|
@cancel="dialogVisible = false"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Slice Dialog -->
|
2026-06-09 17:22:18 +08:00
|
|
|
|
<CustomDialog
|
2026-06-05 17:37:21 +08:00
|
|
|
|
:title="`${$t('knowledgeFileUpload.viewSlices')} - ${currentDocumentName}`"
|
2026-06-03 16:49:08 +08:00
|
|
|
|
:visible.sync="sliceDialogVisible"
|
2026-06-09 17:22:18 +08:00
|
|
|
|
width="1200px"
|
|
|
|
|
|
:footer="false"
|
2026-06-03 16:49:08 +08:00
|
|
|
|
>
|
|
|
|
|
|
<div class="slice-management">
|
|
|
|
|
|
<div v-loading="sliceLoading" class="slice-content-container">
|
|
|
|
|
|
<div v-if="sliceList.length > 0" class="slice-cards-container">
|
|
|
|
|
|
<div v-for="(slice, index) in sliceList" :key="index" class="slice-card">
|
|
|
|
|
|
<div class="slice-card-content">
|
2026-06-09 17:22:18 +08:00
|
|
|
|
<span class="clice-index">{{ $t('knowledgeFileUpload.slice') }} {{ (sliceCurrentPage - 1) * slicePageSize + index + 1 }}</span>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
<div class="content-text">{{ slice.content }}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div v-else class="no-slice-data">
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<el-empty :description="$t('knowledgeFileUpload.noSliceData')"></el-empty>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="slice-pagination">
|
2026-06-09 17:22:18 +08:00
|
|
|
|
<CustomPagination
|
|
|
|
|
|
:total="parseInt(sliceTotal)"
|
|
|
|
|
|
:current-page="sliceCurrentPage"
|
|
|
|
|
|
:page-size="slicePageSize"
|
|
|
|
|
|
:page-size-options="[10, 20, 50]"
|
|
|
|
|
|
@size-change="handleSliceSizeChange"
|
|
|
|
|
|
@page-change="handleSlicePageChange"
|
|
|
|
|
|
/>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-06-09 17:22:18 +08:00
|
|
|
|
</CustomDialog>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- Upload Dialog -->
|
2026-06-09 17:22:18 +08:00
|
|
|
|
<CustomDialog
|
2026-06-05 17:37:21 +08:00
|
|
|
|
:title="$t('knowledgeFileUpload.uploadDocument')"
|
2026-06-03 16:49:08 +08:00
|
|
|
|
:visible.sync="uploadDialogVisible"
|
|
|
|
|
|
width="800px"
|
|
|
|
|
|
@close="handleUploadDialogClose"
|
2026-06-09 17:22:18 +08:00
|
|
|
|
@confirm="handleBatchUploadSubmit"
|
2026-06-03 16:49:08 +08:00
|
|
|
|
>
|
|
|
|
|
|
<el-upload
|
|
|
|
|
|
ref="uploadRef"
|
|
|
|
|
|
action="#"
|
|
|
|
|
|
:auto-upload="false"
|
2026-06-09 17:22:18 +08:00
|
|
|
|
:show-file-list="false"
|
2026-06-03 16:49:08 +08:00
|
|
|
|
:on-change="handleFileChange"
|
|
|
|
|
|
multiple
|
|
|
|
|
|
accept=".doc,.docx,.pdf,.txt,.md,.mdx,.csv,.xls,.xlsx,.ppt,.pptx"
|
|
|
|
|
|
drag
|
|
|
|
|
|
>
|
|
|
|
|
|
<i class="el-icon-upload"></i>
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<div class="el-upload__text">{{ $t('knowledgeFileUpload.dragOrClick') }}</div>
|
|
|
|
|
|
<div class="el-upload__tip" slot="tip">{{ $t('knowledgeFileUpload.uploadTip') }}</div>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
</el-upload>
|
|
|
|
|
|
<!-- 已选择文件列表 -->
|
|
|
|
|
|
<div class="selected-files-section" v-if="selectedFilesList.length > 0">
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<h4>{{ $t('knowledgeFileUpload.selectedFiles') }} ({{ selectedFilesList.length }})</h4>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
<div class="selected-files-list">
|
|
|
|
|
|
<div v-for="(file, index) in selectedFilesList" :key="index" class="selected-file-item">
|
|
|
|
|
|
<div class="file-info">
|
|
|
|
|
|
<i class="el-icon-document"></i>
|
|
|
|
|
|
<span class="file-name">{{ file.name }}</span>
|
|
|
|
|
|
<span class="file-size">{{ formatFileSize(file.size) }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-button type="text" class="remove-btn" @click="removeSelectedFile(index)">
|
|
|
|
|
|
<i class="el-icon-close"></i>
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-06-09 17:22:18 +08:00
|
|
|
|
</CustomDialog>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- Retrieval Test Dialog -->
|
2026-06-09 17:22:18 +08:00
|
|
|
|
<CustomDialog
|
2026-06-05 17:37:21 +08:00
|
|
|
|
:title="$t('knowledgeFileUpload.retrievalTest')"
|
2026-06-03 16:49:08 +08:00
|
|
|
|
:visible.sync="retrievalTestDialogVisible"
|
|
|
|
|
|
width="900px"
|
|
|
|
|
|
:close-on-click-modal="false"
|
2026-06-09 17:22:18 +08:00
|
|
|
|
:confirm-text="$t('knowledgeFileUpload.executeTest')"
|
|
|
|
|
|
:confirmLoading="retrievalTestLoading"
|
|
|
|
|
|
@confirm="runRetrievalTest"
|
2026-06-03 16:49:08 +08:00
|
|
|
|
>
|
|
|
|
|
|
<div class="retrieval-test-form">
|
|
|
|
|
|
<el-form :model="retrievalTestForm" label-width="100px">
|
2026-06-05 17:37:21 +08:00
|
|
|
|
<el-form-item :label="$t('knowledgeFileUpload.testQuestion')" required>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="retrievalTestForm.question"
|
2026-06-09 17:22:18 +08:00
|
|
|
|
@keyup.enter.native="runRetrievalTest"
|
|
|
|
|
|
clearable
|
2026-06-05 17:37:21 +08:00
|
|
|
|
:placeholder="$t('knowledgeFileUpload.testQuestionPlaceholder')"
|
2026-06-03 16:49:08 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
<div v-if="retrievalTestResult" class="retrieval-test-result" style="margin-top: 20px;">
|
2026-06-09 17:22:18 +08:00
|
|
|
|
<div class="result-chunks" v-if="retrievalTestResult.chunks.length">
|
2026-06-03 16:49:08 +08:00
|
|
|
|
<div v-for="(chunk, index) in retrievalTestResult.chunks" :key="index" class="result-chunk">
|
2026-06-09 17:22:18 +08:00
|
|
|
|
<div class="chunk-left">
|
|
|
|
|
|
<div class="chunk-similarity">
|
|
|
|
|
|
<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>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-06-09 17:22:18 +08:00
|
|
|
|
<el-empty v-else :description="$t('knowledgeFileUpload.noSliceData')"></el-empty>
|
2026-06-03 16:49:08 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-06-09 17:22:18 +08:00
|
|
|
|
</CustomDialog>
|
2025-11-04 16:21:01 +08:00
|
|
|
|
|
|
|
|
|
|
<el-footer>
|
|
|
|
|
|
<version-footer />
|
|
|
|
|
|
</el-footer>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import Api from "@/apis/api";
|
|
|
|
|
|
import HeaderBar from "@/components/HeaderBar.vue";
|
|
|
|
|
|
import VersionFooter from "@/components/VersionFooter.vue";
|
|
|
|
|
|
import KnowledgeBaseDialog from "@/components/KnowledgeBaseDialog.vue";
|
2026-06-03 16:49:08 +08:00
|
|
|
|
import KnowledgeBaseItem from "./KnowledgeBaseItem.vue";
|
2026-06-05 17:37:21 +08:00
|
|
|
|
import ManualIcon from "@/components/ManualIcon.vue";
|
2026-06-09 17:22:18 +08:00
|
|
|
|
import CustomDialog from "@/components/CustomDialog.vue";
|
|
|
|
|
|
import CustomPagination from "@/components/CustomPagination.vue";
|
2025-11-04 16:21:01 +08:00
|
|
|
|
|
|
|
|
|
|
export default {
|
2026-06-09 17:22:18 +08:00
|
|
|
|
components: { HeaderBar, VersionFooter, KnowledgeBaseDialog, KnowledgeBaseItem, ManualIcon, CustomDialog, CustomPagination },
|
2025-11-04 16:21:01 +08:00
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
knowledgeBases: [],
|
|
|
|
|
|
selectedKb: null,
|
|
|
|
|
|
searchKbName: '',
|
2025-11-04 16:21:01 +08:00
|
|
|
|
loading: false,
|
|
|
|
|
|
dialogVisible: false,
|
2026-06-03 16:49:08 +08:00
|
|
|
|
dialogTitle: '',
|
2025-11-04 16:21:01 +08:00
|
|
|
|
knowledgeBaseForm: {
|
|
|
|
|
|
id: null,
|
|
|
|
|
|
datasetId: null,
|
2026-06-03 16:49:08 +08:00
|
|
|
|
name: '',
|
|
|
|
|
|
description: '',
|
2025-11-04 16:21:01 +08:00
|
|
|
|
status: 1
|
2026-06-03 16:49:08 +08:00
|
|
|
|
},
|
|
|
|
|
|
uploadDialogVisible: false,
|
|
|
|
|
|
sliceDialogVisible: false,
|
|
|
|
|
|
retrievalTestDialogVisible: false,
|
|
|
|
|
|
retrievalTestForm: {
|
|
|
|
|
|
question: ''
|
|
|
|
|
|
},
|
|
|
|
|
|
retrievalTestResult: null,
|
|
|
|
|
|
retrievalTestLoading: false,
|
|
|
|
|
|
selectedFilesList: [],
|
|
|
|
|
|
uploading: false,
|
|
|
|
|
|
currentDocumentId: '',
|
|
|
|
|
|
currentDocumentName: '',
|
|
|
|
|
|
sliceList: [],
|
|
|
|
|
|
sliceLoading: false,
|
|
|
|
|
|
sliceCurrentPage: 1,
|
|
|
|
|
|
slicePageSize: 10,
|
|
|
|
|
|
sliceTotal: 0,
|
2025-11-04 16:21:01 +08:00
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
filteredKnowledgeBases() {
|
|
|
|
|
|
if (!this.searchKbName) return this.knowledgeBases;
|
|
|
|
|
|
const keyword = this.searchKbName.toLowerCase();
|
|
|
|
|
|
return this.knowledgeBases.filter(kb =>
|
|
|
|
|
|
kb.name.toLowerCase().includes(keyword)
|
|
|
|
|
|
);
|
2025-11-04 16:21:01 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
created() {
|
|
|
|
|
|
this.fetchKnowledgeBases();
|
|
|
|
|
|
},
|
2025-11-04 16:21:01 +08:00
|
|
|
|
methods: {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
fetchKnowledgeBases() {
|
2025-11-04 16:21:01 +08:00
|
|
|
|
this.loading = true;
|
|
|
|
|
|
Api.knowledgeBase.getKnowledgeBaseList(
|
2026-06-03 16:49:08 +08:00
|
|
|
|
{ page: 1, page_size: 100, name: this.searchKbName },
|
2025-11-04 16:21:01 +08:00
|
|
|
|
(res) => {
|
|
|
|
|
|
this.loading = false;
|
|
|
|
|
|
if (res.data && res.data.code === 0) {
|
|
|
|
|
|
const pageData = res.data.data || {};
|
2026-06-03 16:49:08 +08:00
|
|
|
|
this.knowledgeBases = pageData.list || [];
|
|
|
|
|
|
if (this.knowledgeBases.length > 0 && !this.selectedKb) {
|
|
|
|
|
|
this.selectKnowledgeBase(this.knowledgeBases[0]);
|
|
|
|
|
|
} else if (this.selectedKb) {
|
|
|
|
|
|
const updated = this.knowledgeBases.find(kb => kb.datasetId === this.selectedKb.datasetId);
|
|
|
|
|
|
if (updated) this.selectedKb = updated;
|
|
|
|
|
|
}
|
2025-11-04 16:21:01 +08:00
|
|
|
|
} else {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(res.data?.msg || this.$t('knowledgeBaseManagement.getListFailed'));
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
() => {
|
|
|
|
|
|
this.loading = false;
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(this.$t('knowledgeBaseManagement.getListFailed'));
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
selectKnowledgeBase(kb) {
|
|
|
|
|
|
this.selectedKb = kb;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
refreshDocuments() {
|
|
|
|
|
|
this.$refs.knowledgeBaseItem?.fetchDocuments();
|
2025-11-04 16:21:01 +08:00
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
handleSearchKb() {
|
|
|
|
|
|
this.fetchKnowledgeBases();
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
scrollCards(direction) {
|
|
|
|
|
|
const container = this.$refs.kbCards;
|
|
|
|
|
|
if (container) {
|
|
|
|
|
|
container.scrollBy({ left: direction * 364, behavior: 'smooth' });
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
showAddDialog() {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.dialogTitle = this.$t('knowledgeBaseManagement.addKnowledgeBase');
|
2025-11-04 16:21:01 +08:00
|
|
|
|
this.knowledgeBaseForm = {
|
|
|
|
|
|
id: null,
|
|
|
|
|
|
datasetId: null,
|
2026-06-03 16:49:08 +08:00
|
|
|
|
name: '',
|
|
|
|
|
|
description: '',
|
2025-11-04 16:21:01 +08:00
|
|
|
|
status: 1
|
|
|
|
|
|
};
|
|
|
|
|
|
this.dialogVisible = true;
|
|
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
editKnowledgeBase(kb) {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.dialogTitle = this.$t('knowledgeBaseManagement.editKnowledgeBase');
|
2025-11-04 16:21:01 +08:00
|
|
|
|
this.knowledgeBaseForm = {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
id: kb.id,
|
|
|
|
|
|
datasetId: kb.datasetId,
|
|
|
|
|
|
name: kb.name,
|
|
|
|
|
|
description: kb.description || '',
|
|
|
|
|
|
status: kb.status,
|
|
|
|
|
|
ragModelId: kb.ragModelId
|
2025-11-04 16:21:01 +08:00
|
|
|
|
};
|
|
|
|
|
|
this.dialogVisible = true;
|
|
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
handleStatusChange(kb) {
|
|
|
|
|
|
const updateForm = {
|
|
|
|
|
|
id: kb.id,
|
|
|
|
|
|
datasetId: kb.datasetId,
|
|
|
|
|
|
name: kb.name,
|
|
|
|
|
|
description: kb.description,
|
|
|
|
|
|
status: kb.status
|
|
|
|
|
|
};
|
|
|
|
|
|
Api.knowledgeBase.updateKnowledgeBase(kb.datasetId, updateForm, (res) => {
|
|
|
|
|
|
if (res.data && res.data.code !== 0) {
|
|
|
|
|
|
this.fetchKnowledgeBases();
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(res.data?.msg || this.$t('knowledgeBaseManagement.updateFailed'));
|
2026-06-03 16:49:08 +08:00
|
|
|
|
} else {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.success(kb.status === 1 ? this.$t('knowledgeBaseManagement.enabled') : this.$t('knowledgeBaseManagement.disabled'));
|
2026-06-03 16:49:08 +08:00
|
|
|
|
if (this.selectedKb && this.selectedKb.datasetId === kb.datasetId) {
|
|
|
|
|
|
this.selectedKb = { ...kb };
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}, () => {
|
|
|
|
|
|
this.fetchKnowledgeBases();
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(this.$t('knowledgeBaseManagement.updateFailed'));
|
2026-06-03 16:49:08 +08:00
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
deleteKnowledgeBase(kb) {
|
|
|
|
|
|
this.$confirm(
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$t("knowledgeBaseManagement.confirmBatchDelete", {count: 1}),
|
|
|
|
|
|
this.$t('message.warning'),
|
2026-06-03 16:49:08 +08:00
|
|
|
|
{
|
2026-06-05 17:37:21 +08:00
|
|
|
|
confirmButtonText: this.$t('knowledgeBaseDialog.confirm'),
|
|
|
|
|
|
cancelButtonText: this.$t('knowledgeBaseDialog.cancel'),
|
2026-06-03 16:49:08 +08:00
|
|
|
|
type: 'warning'
|
|
|
|
|
|
}
|
|
|
|
|
|
).then(() => {
|
|
|
|
|
|
Api.knowledgeBase.deleteKnowledgeBase(kb.datasetId, (res) => {
|
2025-11-04 16:21:01 +08:00
|
|
|
|
if (res.data && res.data.code === 0) {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.success(this.$t('knowledgeBaseManagement.batchDeleteSuccess', { count: 1 }));
|
2026-06-03 16:49:08 +08:00
|
|
|
|
if (this.selectedKb && this.selectedKb.datasetId === kb.datasetId) {
|
|
|
|
|
|
this.selectedKb = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.fetchKnowledgeBases();
|
2025-11-04 16:21:01 +08:00
|
|
|
|
} else {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(res.data?.msg || this.$t('knowledgeBaseManagement.deleteFailed'));
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
2025-11-10 15:14:32 +08:00
|
|
|
|
}, (err) => {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(err?.data?.msg || this.$t('knowledgeBaseManagement.deleteFailed'));
|
2026-06-03 16:49:08 +08:00
|
|
|
|
});
|
|
|
|
|
|
}).catch(() => {});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
showUploadDialog() {
|
|
|
|
|
|
this.uploadDialogVisible = true;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
handleSubmit(form) {
|
|
|
|
|
|
if (form.id) {
|
|
|
|
|
|
Api.knowledgeBase.updateKnowledgeBase(form.datasetId, form, (res) => {
|
|
|
|
|
|
if (res.data && res.data.code === 0) {
|
|
|
|
|
|
this.dialogVisible = false;
|
|
|
|
|
|
this.fetchKnowledgeBases();
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.success(this.$t('knowledgeBaseManagement.updateSuccess'));
|
2025-11-10 15:14:32 +08:00
|
|
|
|
} else {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(res.data?.msg || this.$t('knowledgeBaseManagement.updateFailed'));
|
2025-11-10 15:14:32 +08:00
|
|
|
|
}
|
2026-06-03 16:49:08 +08:00
|
|
|
|
}, (err) => {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(err?.data?.msg || this.$t('knowledgeBaseManagement.updateFailed'));
|
2025-11-04 16:21:01 +08:00
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const createData = {
|
|
|
|
|
|
name: form.name,
|
|
|
|
|
|
description: form.description,
|
|
|
|
|
|
status: form.status,
|
|
|
|
|
|
ragModelId: form.ragModelId
|
|
|
|
|
|
};
|
|
|
|
|
|
Api.knowledgeBase.createKnowledgeBase(createData, (res) => {
|
|
|
|
|
|
if (res.data && res.data.code === 0) {
|
|
|
|
|
|
this.dialogVisible = false;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
this.fetchKnowledgeBases();
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.success(this.$t('knowledgeBaseManagement.addSuccess'));
|
2025-11-10 15:14:32 +08:00
|
|
|
|
} else {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(res.data?.msg || this.$t('knowledgeBaseManagement.addFailed'));
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
2025-11-05 16:03:19 +08:00
|
|
|
|
}, (err) => {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(err?.data?.msg || this.$t('knowledgeBaseManagement.addFailed'));
|
2025-11-04 16:21:01 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
handleUploadDialogClose() {
|
|
|
|
|
|
if (this.$refs.uploadRef) {
|
|
|
|
|
|
this.$refs.uploadRef.clearFiles();
|
|
|
|
|
|
}
|
|
|
|
|
|
this.selectedFilesList = [];
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-06-05 17:37:21 +08:00
|
|
|
|
handleFileChange(file) {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
if (!file || !file.raw) return;
|
2026-06-09 17:22:18 +08:00
|
|
|
|
// 文件上传前的验证
|
|
|
|
|
|
const isLt10M = file.size / 1024 / 1024 < 10;
|
|
|
|
|
|
if (!isLt10M) {
|
|
|
|
|
|
this.$message.error(this.$t('knowledgeFileUpload.fileSizeExceeded'));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-06-03 16:49:08 +08:00
|
|
|
|
this.selectedFilesList.push({
|
|
|
|
|
|
name: file.name,
|
|
|
|
|
|
size: file.size,
|
|
|
|
|
|
raw: file.raw
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
removeSelectedFile(index) {
|
|
|
|
|
|
this.selectedFilesList.splice(index, 1);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
formatFileSize(bytes) {
|
|
|
|
|
|
if (bytes === 0) return '0 B';
|
|
|
|
|
|
const k = 1024;
|
|
|
|
|
|
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
|
|
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
|
|
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
handleBatchUploadSubmit() {
|
|
|
|
|
|
if (this.selectedFilesList.length === 0) {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(this.$t('knowledgeFileUpload.fileRequired'));
|
2025-11-04 16:21:01 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
this.uploading = true;
|
|
|
|
|
|
|
|
|
|
|
|
const uploadPromises = this.selectedFilesList.map(file => {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
|
formData.append('file', file.raw);
|
|
|
|
|
|
|
|
|
|
|
|
Api.knowledgeBase.uploadDocument(this.selectedKb.datasetId, formData,
|
|
|
|
|
|
({ data }) => {
|
|
|
|
|
|
if (data && data.code === 0) {
|
|
|
|
|
|
resolve({ success: true, fileName: file.name });
|
|
|
|
|
|
} else {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
reject({ success: false, fileName: file.name, error: data?.msg || this.$t('knowledgeFileUpload.uploadFailed') });
|
2026-06-03 16:49:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
(err) => {
|
|
|
|
|
|
if (err && err.data) {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
reject({ success: false, fileName: file.name, error: err.data.msg || err.msg || this.$t('knowledgeFileUpload.uploadFailed') });
|
2026-06-03 16:49:08 +08:00
|
|
|
|
} else {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
reject({ success: false, fileName: file.name, error: this.$t('knowledgeFileUpload.uploadFailed') });
|
2026-06-03 16:49:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
console.error('上传文档失败:', err);
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
2025-11-04 16:21:01 +08:00
|
|
|
|
});
|
|
|
|
|
|
});
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
Promise.all(uploadPromises.map(p => p.catch(e => e)))
|
|
|
|
|
|
.then(results => {
|
|
|
|
|
|
this.uploading = false;
|
|
|
|
|
|
|
|
|
|
|
|
const successCount = results.filter(r => r.success).length;
|
|
|
|
|
|
const failedCount = results.filter(r => !r.success).length;
|
|
|
|
|
|
|
|
|
|
|
|
if (successCount > 0) {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.success(this.$t('knowledgeFileUpload.uploadSuccess'));
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
if (failedCount > 0) {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(this.$t('knowledgeFileUpload.uploadFailed'));
|
2025-11-10 15:14:32 +08:00
|
|
|
|
}
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
if (successCount > 0) {
|
|
|
|
|
|
this.uploadDialogVisible = false;
|
|
|
|
|
|
this.refreshDocuments();
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(error => {
|
|
|
|
|
|
this.uploading = false;
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(this.$t('knowledgeFileUpload.uploadFailed'));
|
|
|
|
|
|
console.error(error);
|
2025-11-04 16:21:01 +08:00
|
|
|
|
});
|
|
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
showRetrievalTestDialog() {
|
|
|
|
|
|
this.retrievalTestForm.question = '';
|
|
|
|
|
|
this.retrievalTestResult = null;
|
|
|
|
|
|
this.retrievalTestDialogVisible = true;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
runRetrievalTest() {
|
2026-06-09 17:22:18 +08:00
|
|
|
|
if (this.retrievalTestLoading) return;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
if (!this.retrievalTestForm.question.trim()) {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(this.$t('knowledgeFileUpload.testQuestionRequired'));
|
2026-06-03 16:49:08 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.retrievalTestLoading = true;
|
|
|
|
|
|
Api.knowledgeBase.retrievalTest(
|
|
|
|
|
|
this.selectedKb.datasetId,
|
|
|
|
|
|
{ question: this.retrievalTestForm.question.trim() },
|
|
|
|
|
|
({ data }) => {
|
|
|
|
|
|
this.retrievalTestLoading = false;
|
|
|
|
|
|
if (data && data.code === 0) {
|
|
|
|
|
|
this.retrievalTestResult = data.data || data;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.$message.error(data?.msg || '召回测试失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
(err) => {
|
|
|
|
|
|
this.retrievalTestLoading = false;
|
|
|
|
|
|
this.$message.error(err?.data?.msg || '召回测试失败');
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
2026-06-03 16:49:08 +08:00
|
|
|
|
);
|
2025-11-04 16:21:01 +08:00
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
handleViewSlices(doc) {
|
|
|
|
|
|
this.currentDocumentId = doc.id;
|
|
|
|
|
|
this.currentDocumentName = doc.name;
|
|
|
|
|
|
this.sliceDialogVisible = true;
|
|
|
|
|
|
this.sliceCurrentPage = 1;
|
|
|
|
|
|
this.fetchSlices();
|
2025-11-04 16:21:01 +08:00
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
fetchSlices() {
|
|
|
|
|
|
if (!this.selectedKb || !this.currentDocumentId) return;
|
|
|
|
|
|
this.sliceLoading = true;
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
page: this.sliceCurrentPage,
|
|
|
|
|
|
page_size: this.slicePageSize
|
|
|
|
|
|
};
|
|
|
|
|
|
Api.knowledgeBase.listChunks(
|
|
|
|
|
|
this.selectedKb.datasetId,
|
|
|
|
|
|
this.currentDocumentId,
|
|
|
|
|
|
params,
|
|
|
|
|
|
({ data }) => {
|
|
|
|
|
|
this.sliceLoading = false;
|
|
|
|
|
|
if (data && data.code === 0) {
|
|
|
|
|
|
const responseData = data.data;
|
|
|
|
|
|
if (responseData && responseData.list) {
|
|
|
|
|
|
this.sliceList = responseData.list;
|
|
|
|
|
|
this.sliceTotal = responseData.total || responseData.list.length;
|
|
|
|
|
|
} else if (responseData && responseData.chunks && Array.isArray(responseData.chunks)) {
|
|
|
|
|
|
this.sliceList = responseData.chunks;
|
|
|
|
|
|
this.sliceTotal = responseData.total || responseData.chunks.length;
|
|
|
|
|
|
} else if (Array.isArray(responseData)) {
|
|
|
|
|
|
this.sliceList = responseData;
|
|
|
|
|
|
this.sliceTotal = responseData.length;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.sliceList = [];
|
|
|
|
|
|
this.sliceTotal = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(data?.msg || this.$t('knowledgeBaseManagement.getListFailed'));
|
2026-06-03 16:49:08 +08:00
|
|
|
|
this.sliceList = [];
|
|
|
|
|
|
this.sliceTotal = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
(err) => {
|
|
|
|
|
|
this.sliceLoading = false;
|
|
|
|
|
|
if (err && err.data) {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(err.data.msg || err.msg || this.$t('knowledgeBaseManagement.getListFailed'));
|
2026-06-03 16:49:08 +08:00
|
|
|
|
} else {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
this.$message.error(this.$t('knowledgeBaseManagement.getListFailed'));
|
2026-06-03 16:49:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
this.sliceList = [];
|
|
|
|
|
|
this.sliceTotal = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
2025-11-04 16:21:01 +08:00
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
handleSliceSizeChange(val) {
|
|
|
|
|
|
this.slicePageSize = val;
|
|
|
|
|
|
this.sliceCurrentPage = 1;
|
|
|
|
|
|
this.fetchSlices();
|
2025-11-04 16:21:01 +08:00
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
2026-06-09 17:22:18 +08:00
|
|
|
|
handleSlicePageChange(page) {
|
|
|
|
|
|
this.sliceCurrentPage = page;
|
|
|
|
|
|
this.fetchSlices();
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
getCardColorClass(index) {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
const colors = ['blue', 'green', 'purple', 'orange', 'pink', 'cyan'];
|
|
|
|
|
|
return colors[index % colors.length];
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getIconColor(index) {
|
|
|
|
|
|
const colors = ['#2f5bff', '#34c759', '#6a5cff', '#ff9500', '#f43f7a', '#00c9db'];
|
2026-06-03 16:49:08 +08:00
|
|
|
|
return colors[index % colors.length];
|
2025-11-04 16:21:01 +08:00
|
|
|
|
},
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
getStatusClass(kb) {
|
|
|
|
|
|
return kb.status === 1 ? 'active' : 'inactive';
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getStatusText(kb) {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
return kb.status === 1 ? this.$t('knowledgeBaseManagement.enabled') : this.$t('knowledgeBaseManagement.disabled');
|
2026-06-03 16:49:08 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
formatDate(dateString) {
|
2025-11-04 16:21:01 +08:00
|
|
|
|
if (!dateString) return '';
|
|
|
|
|
|
const date = new Date(dateString);
|
2026-06-03 16:49:08 +08:00
|
|
|
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
formatFileSize(bytes) {
|
|
|
|
|
|
if (!bytes) return '0 B';
|
|
|
|
|
|
const units = ['B', 'KB', 'MB', 'GB'];
|
|
|
|
|
|
let i = 0;
|
|
|
|
|
|
let size = bytes;
|
|
|
|
|
|
while (size >= 1024 && i < units.length - 1) {
|
|
|
|
|
|
size /= 1024;
|
|
|
|
|
|
i++;
|
|
|
|
|
|
}
|
|
|
|
|
|
return size.toFixed(1) + ' ' + units[i];
|
|
|
|
|
|
},
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.welcome {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
min-width: 900px;
|
|
|
|
|
|
min-height: 506px;
|
|
|
|
|
|
height: 100vh;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
background: linear-gradient(to bottom right, #dce8ff, #e4eeff, #e6cbfd) center;
|
|
|
|
|
|
background-size: cover;
|
|
|
|
|
|
-webkit-background-size: cover;
|
|
|
|
|
|
-o-background-size: cover;
|
|
|
|
|
|
overflow: hidden;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.main-wrapper {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
height: calc(100vh - 63px - 35px - 72px);
|
|
|
|
|
|
margin: 0 22px;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.operation-bar {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 16px 24px;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.page-title {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
margin: 0;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.right-operations {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
|
align-items: center;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.search-input {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
width: 240px;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
.btn-upload {
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
padding: 0 20px;
|
|
|
|
|
|
border: 1px solid #2f5bff !important;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
color: #2f5bff;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
background: #f2f6ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-new-kb {
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
padding: 0 20px;
|
|
|
|
|
|
border: none !important;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
background: linear-gradient(135deg, #6a5cff, #2f5bff) !important;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
transition: opacity 0.2s;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
|
}
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content-panel {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
border-radius: 15px;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content-area {
|
2026-06-03 16:49:08 +08:00
|
|
|
|
flex: 1;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
min-width: 600px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 20px;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
/* ========== Knowledge Base Cards Section ========== */
|
|
|
|
|
|
.kb-section {
|
|
|
|
|
|
background: #fff;
|
2026-06-05 17:37:21 +08:00
|
|
|
|
border-radius: 10px;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
padding: 14px 20px;
|
|
|
|
|
|
box-shadow: 0 4px 16px rgba(31, 42, 68, 0.06);
|
|
|
|
|
|
border: 1px solid #f0f3f9;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
.kb-section-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
margin-bottom: 10px;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
.kb-section-title {
|
|
|
|
|
|
font-size: 20px;
|
2026-06-05 17:37:21 +08:00
|
|
|
|
font-weight: 500;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
color: #1f2a44;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
.kb-cards-wrapper {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 12px;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
.kb-cards {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 24px;
|
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
|
padding: 10px;
|
|
|
|
|
|
scroll-behavior: smooth;
|
|
|
|
|
|
scrollbar-width: none;
|
|
|
|
|
|
|
|
|
|
|
|
&::-webkit-scrollbar {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-arrow {
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border: 1px solid #e7ecf5;
|
|
|
|
|
|
box-shadow: 0 4px 16px rgba(31, 42, 68, 0.06);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
color: #2f5bff;
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
border-color: #2f5bff;
|
|
|
|
|
|
box-shadow: 0 4px 20px rgba(47, 91, 255, 0.15);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-card {
|
2026-06-05 17:37:21 +08:00
|
|
|
|
width: 300px;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
border: 1px solid transparent;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all 0.25s;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2026-06-05 17:37:21 +08:00
|
|
|
|
flex: 0 0 300px;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
position: relative;
|
|
|
|
|
|
box-shadow: 0 0 10px rgba(47, 91, 255, 0.15);
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
box-shadow: 0 0 10px rgba(47, 91, 255, 0.15);
|
|
|
|
|
|
border: 1px solid #6b80eb;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.active {
|
|
|
|
|
|
border: 1px solid #6b80eb;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-card-top {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-card-actions-top {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 10px;
|
|
|
|
|
|
right: 4px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-action-icon {
|
|
|
|
|
|
width: 24px;
|
|
|
|
|
|
height: 24px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
color: #6b7280;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
color: #2f5bff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.delete:hover {
|
|
|
|
|
|
color: #ff5a5f;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-card-icon {
|
|
|
|
|
|
width: 56px;
|
|
|
|
|
|
height: 56px;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
|
|
|
|
|
|
&.blue {
|
|
|
|
|
|
background: linear-gradient(135deg, #e8ecff, #d4daff);
|
|
|
|
|
|
color: #2f5bff;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.purple {
|
|
|
|
|
|
background: linear-gradient(135deg, #ede8ff, #dcd4ff);
|
|
|
|
|
|
color: #6a5cff;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.green {
|
|
|
|
|
|
background: linear-gradient(135deg, #e2f9ed, #c8f0d8);
|
|
|
|
|
|
color: #34c759;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.orange {
|
|
|
|
|
|
background: linear-gradient(135deg, #fff3e0, #ffe4b8);
|
|
|
|
|
|
color: #ff9500;
|
|
|
|
|
|
}
|
2026-06-05 17:37:21 +08:00
|
|
|
|
&.pink {
|
|
|
|
|
|
background: linear-gradient(135deg, #ffe4ed, #ffc8db);
|
|
|
|
|
|
color: #f43f7a;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.cyan {
|
|
|
|
|
|
background: linear-gradient(135deg, #dff8fa, #c4f0f4);
|
|
|
|
|
|
color: #00c9db;
|
|
|
|
|
|
}
|
2026-06-03 16:49:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-card-info {
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-card-name {
|
|
|
|
|
|
font-size: 18px;
|
2026-06-05 17:37:21 +08:00
|
|
|
|
font-weight: 500;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
color: #1f2a44;
|
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-card-desc {
|
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
color: #7888a8;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
display: -webkit-box;
|
|
|
|
|
|
-webkit-line-clamp: 2;
|
|
|
|
|
|
-webkit-box-orient: vertical;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-card-bottom {
|
|
|
|
|
|
margin-top: auto;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-card-meta {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #a0aec0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.kb-empty {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
min-height: 120px;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
color: #b0bbc9;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
|
|
|
|
|
|
i {
|
|
|
|
|
|
font-size: 36px;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ========== Slice Dialog ========== */
|
|
|
|
|
|
.slice-management {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.slice-content-container {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.slice-cards-container {
|
2026-06-09 17:22:18 +08:00
|
|
|
|
max-height: 60vh;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
padding-right: 4px;
|
|
|
|
|
|
|
|
|
|
|
|
&::-webkit-scrollbar {
|
|
|
|
|
|
width: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
&::-webkit-scrollbar-thumb {
|
|
|
|
|
|
background: #e0e0e0;
|
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.slice-card {
|
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.slice-header-info {
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
p {
|
|
|
|
|
|
margin: 0;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
font-size: 14px;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
strong {
|
|
|
|
|
|
color: #374151;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
.slice-card-content {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border: 1px solid #f0f0f0;
|
2026-06-09 17:22:18 +08:00
|
|
|
|
border-radius: 8px;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
padding: 12px;
|
2026-06-09 17:22:18 +08:00
|
|
|
|
text-align: left;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
&::-webkit-scrollbar {
|
|
|
|
|
|
width: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
&::-webkit-scrollbar-thumb {
|
|
|
|
|
|
background: #e0e0e0;
|
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
|
}
|
2026-06-09 17:22:18 +08:00
|
|
|
|
.clice-index {
|
|
|
|
|
|
color: #467afe;
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
padding: 4px 6px;
|
|
|
|
|
|
background: #e6f0fe;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
}
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
.content-text {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.no-slice-data {
|
|
|
|
|
|
min-height: 200px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.slice-pagination {
|
|
|
|
|
|
display: flex;
|
2026-06-09 17:22:18 +08:00
|
|
|
|
justify-content: center;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ========== Selected Files List ========== */
|
|
|
|
|
|
.selected-files-section {
|
|
|
|
|
|
margin-top: 16px;
|
|
|
|
|
|
border: 1px solid #e5e7eb;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
background-color: #f8f9fa;
|
|
|
|
|
|
|
|
|
|
|
|
h4 {
|
|
|
|
|
|
margin: 0 0 12px 0;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.selected-files-list {
|
|
|
|
|
|
max-height: 180px;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.selected-file-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
|
background-color: white;
|
|
|
|
|
|
border: 1px solid #e5e7eb;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-info {
|
2025-11-04 16:21:01 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
.el-icon-document {
|
|
|
|
|
|
color: #409eff;
|
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
|
font-size: 16px;
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
|
|
|
|
|
.file-name {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
margin-right: 12px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
max-width: 300px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-size {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.remove-btn {
|
|
|
|
|
|
color: #f56c6c;
|
|
|
|
|
|
padding: 4px;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
color: #f78989;
|
|
|
|
|
|
background-color: #fef0f0;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 16:49:08 +08:00
|
|
|
|
/* ========== Retrieval Test Dialog ========== */
|
|
|
|
|
|
.retrieval-test-form {
|
|
|
|
|
|
.result-chunks {
|
|
|
|
|
|
max-height: 400px;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.result-chunk {
|
|
|
|
|
|
border: 1px solid #e5e7eb;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
padding: 14px;
|
|
|
|
|
|
margin-bottom: 12px;
|
2026-06-09 17:22:18 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 14px;
|
2026-06-03 16:49:08 +08:00
|
|
|
|
|
2026-06-09 17:22:18 +08:00
|
|
|
|
.chunk-left {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-09 17:22:18 +08:00
|
|
|
|
.chunk-right {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
|
|
|
|
|
|
.chunk-right-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
2026-06-03 16:49:08 +08:00
|
|
|
|
}
|
2025-11-04 16:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|