feat(knowledge): 新增知识库上传预览功能

- 添加上传至知识库前的预览页面
- 实现分段设置和预处理规则配置
-增加文档详情和预估块数展示
- 优化预览界面样式,添加面包屑导航- 新增保存并处理功能,支持不同上传模式
This commit is contained in:
陈昱达
2025-05-07 17:56:37 +08:00
parent d3f2aa0bde
commit 185c8f12f5
10 changed files with 489 additions and 31 deletions

View File

@@ -0,0 +1,74 @@
import request from '@/assets/js/utils/request'
import getUrl from '@/assets/js/utils/get-url'
// 上传到知识库 - 预处理文件
export function preprocessEmbedding(data) {
return request({
url: getUrl('/datasetDocumentEx/preprocess/embedding'),
method: 'post',
data
})
}
// 上传到知识库 - 拆分内容
export function splitEmbedding(data) {
return request({
url: getUrl(`/datasetDocumentEx/split/embedding`),
method: 'post',
data
})
}
// 上传到知识库 - 题词内容
export function extractEmbedding(data) {
return request({
url: getUrl(`/datasetDocumentEx/extract/embedding`),
method: 'post',
data
})
}
// 上传到知识库 - 原文件
export function directEmbedding(data) {
return request({
url: getUrl(`/datasetDocumentEx/direct/embedding`),
method: 'post',
data
})
}
// 分段预览 - 源文件
export function segmentEstimate(data) {
return request({
url: getUrl(`/datasetDocumentEx/direct/segment_estimate`),
method: 'post',
data
})
}
// 分段预览-预处理文件
export function preprocessSegmentEstimate(data) {
return request({
url: getUrl(`/datasetDocumentEx/preprocess/segment_estimate`),
method: 'post',
data
})
}
// 分段预览-拆分内容
export function splitSegmentEstimate(data) {
return request({
url: getUrl(`/datasetDocumentEx/split/segment_estimate`),
method: 'post',
data
})
}
// 分段预览-题词内容
export function extractSegmentEstimate(data) {
return request({
url: getUrl(`/datasetDocumentEx/extract/segment_estimate`),
method: 'post',
data
})
}

View File

@@ -165,6 +165,13 @@
}
}
}
& .el-input {
&.is-focus {
& .el-input__inner {
border-color: $--color-primary;
}
}
}
}
.el-select-dropdown__item {

View File

@@ -1,4 +1,5 @@
.el-radio__inner {
.el-radio__inner,
.el-checkbox__inner {
border-radius: 4px;
width: 14px;
height: 14px;
@@ -6,12 +7,16 @@
border-color: $--color-primary;
}
}
.el-radio__input {
.el-radio__input,
.el-checkbox__input {
&.is-checked {
& + .el-radio__label {
& + .el-radio__label,
& + .el-checkbox__label {
color: $--color-primary;
}
.el-radio__inner {
.el-radio__inner,
.el-checkbox__inner {
background: #fff;
border-color: $--color-primary;
&:after {
@@ -19,9 +24,18 @@
width: 7px;
height: 7px;
border-radius: 1px;
left: 50%;
top: 50%;
border: unset;
transform: translate(-50%, -50%) scale(1);
}
}
}
&.is-focus {
& .el-checkbox__inner {
border-color: $--color-primary;
}
}
}
.el-switch {
& .el-switch__core {

View File

@@ -288,18 +288,27 @@ export default {
methods: {
// 上传文档到知识库
uploadKnowledge() {
preprocessEmbedding({ documentId: this.documentId }).then(res => {
if (res) {
this.$message.success('上传成功')
this.$router.push({
path: '/knowledge/detail/segments',
path: '/knowledge/reviewKnowledge',
query: {
documentId: this.documentId,
datasetId: this.$route.query.datasetId
}
})
datasetId: this.$route.query.datasetId,
active: '1'
}
})
// preprocessEmbedding({ documentId: this.documentId }).then(res => {
// if (res) {
// this.$message.success('上传成功')
// this.$router.push({
// path: '/knowledge/detail/segments',
// query: {
// documentId: this.documentId,
// datasetId: this.$route.query.datasetId
// }
// })
// }
// })
},
//changePage
// 分页发生改变时

1
src/icons/svg/chart.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[10px]" data-icon="SelectionMod" aria-hidden="true"><g id="Group"><path id="Vector" d="M2.5 10H0V7.5H2.5V10Z" fill="#676F83"></path><path id="Vector_2" d="M6.25 6.25H3.75V3.75H6.25V6.25Z" fill="#676F83"></path><path id="Vector_3" d="M2.5 6.25H0V3.75H2.5V6.25Z" fill="#676F83"></path><path id="Vector_4" d="M6.25 2.5H3.75V0H6.25V2.5Z" fill="#676F83"></path><path id="Vector_5" d="M2.5 2.5H0V0H2.5V2.5Z" fill="#676F83"></path><path id="Vector_6" d="M10 2.5H7.5V0H10V2.5Z" fill="#676F83"></path><path id="Vector_7" d="M9.58332 7.91663H7.91666V9.58329H9.58332V7.91663Z" fill="#676F83"></path><path id="Vector_8" d="M9.58332 4.16663H7.91666V5.83329H9.58332V4.16663Z" fill="#676F83"></path><path id="Vector_9" d="M5.83332 7.91663H4.16666V9.58329H5.83332V7.91663Z" fill="#676F83"></path></g></svg>

After

Width:  |  Height:  |  Size: 898 B

View File

@@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0002 0.833374C10.4604 0.833374 10.8335 1.20647 10.8335 1.66671V2.54597C11.5977 2.63056 12.328 2.8301 13.0061 3.12703L13.4452 2.36666C13.6752 1.96808 14.1849 1.83152 14.5835 2.06164C14.9821 2.29176 15.1186 2.80142 14.8885 3.2L14.4488 3.96146C15.0552 4.40877 15.5915 4.94516 16.0388 5.55143L16.8003 5.11177C17.1989 4.88165 17.7086 5.01821 17.9387 5.41679C18.1688 5.81537 18.0322 6.32502 17.6337 6.55514L16.8732 6.99418C17.1702 7.67226 17.3697 8.40254 17.4543 9.16679H18.3335C18.7937 9.16679 19.1668 9.53987 19.1668 10.0001C19.1668 10.4604 18.7937 10.8335 18.3335 10.8335H17.4543C17.3697 11.5977 17.1702 12.328 16.8732 13.0061L17.6337 13.4452C18.0322 13.6753 18.1688 14.185 17.9387 14.5835C17.7086 14.9821 17.1989 15.1187 16.8003 14.8885L16.0388 14.4489C15.5915 15.0551 15.0551 15.5915 14.4488 16.0388L14.8885 16.8004C15.1186 17.1989 14.9821 17.7085 14.5835 17.9387C14.1849 18.1688 13.6752 18.0322 13.4452 17.6337L13.0061 16.8732C12.328 17.1701 11.5977 17.3697 10.8335 17.4543V18.3334C10.8335 18.7936 10.4604 19.1667 10.0002 19.1667C9.53991 19.1667 9.16683 18.7936 9.16683 18.3334V17.4543C8.40258 17.3697 7.6723 17.1701 6.99424 16.8732L6.55516 17.6337C6.32505 18.0323 5.81539 18.1689 5.41681 17.9388C5.01824 17.7086 4.88167 17.199 5.11179 16.8005L5.55149 16.0388C4.94519 15.5915 4.40878 15.0551 3.96145 14.4488L3.19993 14.8885C2.80135 15.1186 2.2917 14.982 2.06158 14.5835C1.83145 14.1849 1.96802 13.6752 2.3666 13.4451L3.12704 13.006C2.83011 12.328 2.63056 11.5977 2.54598 10.8335L1.66679 10.8334C1.20655 10.8334 0.833474 10.4602 0.833496 10C0.833521 9.53979 1.20663 9.16671 1.66687 9.16671L2.54599 9.16679C2.63058 8.40254 2.8301 7.67229 3.12701 6.99424L2.3666 6.55523C1.96802 6.32512 1.83145 5.81546 2.06157 5.41687C2.29169 5.0183 2.80135 4.88173 3.19992 5.11185L3.96142 5.55148C4.40874 4.94518 4.94515 4.40877 5.55145 3.96144L5.11179 3.19991C4.88167 2.80133 5.01823 2.29167 5.41681 2.06156C5.81539 1.83144 6.32505 1.968 6.55516 2.36657L6.9942 3.12702C7.67228 2.83009 8.40258 2.63055 9.16683 2.54597V1.66671C9.16683 1.20647 9.53991 0.833374 10.0002 0.833374ZM6.39156 5.41655C5.81089 5.87442 5.31917 6.44029 4.94695 7.08361C4.45095 7.94087 4.16681 8.93604 4.16681 10.0001C4.16681 11.0642 4.45096 12.0594 4.94698 12.9167C5.3192 13.56 5.81091 14.1259 6.39159 14.5837L8.1 11.6246C7.72651 11.1881 7.50015 10.6208 7.50015 10.0001C7.50015 9.37946 7.72651 8.81212 8.09999 8.37562L6.39156 5.41655ZM9.54316 7.54194L7.83418 4.5819C8.50325 4.31416 9.23383 4.16679 10.0002 4.16679C11.0642 4.16679 12.0594 4.45095 12.9167 4.94697C13.8022 5.45932 14.541 6.19807 15.0533 7.08357C15.4173 7.71277 15.6673 8.41629 15.7745 9.16679H12.3579C12.0147 8.19579 11.0887 7.50012 10.0002 7.50012C9.84433 7.50012 9.69149 7.51446 9.54316 7.54194ZM12.3579 10.8335C12.0147 11.8045 11.0887 12.5001 10.0002 12.5001C9.84433 12.5001 9.69149 12.4858 9.54316 12.4583L7.8342 15.4184C8.50325 15.6861 9.23383 15.8335 10.0002 15.8335C11.0642 15.8335 12.0594 15.5493 12.9167 15.0533C13.8022 14.541 14.5409 13.8022 15.0532 12.9167C15.4173 12.2875 15.6673 11.584 15.7745 10.8335H12.3579Z" fill="#444CE7"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -99,6 +99,18 @@ export default [
icon: 'el-icon-s-home'
}
},
{
path: '/knowledge/reviewKnowledge',
name: 'knowledge-review-knowledge',
component: () =>
import('@/views/knowledge/detail/reviewKnowledge.vue'),
meta: {
title: '直接上传至知识库',
icon: 'el-icon-s-home'
}
},
{
path: '/knowledge/detail/segments',
name: 'knowledge-segments',
@@ -235,6 +247,7 @@ export default [
}
]
},
{
path: '/intelligentAgent',
name: 'intelligentAgent',

View File

@@ -74,17 +74,26 @@ export default {
},
methods: {
emitKnowledgeDataset() {
embedding({ documentId: this.documentId }).then(res => {
if (res) {
this.$router.push({
path: '/knowledge/detail/segments',
path: '/knowledge/reviewKnowledge',
query: {
documentId: this.documentId,
datasetId: this.$route.query.datasetId
}
})
datasetId: this.$route.query.datasetId,
active: '2'
}
})
// embedding({ documentId: this.documentId }).then(res => {
// if (res) {
// this.$router.push({
// path: '/knowledge/detail/segments',
// query: {
// documentId: this.documentId,
// datasetId: this.$route.query.datasetId
// }
// })
// }
// })
},
handleNodeClick() {},
handleClose() {

View File

@@ -199,7 +199,6 @@ export default {
},
created() {},
mounted() {
console.log(123123)
let { documentId, datasetId, activeLevel, isMd } = this.$route.query
if (documentId) {
this.documentId = documentId
@@ -217,7 +216,6 @@ export default {
case 1:
setTimeout(() => {
if (isMd) {
console.log(this.$refs.splitConfig)
this.$refs.splitConfig.previewOperation(documentId)
}
}, 1000)

View File

@@ -1,9 +1,153 @@
<!--直接上传到知识库之前的页面 预览页面-->
<template>
<div class="render-container"></div>
<div class="render-container">
<div slot="header" class="clearfix">
<h3>上传至知识库</h3>
</div>
<div class="flex align-items-s justify-content-b mt20">
<div
class="settings"
style="width: 49.5%;,max-height: calc(100vh - 110px)"
>
<div class="setting-item mb10">
<div class="title">分段设置</div>
<div class="body">
<div class="body-title flex">
<svg-icon
icon-class="setting-gear"
class-name="el-card mr10 is-always-shadow"
style="width: 30px;height: 30px;padding: 5px"
></svg-icon>
<div>
<div>通用</div>
<p class="theme-primary-desc-text mt10">
通用文本分块模式检索和召回的块是相同的
</p>
</div>
</div>
<div class="body-content">
<el-form inline label-position="top" :model="form">
<el-form-item label="分段标识符" prop="separator">
<el-input size="mini" v-model="form.separator"></el-input>
</el-form-item>
<el-form-item label="分段最大长度(tokens)" prop="maxTokens">
<el-input-number
controls-position="right"
style="width: 100%"
size="mini"
v-model="form.maxTokens"
></el-input-number>
</el-form-item>
<el-form-item label="分段重叠长度(tokens)" prop="chunkOverlap">
<el-input-number
style="width: 100%"
controls-position="right"
size="mini"
v-model="form.chunkOverlap"
></el-input-number>
</el-form-item>
</el-form>
<!-- 文本预处理规则-->
<div>
<el-divider content-position="left" style="margin: 0"
><h5>文本预处理规则</h5></el-divider
>
<el-checkbox
style="width: 100%"
class="mv10"
:value="1"
v-model="form.removeExtraSpaces"
label="替换掉连续的空格、换行符和制表符"
>
</el-checkbox>
<el-checkbox
style="width: 100%"
v-model="form.removeUrlsEmails"
:value="1"
label="删除所有 URL 和电子邮件地址"
></el-checkbox>
<el-divider></el-divider>
</div>
<!--使用Q&A语言-->
<div>
<!-- <el-checkbox-->
<!-- disabled-->
<!-- style="width: 100%"-->
<!-- label="删除所有 URL 和电子邮件地址"-->
<!-- >-->
<!-- 使用 Q&A 分段语言-->
<!-- <el-select size="mini" disabled></el-select>-->
<!-- </el-checkbox>-->
</div>
<!-- 预览与重置-->
<div class="mt10">
<el-button
class="line-button"
size="medium"
@click="review_logs"
>预览</el-button
>
<el-button size="medium" @click="resetForm">重置</el-button>
</div>
</div>
</div>
</div>
<div class="buttons text-right">
<el-button class="line-button" size="medium" @click="saveUs"
>保存并处理</el-button
>
</div>
</div>
<div
style="width: 49.5%;height: calc(100vh - 110px)"
class="el-card review"
>
<div class="title">
{{ documentDetail.knowledgeName }}
<span class="block" v-if="viewsDetail.total_segments">
预估块{{ viewsDetail.total_segments }}
</span>
</div>
<div
class="review-body"
style="height: calc(100% - 40px);overflow-y:auto"
>
<div v-for="(item, index) in viewsDetail.preview" class="review-item">
<div class="item-title mb10 flex ">
<span>
<svg-icon icon-class="chart"></svg-icon>
Chunk-{{ index + 1 }}
</span>
<span class="ml20">{{ item.content.length }}字符</span>
</div>
<div
style="padding: 0"
class="item-body mb10 view-body"
v-html="item.content.replace(/<sc/g, '< sc ')"
></div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { preprocessEmbedding, embedding } from '@/api/generatedApi/pdfApi'
import { getDatasetById, datasetDocumentEx } from '@/api/generatedApi'
import {
preprocessEmbedding,
splitEmbedding,
extractEmbedding,
directEmbedding,
segmentEstimate,
preprocessSegmentEstimate,
splitSegmentEstimate,
extractSegmentEstimate
} from '@/api/generatedApi/embedding'
// preprocessEmbedding 预览页面
// embedding 题词拆分
@@ -11,16 +155,201 @@ import { preprocessEmbedding, embedding } from '@/api/generatedApi/pdfApi'
export default {
name: 'reviewKnowledge',
data() {
return {}
return {
reset: {
mode: 'custom',
removeExtraSpaces: true,
removeUrlsEmails: false,
separator: '###',
maxTokens: 1000,
chunkOverlap: 50
},
form: {
mode: 'custom',
removeExtraSpaces: true,
removeUrlsEmails: false,
separator: '###',
maxTokens: 1000,
chunkOverlap: 50
},
datasetDetail: {},
documentDetail: {},
viewsDetail: {}
}
},
props: {},
watch: {},
components: {},
filters: {},
methods: {},
created() {},
methods: {
resetForm() {
this.form = JSON.parse(JSON.stringify(this.reset))
},
review_logs() {
let { documentId, active } = this.$route.query
let api = segmentEstimate
switch (active) {
case '0':
api = segmentEstimate
break
case '1':
api = preprocessSegmentEstimate
break
case '2':
api = splitSegmentEstimate
break
case '3':
api = extractSegmentEstimate
break
}
console.log(api)
api({
documentId,
segmentConfig: {
...this.form,
removeExtraSpaces: this.form.removeExtraSpaces ? 1 : 0,
removeUrlsEmails: this.form.removeUrlsEmails ? 1 : 0
}
}).then(res => {
this.viewsDetail = res.content.content
})
},
// 获取文档详情
getDocumentDetail() {
let { documentId } = this.$route.query
datasetDocumentEx({ documentId }).then(res => {
if (res) {
this.documentDetail = res.content.content
}
})
},
// 获取知识库详情
getDatasetDetail() {
getDatasetById({ id: this.$route.query.datasetId }).then(res => {
if (res) {
this.datasetDetail = res.content.content
}
})
},
// 保存 处理
saveUs() {
let { documentId, active } = this.$route.query
let api = directEmbedding
switch (active) {
case '0':
api = directEmbedding
break
case '1':
api = preprocessEmbedding
break
case '2':
api = splitEmbedding
break
case '3':
api = extractEmbedding
break
}
api({
documentId,
segmentConfig: {
...this.form,
removeExtraSpaces: this.form.removeExtraSpaces ? 1 : 0,
removeUrlsEmails: this.form.removeUrlsEmails ? 1 : 0
}
}).then(res => {
if (res) {
this.$router.push({
path: '/knowledge/detail',
query: {
datasetId: this.$route.query.datasetId
}
})
}
})
}
},
created() {
this.getDatasetDetail()
this.getDocumentDetail()
},
mounted() {},
computed: {}
}
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.el-divider--horizontal {
margin: 8px 0;
}
.render-container {
& .settings {
& .setting-item {
& .title {
font-weight: 600;
font-size: 14px;
}
& .body {
border: 1px solid #ebeef5;
margin-top: 5px;
border-radius: 8px;
& .body-title {
background: #f0f4fa;
padding: 10px;
& div {
font-weight: 600;
font-size: 13px;
}
& p {
font-size: 12px;
font-weight: unset;
}
}
& .body-content {
padding: 10px;
}
}
}
}
& .review {
& .title {
height: 40px;
line-height: 40px;
border-bottom: 1px solid #ebeef5;
font-weight: 600;
font-size: 15px;
padding: 0 10px;
& .block {
font-size: 11px;
padding: 2px 8px;
border: 1px solid #ebeef5;
border-radius: 5px;
background: #ebeef5;
}
}
& .review-body {
& .review-item {
padding: 10px;
& .item-title {
font-size: 12px;
color: #cecece;
}
& .item-body {
font-size: 14px;
font-weight: 400;
line-height: 20px;
}
&:hover {
background: #f0f4fa;
}
}
}
}
}
</style>