This commit is contained in:
2026-08-05 15:35:55 +08:00
parent 20c1805efb
commit dc767d83f9
11 changed files with 239 additions and 53 deletions
+4
View File
@@ -12,6 +12,10 @@ export function deleteDocument(id) {
return request.post('/document/delete', { id })
}
export function getDocumentDetail(id) {
return request.get('/document/detail', { params: { id } })
}
export function reembedDocument(id) {
return request.post('/document/reembed', { id })
}
+112 -22
View File
@@ -39,25 +39,38 @@
<el-pagination class="dd-page-bar" layout="total, prev, pager, next" :total="total"
:page-size="pageSize" v-model:current-page="page" @current-change="load" />
<el-drawer v-model="chunkDrawer" :title="chunkDrawerTitle" size="55%">
<div class="chunk-toolbar">
<el-input v-model="chunkKeyword" placeholder="搜索分块内容" clearable style="width: 240px" @change="loadChunks" />
<el-drawer v-model="chunkDrawer" :title="chunkDrawerTitle" size="70%" class="chunk-drawer">
<div class="drawer-split">
<div class="drawer-left">
<div class="pane-title">原始内容{{ rawContent.length }} </div>
<div v-loading="rawLoading" class="raw-scroll">
<pre v-if="rawContent" class="raw-content">{{ rawContent }}</pre>
<div v-else-if="!rawLoading" class="raw-empty">{{ rawEmptyTip }}</div>
</div>
</div>
<div class="drawer-right">
<div class="chunk-toolbar">
<el-input v-model="chunkKeyword" placeholder="搜索分块内容" clearable style="width: 240px" @change="loadChunks" />
</div>
<div class="chunk-table-scroll">
<el-table :data="chunks" v-loading="chunkLoading" size="small">
<el-table-column type="index" label="#" width="50" />
<el-table-column label="内容" min-width="300">
<template #default="{ row }">
<div class="chunk-content">{{ row.content }}</div>
</template>
</el-table-column>
<el-table-column label="操作" width="110" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="editChunk(row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-pagination class="dd-page-bar" layout="total, prev, pager, next" :total="chunkTotal"
:page-size="chunkPageSize" v-model:current-page="chunkPage" @current-change="loadChunks" />
</div>
</div>
<el-table :data="chunks" v-loading="chunkLoading" size="small">
<el-table-column type="index" label="#" width="50" />
<el-table-column label="内容" min-width="300">
<template #default="{ row }">
<div class="chunk-content">{{ row.content }}</div>
</template>
</el-table-column>
<el-table-column label="操作" width="110" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="editChunk(row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination class="dd-page-bar" layout="total, prev, pager, next" :total="chunkTotal"
:page-size="chunkPageSize" v-model:current-page="chunkPage" @current-change="loadChunks" />
</el-drawer>
<el-dialog v-model="editDialog" title="编辑分块" width="640px">
@@ -79,6 +92,7 @@ import { ArrowLeft, UploadFilled } from '@element-plus/icons-vue'
import { listDatasets } from '../api/dataset.js'
import { listDocuments, uploadDocument, deleteDocument, reembedDocument, retryParseTask } from '../api/document.js'
import { listChunks, updateChunk } from '../api/chunk.js'
import { getDocumentDetail } from '../api/document.js'
const route = useRoute()
const datasetId = computed(() => Number(route.params.id))
@@ -93,6 +107,9 @@ const uploading = ref(false)
const chunkDrawer = ref(false)
const chunkDrawerTitle = ref('')
const rawContent = ref('')
const rawLoading = ref(false)
const rawEmptyTip = ref('')
const chunks = ref([])
const chunkTotal = ref(0)
const chunkPage = ref(1)
@@ -199,8 +216,8 @@ async function reembed(row) {
} catch {
return
}
await reembedDocument(row.id)
ElMessage.success('重新向量化完成')
const d = await reembedDocument(row.id)
ElMessage.success(`已重新向量化 ${d.count} 个分块`)
}
async function openChunks(row) {
@@ -209,9 +226,27 @@ async function openChunks(row) {
chunkDrawer.value = true
chunkPage.value = 1
chunkKeyword.value = ''
rawContent.value = ''
await loadRaw()
await loadChunks()
}
// 加载文档原始全文(存量文档后端会现场解析回填)
async function loadRaw() {
if (!currentDoc) return
rawLoading.value = true
rawEmptyTip.value = ''
try {
const d = await getDocumentDetail(currentDoc.id)
rawContent.value = (d.document && d.document.content) || ''
if (!rawContent.value) rawEmptyTip.value = '该文档无原始内容'
} catch (e) {
rawEmptyTip.value = '原始内容加载失败:' + (e.message || e)
} finally {
rawLoading.value = false
}
}
async function loadChunks() {
if (!currentDoc) return
chunkLoading.value = true
@@ -265,15 +300,70 @@ async function saveChunk() {
margin-top: 10px;
justify-content: flex-end;
}
.chunk-drawer :deep(.el-drawer__body) {
overflow: hidden;
}
.drawer-split {
display: flex;
gap: 16px;
height: 100%;
}
.drawer-left,
.drawer-right {
display: flex;
flex-direction: column;
min-height: 0;
}
.drawer-left {
flex: 0 0 42%;
border-right: 1px solid #ebeef5;
padding-right: 16px;
}
.drawer-right {
flex: 1;
}
.pane-title {
font-size: 13px;
font-weight: 600;
color: #303133;
margin-bottom: 8px;
}
.raw-scroll {
flex: 1;
overflow: auto;
min-height: 0;
}
.raw-content {
margin: 0;
padding: 10px 12px;
white-space: pre-wrap;
word-break: break-all;
background: #f8f8f8;
border-radius: 4px;
font-size: 13px;
line-height: 1.7;
color: #303133;
}
.raw-empty {
padding: 12px;
font-size: 13px;
color: #909399;
}
.chunk-toolbar {
margin-bottom: 10px;
flex-shrink: 0;
}
.chunk-table-scroll {
flex: 1;
overflow: auto;
min-height: 0;
}
.chunk-content {
max-height: 60px;
overflow: hidden;
font-size: 13px;
color: #606266;
line-height: 1.6;
white-space: pre-wrap;
word-break: break-all;
}
.edit-tip {
margin-top: 6px;
+2 -2
View File
@@ -46,9 +46,9 @@
</el-select>
<div class="ds-tip">标题分块保留标题与段落结构递归字符分块按分隔符切分通用文本语义分块按语义相似度切分质量更高但解析更慢且需绑定向量模型</div>
</el-form-item>
<el-form-item label="分块大小">
<el-form-item :label="form.chunk_strategy === 'semantic' ? '上限大小' : '分块大小'">
<el-input-number v-model="form.chunk_size" :min="50" :max="5000" :step="50" style="width: 100%" />
<div class="ds-tip">{{ form.chunk_strategy === 'semantic' ? '语义分块按句子相似度切分,此值决定最小分块大小(低于该值的候选块会继续合并)' : '每块最大字数,超长段落自动按句号/换行切分' }}</div>
<div class="ds-tip">{{ form.chunk_strategy === 'semantic' ? '语义分块按句子相似度切分,此值为安全上限(max_chunk_size),超过上限的块会再切分;语义分块不支持重叠' : '每块最大字数,超长段落自动按句号/换行切分' }}</div>
</el-form-item>
<el-form-item v-if="form.chunk_strategy !== 'semantic'" label="重叠字数">
<el-input-number v-model="form.chunk_overlap" :min="0" :max="500" :step="10" style="width: 100%" />
+2 -2
View File
@@ -11,8 +11,8 @@
<el-table-column prop="name" label="名称" min-width="140" />
<el-table-column prop="model_name" label="模型" min-width="160" show-overflow-tooltip />
<el-table-column prop="endpoint_url" label="接口地址" min-width="220" show-overflow-tooltip />
<el-table-column prop="dimension" label="维度" width="80">
<template #default="{ row }">{{ row.model_type === 'embedding' ? row.dimension : '-' }}</template>
<el-table-column v-if="modelType === 'embedding'" prop="dimension" label="维度" width="80">
<template #default="{ row }">{{ row.dimension }}</template>
</el-table-column>
<el-table-column label="默认" width="110" align="center">
<template #default="{ row }">