feat: PDF Preview (#3502)

This commit is contained in:
shaohuzhang1 2025-07-07 22:58:23 +08:00 committed by GitHub
parent b52c972ac0
commit 7f7704042e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 49 additions and 23 deletions

View File

@ -3,7 +3,7 @@ SELECT
knowledge."name" AS "knowledge_name", knowledge."name" AS "knowledge_name",
knowledge."type" AS "knowledge_type", knowledge."type" AS "knowledge_type",
"document"."name" AS "document_name", "document"."name" AS "document_name",
"document"."meta" AS "meta", "document"."meta"::json AS "meta",
"document"."hit_handling_method" AS "hit_handling_method", "document"."hit_handling_method" AS "hit_handling_method",
"document"."directly_return_similarity" as "directly_return_similarity" "document"."directly_return_similarity" as "directly_return_similarity"
FROM FROM

View File

@ -817,6 +817,7 @@ class DocumentSerializers(serializers.Serializer):
@staticmethod @staticmethod
def get_document_paragraph_model(knowledge_id, instance: Dict): def get_document_paragraph_model(knowledge_id, instance: Dict):
source_meta = {'source_file_id': instance.get('source_file_id')} if instance.get('source_file_id') else {}
document_model = Document( document_model = Document(
**{ **{
'knowledge_id': knowledge_id, 'knowledge_id': knowledge_id,
@ -826,7 +827,8 @@ class DocumentSerializers(serializers.Serializer):
lambda x, y: x + y, lambda x, y: x + y,
[len(p.get('content')) for p in instance.get('paragraphs', [])], [len(p.get('content')) for p in instance.get('paragraphs', [])],
0), 0),
'meta': instance.get('meta') if instance.get('meta') is not None else {}, 'meta': {**instance.get('meta'), **source_meta} if instance.get(
'meta') is not None else source_meta,
'type': instance.get('type') if instance.get('type') is not None else KnowledgeType.BASE 'type': instance.get('type') if instance.get('type') is not None else KnowledgeType.BASE
}) })

View File

@ -9,7 +9,7 @@
class="content" class="content"
@mouseup="openControl" @mouseup="openControl"
:style="{ :style="{
'padding-right': showUserAvatar ? 'var(--padding-left)' : '0' 'padding-right': showUserAvatar ? 'var(--padding-left)' : '0',
}" }"
> >
<el-card shadow="always" class="mb-8 border-r-8" style="--el-card-padding: 6px 16px"> <el-card shadow="always" class="mb-8 border-r-8" style="--el-card-padding: 6px 16px">
@ -46,7 +46,7 @@
:executionIsRightPanel="props.executionIsRightPanel" :executionIsRightPanel="props.executionIsRightPanel"
@open-execution-detail="emit('openExecutionDetail')" @open-execution-detail="emit('openExecutionDetail')"
@openParagraph="emit('openParagraph')" @openParagraph="emit('openParagraph')"
@openParagraphDocument="(val: string)=>emit('openParagraphDocument', val)" @openParagraphDocument="(val: string) => emit('openParagraphDocument', val)"
v-if="showSource(chatRecord) && index === chatRecord.answer_text_list.length - 1" v-if="showSource(chatRecord) && index === chatRecord.answer_text_list.length - 1"
/> />
</el-card> </el-card>
@ -56,7 +56,7 @@
class="content" class="content"
:style="{ :style="{
'padding-left': showAvatar ? 'var(--padding-left)' : '0', 'padding-left': showAvatar ? 'var(--padding-left)' : '0',
'padding-right': showUserAvatar ? 'var(--padding-left)' : '0' 'padding-right': showUserAvatar ? 'var(--padding-left)' : '0',
}" }"
> >
<OperationButton <OperationButton
@ -92,7 +92,12 @@ const props = defineProps<{
const { user } = useStore() const { user } = useStore()
const emit = defineEmits(['update:chatRecord', 'openExecutionDetail', 'openParagraph','openParagraphDocument']) const emit = defineEmits([
'update:chatRecord',
'openExecutionDetail',
'openParagraph',
'openParagraphDocument',
])
const showAvatar = computed(() => { const showAvatar = computed(() => {
return user.isEnterprise() ? props.application.show_avatar : true return user.isEnterprise() ? props.application.show_avatar : true
@ -130,8 +135,8 @@ const answer_text_list = computed(() => {
chat_record_id: undefined, chat_record_id: undefined,
child_node: undefined, child_node: undefined,
runtime_node_id: undefined, runtime_node_id: undefined,
reasoning_content: undefined reasoning_content: undefined,
} },
] ]
} else if (item instanceof Array) { } else if (item instanceof Array) {
return item return item

View File

@ -1,7 +1,18 @@
<template> <template>
<div> <div style="width: 100%; height: 100%">
TODO 内容 <embed v-if="is_pdf" style="width: 100%; height: 100%" :src="pdfSrc" />
</div> </div>
</template> </template>
<script setup lang="ts"></script> <script setup lang="ts">
import { computed } from 'vue'
const props = defineProps<{
detail?: any
}>()
const is_pdf = computed(() => {
return props.detail?.meta?.source_file_id
})
const pdfSrc = computed(() => {
return `${window.MaxKB.prefix}/oss/file/${props.detail?.meta?.source_file_id}`
})
</script>

View File

@ -17,7 +17,12 @@
<div class="flex-between"> <div class="flex-between">
<div class="flex align-center"> <div class="flex align-center">
<img :src="getImgUrl(item && item?.document_name)" alt="" width="24" /> <img :src="getImgUrl(item && item?.document_name)" alt="" width="24" />
<div class="ml-4 ellipsis-1" :title="item?.document_name" v-if="!item.source_url" @click="openParagraphDocument(item)"> <div
class="ml-4 ellipsis-1"
:title="item?.document_name"
v-if="!item.source_url"
@click="openParagraphDocument(item)"
>
<p>{{ item && item?.document_name }}</p> <p>{{ item && item?.document_name }}</p>
</div> </div>
<div class="ml-8" v-else> <div class="ml-8" v-else>
@ -101,7 +106,7 @@ const props = defineProps({
}, },
}) })
const emit = defineEmits(['openExecutionDetail', 'openParagraph','openParagraphDocument']) const emit = defineEmits(['openExecutionDetail', 'openParagraph', 'openParagraphDocument'])
const dialogVisible = ref(false) const dialogVisible = ref(false)
const dialogTitle = ref('') const dialogTitle = ref('')
@ -134,9 +139,10 @@ function openExecutionDetail(row: any) {
} }
function openParagraphDocument(row: any) { function openParagraphDocument(row: any) {
if (props.executionIsRightPanel) { if (props.executionIsRightPanel) {
emit('openParagraphDocument',row) emit('openParagraphDocument', row)
return return
} }
currentComponent.value = ParagraphDocumentContent currentComponent.value = ParagraphDocumentContent
dialogTitle.value = row.document_name dialogTitle.value = row.document_name
currentChatDetail.value = row currentChatDetail.value = row

View File

@ -301,7 +301,7 @@
:detail="executionDetail" :detail="executionDetail"
:type="applicationDetail?.type" :type="applicationDetail?.type"
/> />
<ParagraphDocumentContent v-else /> <ParagraphDocumentContent :detail="rightPanelDetail" v-else />
</div> </div>
</el-splitter-panel> </el-splitter-panel>
</el-splitter> </el-splitter>
@ -609,6 +609,7 @@ function openParagraphDocument(detail: any, row: any) {
rightPanelTitle.value = row.document_name rightPanelTitle.value = row.document_name
rightPanelType.value = 'paragraphDocument' rightPanelType.value = 'paragraphDocument'
rightPanelSize.value = 400 rightPanelSize.value = 400
rightPanelDetail.value = row
} }
function closeExecutionDetail() { function closeExecutionDetail() {
@ -716,11 +717,11 @@ function closeExecutionDetail() {
.add-button { .add-button {
border: 1px solid var(--el-color-primary); border: 1px solid var(--el-color-primary);
background-color: #3370FF1A; background-color: #3370ff1a;
color: #3370FF; color: #3370ff;
font-weight: 500; font-weight: 500;
&:hover { &:hover {
background-color: #3370FF33; background-color: #3370ff33;
} }
} }
@ -799,12 +800,13 @@ function closeExecutionDetail() {
} }
.chat-pc-popper { .chat-pc-popper {
background: linear-gradient(187.61deg, rgba(235, 241, 255, 0.5) 39.6%, rgba(231, 249, 255, 0.5) 94.3%), background:
#eef1f4 !important; linear-gradient(187.61deg, rgba(235, 241, 255, 0.5) 39.6%, rgba(231, 249, 255, 0.5) 94.3%),
#eef1f4 !important;
.el-menu { .el-menu {
background: transparent; background: transparent;
} }
.el-menu-item-group__title { .el-menu-item-group__title {
padding-bottom: 16px; padding-bottom: 16px;
font-weight: 500; font-weight: 500;
color: var(--app-text-color-secondary); color: var(--app-text-color-secondary);