增加分段预览功能,在知识库查看文件,可以看到其分段内容与信息
This commit is contained in:
parent
2ae5f2f4ad
commit
6f60a25b5a
|
|
@ -0,0 +1,9 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function getSegmentList(params: { datasetId: string; documentId: string }) {
|
||||||
|
return request({
|
||||||
|
url: '/brichat-service/documentSegment/selectSegments',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -236,51 +236,76 @@
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 预览抽屉 -->
|
<!-- 预览抽屉 -->
|
||||||
<el-drawer
|
|
||||||
v-model="previewDrawerVisible"
|
|
||||||
:title="t('vabI18n.knowledge.document.preview.title')"
|
|
||||||
:direction="'rtl'"
|
|
||||||
size="60%"
|
|
||||||
class="preview-drawer"
|
|
||||||
:close-on-click-modal="false"
|
|
||||||
>
|
|
||||||
<div v-loading="previewLoading">
|
|
||||||
<!-- TXT 文件预览 -->
|
|
||||||
<div v-if="previewFileType === 'txt'" class="text-preview-container">
|
|
||||||
<pre class="text-preview">{{ previewTextContent }}</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Markdown 文件预览 -->
|
<el-drawer
|
||||||
<div v-if="['md', 'markdown', 'mdx'].includes(previewFileType.toLowerCase())"
|
v-model="previewDrawerVisible"
|
||||||
class="markdown-preview-container">
|
:title="t('vabI18n.knowledge.document.preview.title')"
|
||||||
<div class="markdown-preview" v-html="previewMarkdownContent"></div>
|
:direction="'rtl'"
|
||||||
</div>
|
size="80%"
|
||||||
|
class="preview-drawer"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<div v-loading="previewLoading" class="drawer-content-container" style="display: flex; height: 100%;">
|
||||||
|
|
||||||
<vue-office-pdf
|
<!-- 左侧分段数据容器 -->
|
||||||
v-if="previewFileType === 'pdf'"
|
<div class="segment-list-container" style="width: 30%; border-right: 1px solid #ebeef5; overflow-y: auto; padding: 10px;">
|
||||||
:src="previewFileUrl"
|
<h3>分段数据</h3>
|
||||||
@rendered="renderedHandler"
|
<el-form v-if="segmentList.length > 0">
|
||||||
@error="errorHandler"
|
<el-form-item
|
||||||
/>
|
v-for="(segment) in segmentList"
|
||||||
|
:key="segment.id"
|
||||||
|
style="cursor: pointer; margin-bottom: 5px;"
|
||||||
|
>
|
||||||
|
<span>分段{{segment.position}}:</span>
|
||||||
|
<h4>{{segment.content}}</h4>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div v-else>
|
||||||
|
暂无分段数据
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<vue-office-docx
|
<!-- 右侧原预览内容 -->
|
||||||
v-if="previewFileType == 'doc' || previewFileType == 'docx'"
|
<div class="preview-content-container" style="flex: 1; padding: 10px; overflow-y: auto;">
|
||||||
:src="previewFileUrl"
|
<!-- TXT 文件预览 -->
|
||||||
@rendered="renderedHandler"
|
<div v-if="previewFileType === 'txt'" class="text-preview-container">
|
||||||
@error="errorHandler"
|
<pre class="text-preview">{{ previewTextContent }}</pre>
|
||||||
/>
|
</div>
|
||||||
|
|
||||||
<vue-office-excel
|
<!-- Markdown 文件预览 -->
|
||||||
v-if="previewFileType == 'xlsx' || previewFileType == 'xls' || previewFileType == 'csv'"
|
<div v-if="['md', 'markdown', 'mdx'].includes(previewFileType.toLowerCase())"
|
||||||
:src="previewFileUrl"
|
class="markdown-preview-container">
|
||||||
style="height: 100vh;"
|
<div class="markdown-preview" v-html="previewMarkdownContent"></div>
|
||||||
@rendered="renderedHandler"
|
</div>
|
||||||
@error="errorHandler"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</el-drawer>
|
|
||||||
|
|
||||||
<!-- 重命名对话框 -->
|
<vue-office-pdf
|
||||||
|
v-if="previewFileType === 'pdf'"
|
||||||
|
:src="previewFileUrl"
|
||||||
|
@rendered="renderedHandler"
|
||||||
|
@error="errorHandler"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<vue-office-docx
|
||||||
|
v-if="previewFileType == 'doc' || previewFileType == 'docx'"
|
||||||
|
:src="previewFileUrl"
|
||||||
|
@rendered="renderedHandler"
|
||||||
|
@error="errorHandler"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<vue-office-excel
|
||||||
|
v-if="previewFileType == 'xlsx' || previewFileType == 'xls' || previewFileType == 'csv'"
|
||||||
|
:src="previewFileUrl"
|
||||||
|
style="height: 100vh;"
|
||||||
|
@rendered="renderedHandler"
|
||||||
|
@error="errorHandler"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 重命名对话框 -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="renameDialogVisible"
|
v-model="renameDialogVisible"
|
||||||
:title="t('vabI18n.knowledge.document.renameDialog.title')"
|
:title="t('vabI18n.knowledge.document.renameDialog.title')"
|
||||||
|
|
@ -410,7 +435,8 @@
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
|
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
|
||||||
import VueOfficePdf from '@vue-office/pdf'
|
import VueOfficePdf from '@vue-office/pdf'
|
||||||
import { getDatasetDocPage, uploadDocument, deleteDocument, downloadDocument, previewDocumentUrl, renameDocument, createFolder, CreateFolderReq } from '@/api/dataset'
|
import { getDatasetDocPage, uploadDocument, deleteDocument, downloadDocument, previewDocumentUrl, renameDocument, createFolder, CreateFolderReq} from '@/api/dataset'
|
||||||
|
import {getSegmentList} from "@/api/Segment"
|
||||||
import DocUpload from './DocUpload.vue'
|
import DocUpload from './DocUpload.vue'
|
||||||
//引入VueOfficeDocx组件
|
//引入VueOfficeDocx组件
|
||||||
import VueOfficeDocx from '@vue-office/docx'
|
import VueOfficeDocx from '@vue-office/docx'
|
||||||
|
|
@ -421,6 +447,7 @@ import VueOfficeExcel from '@vue-office/excel'
|
||||||
//引入相关样式
|
//引入相关样式
|
||||||
import '@vue-office/excel/lib/index.css'
|
import '@vue-office/excel/lib/index.css'
|
||||||
|
|
||||||
|
|
||||||
import VueOfficePptx from '@vue-office/pptx'
|
import VueOfficePptx from '@vue-office/pptx'
|
||||||
|
|
||||||
// 引入 markdown 解析器和代码高亮
|
// 引入 markdown 解析器和代码高亮
|
||||||
|
|
@ -491,7 +518,7 @@ folderName: [
|
||||||
{ pattern: /^[^<>:"/\\|?*]+$/, message: t('vabI18n.knowledge.document.createFolderDialog.rules.namePattern', '文件夹名称不能包含特殊字符'), trigger: 'blur' }
|
{ pattern: /^[^<>:"/\\|?*]+$/, message: t('vabI18n.knowledge.document.createFolderDialog.rules.namePattern', '文件夹名称不能包含特殊字符'), trigger: 'blur' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
const segmentList= ref([])
|
||||||
// 右键菜单相关
|
// 右键菜单相关
|
||||||
const contextMenuVisible = ref(false)
|
const contextMenuVisible = ref(false)
|
||||||
const contextMenuPosition = reactive({
|
const contextMenuPosition = reactive({
|
||||||
|
|
@ -511,7 +538,10 @@ name: string;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
uploadDialogVisible.value = false
|
||||||
|
segmentList.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 组件卸载时清理
|
// 组件卸载时清理
|
||||||
|
|
@ -636,6 +666,10 @@ try {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
previewDrawerVisible.value = false,
|
||||||
|
segmentList.value = []
|
||||||
|
}
|
||||||
const loading = ElLoading.service({
|
const loading = ElLoading.service({
|
||||||
lock: true,
|
lock: true,
|
||||||
text: t('vabI18n.knowledge.document.messages.deleteing'),
|
text: t('vabI18n.knowledge.document.messages.deleteing'),
|
||||||
|
|
@ -848,7 +882,6 @@ const handlePreview = async (row: FileItem) => {
|
||||||
previewDrawerVisible.value = true
|
previewDrawerVisible.value = true
|
||||||
previewLoading.value = true
|
previewLoading.value = true
|
||||||
|
|
||||||
console.log("row",row)
|
|
||||||
previewFileType.value = row.fileType;
|
previewFileType.value = row.fileType;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -866,6 +899,13 @@ const handlePreview = async (row: FileItem) => {
|
||||||
breaks: true,
|
breaks: true,
|
||||||
gfm: true
|
gfm: true
|
||||||
})
|
})
|
||||||
|
const resp = await getSegmentList({
|
||||||
|
datasetId:datasetId.value,
|
||||||
|
documentId: row.difyDocId
|
||||||
|
})
|
||||||
|
if (resp.data?.length) {
|
||||||
|
segmentList.value = resp.data
|
||||||
|
}
|
||||||
|
|
||||||
previewMarkdownContent.value = marked.parse(content) as string
|
previewMarkdownContent.value = marked.parse(content) as string
|
||||||
}
|
}
|
||||||
|
|
@ -2163,4 +2203,82 @@ transform: scale(0.9) translateY(-5px);
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.segment-list-container {
|
||||||
|
width: 30%;
|
||||||
|
border-right: 1px solid #ebeef5;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 16px;
|
||||||
|
background: #f9fafc;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 16px; // 内边距增加
|
||||||
|
border-radius: 8px;
|
||||||
|
background: white;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||||||
|
min-height: 100px; // 增加最小高度,让卡片更高
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center; // 让内容垂直居中
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f0f4ff;
|
||||||
|
transform: translateX(2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #606266;
|
||||||
|
margin-top: 4px;
|
||||||
|
word-break: break-word; // 防止长文本溢出
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-family: "Microsoft YaHei", "微软雅黑", sans-serif; // 设置微软雅黑字体
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #4a4a4a;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 4;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧预览内容保持原有样式 */
|
||||||
|
.preview-content-container {
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.bjtds.brichat.controller;
|
||||||
|
|
||||||
|
import com.bjtds.brichat.entity.dto.SegmentDto;
|
||||||
|
import com.bjtds.brichat.service.dify.DocumentSegmentService;
|
||||||
|
import com.bjtds.brichat.util.ResultUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.checkerframework.checker.units.qual.C;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@Slf4j
|
||||||
|
@CrossOrigin(value = "*", maxAge = 3600)
|
||||||
|
@RequestMapping("/documentSegment")
|
||||||
|
public class DocumentSegmentController {
|
||||||
|
@Autowired
|
||||||
|
private DocumentSegmentService documentSegmentService;
|
||||||
|
|
||||||
|
@GetMapping("/selectSegments")
|
||||||
|
public ResultUtils SelectSegments(@RequestParam("documentId") String documentId, @RequestParam("datasetId") String datasetId) {
|
||||||
|
List<SegmentDto> segments = documentSegmentService.SelectSegments(documentId, datasetId);
|
||||||
|
segments.sort(Comparator.comparing(SegmentDto::getPosition));
|
||||||
|
return ResultUtils.success(segments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.bjtds.brichat.mapper.postgresql;
|
||||||
|
|
||||||
|
|
||||||
|
import com.bjtds.brichat.entity.dto.SegmentDto;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface DifyDocumentSegmentMapper {
|
||||||
|
|
||||||
|
List<SegmentDto> SelectSegmentsByDocumentIdAndDatasetId(String documentId, String datasetId);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.bjtds.brichat.service.dify;
|
||||||
|
|
||||||
|
import com.bjtds.brichat.entity.dto.SegmentDto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface DocumentSegmentService {
|
||||||
|
|
||||||
|
List<SegmentDto> SelectSegments(String documentId, String datasetId);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.bjtds.brichat.service.dify.impl;
|
||||||
|
|
||||||
|
import com.bjtds.brichat.entity.dto.SegmentDto;
|
||||||
|
import com.bjtds.brichat.mapper.postgresql.DifyDocumentSegmentMapper;
|
||||||
|
import com.bjtds.brichat.service.dify.DocumentSegmentService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DocumentSegmentServiceImpl implements DocumentSegmentService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DifyDocumentSegmentMapper difyDocumentSegmentMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SegmentDto> SelectSegments(String documentId, String datasetId) {
|
||||||
|
return difyDocumentSegmentMapper.SelectSegmentsByDocumentIdAndDatasetId(documentId, datasetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.bjtds.brichat.mapper.postgresql.DifyDocumentSegmentMapper">
|
||||||
|
<select id="SelectSegmentsByDocumentIdAndDatasetId" resultType="com.bjtds.brichat.entity.dto.SegmentDto">
|
||||||
|
select *
|
||||||
|
from document_segments
|
||||||
|
where
|
||||||
|
<if test="documentId != null">
|
||||||
|
document_id = CAST(#{documentId} as uuid) and
|
||||||
|
</if>
|
||||||
|
<if test="datasetId != null">
|
||||||
|
dataset_id = CAST(#{datasetId} as uuid)
|
||||||
|
</if>
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
Loading…
Reference in New Issue