11<script setup lang="ts">
2- import { reactive , ref , useTemplateRef } from ' vue'
2+ import { computed , nextTick , reactive , ref , useTemplateRef } from ' vue'
33import type { FormInstance } from ' element-plus'
44import type { ResourceDetailPageProps } from ' @/layout/ResourceDetailLayout.vue'
5- import { KNOWLEDGE_SEARCH_MODE , KNOWLEDGE_TYPE } from ' @/api/enums'
5+ import { DOCUMENT_RESOURCE_TYPE , KNOWLEDGE_SEARCH_MODE } from ' @/api/enums'
66import { KNOWLEDGE_SEARCH_MODE_OPTIONS } from ' @/constants/knowledge'
7- import type { KnowledgeHitTestPayload , KnowledgeHitTestResult } from ' @/api/types'
7+ import type { KnowledgeHitTestPayload , KnowledgeHitTestResult , ParagraphItem } from ' @/api/types'
88import { useKnowledgeDetailContext } from ' @/views/knowledge-detail/context'
9+ import ParagraphDialog from ' @/views/document-detail/paragraph-dialog/ParagraphDialog.vue'
910import emptyHitTestImage from ' @/assets/empty/hit-test.png'
11+ import fileImageIcon from ' @/assets/file-type/file-image-icon.svg'
1012import HitTestInput from ' ./components/HitTestInput.vue'
13+ import { getFileUrl } from ' @/utils/common'
14+ import { getFileIconUrl } from ' @/utils/icon'
1115
1216defineOptions ({ name: ' HitTestView' })
1317defineProps <ResourceDetailPageProps >()
@@ -30,10 +34,31 @@ function changeSearchMode() {
3034}
3135
3236// 仅展示本次提交的查询快照,失败保留输入和检索参数供重试。
33- const resultParagraphs = ref <KnowledgeHitTestResult []>([])
37+ const hitTestResults = ref <KnowledgeHitTestResult []>([])
38+ const resultParagraphs = computed (() =>
39+ hitTestResults .value .map ((paragraph ) => ({
40+ ... paragraph ,
41+ isImgaeDoc: Boolean (paragraph .document_image_id ) && paragraph .document_resource_type === DOCUMENT_RESOURCE_TYPE .IMAGE ,
42+ })),
43+ )
3444const hasTestResult = ref (false )
3545const submittedQuery = ref <Required <Pick <KnowledgeHitTestPayload , ' query_text' | ' image_list' >>>({ query_text: ' ' , image_list: [] })
3646
47+ // 点击召回分段后挂载详情弹窗,保存正文时同步当前结果。
48+ const selectedParagraph = ref <KnowledgeHitTestResult >()
49+ const paragraphDialogRef = useTemplateRef <InstanceType <typeof ParagraphDialog >>(' paragraphDialogRef' )
50+
51+ async function handleOpenParagraph(paragraph : KnowledgeHitTestResult ) {
52+ if (loading .value || selectedParagraph .value ) return
53+ selectedParagraph .value = paragraph
54+ await nextTick ()
55+ paragraphDialogRef .value ?.open (paragraph )
56+ }
57+
58+ function handleParagraphSuccess(paragraph : ParagraphItem ) {
59+ hitTestResults .value = hitTestResults .value .map ((result ) => (result .id === paragraph .id ? { ... result , ... paragraph } : result ))
60+ }
61+
3762function testKnowledge(query : Required <Pick <KnowledgeHitTestPayload , ' query_text' | ' image_list' >>) {
3863 if (loading .value ) return
3964 searchFormRef .value ?.validate ((valid ) => {
@@ -47,7 +72,7 @@ function testKnowledge(query: Required<Pick<KnowledgeHitTestPayload, 'query_text
4772 return resourceApi .knowledge
4873 .postKnowledgeHitTest (knowledgeId .value , payload )
4974 .then ((paragraphs ) => {
50- resultParagraphs .value = paragraphs
75+ hitTestResults .value = paragraphs
5176 submittedQuery .value = query
5277 hasTestResult .value = true
5378 })
@@ -79,7 +104,7 @@ function testKnowledge(query: Required<Pick<KnowledgeHitTestPayload, 'query_text
79104 <el-radio-group v-model =" searchForm.search_mode" @change =" changeSearchMode" class =" space-y-2" >
80105 <template v-for =" searchMode in KNOWLEDGE_SEARCH_MODE_OPTIONS " :key =" searchMode .value " >
81106 <el-card shadow =" never" class =" w-full!" :class =" { 'border-primary!': searchForm.search_mode === searchMode.value }" >
82- <el-radio :value =" searchMode.value" :disabled = " loading " class =" mk-card-radio" >
107+ <el-radio :value =" searchMode.value" class =" mk-card-radio" >
83108 <h6 >{{ searchMode.label }}</h6 >
84109 <span class =" block text-sm text-N600" >{{ searchMode.description }}</span >
85110 </el-radio >
@@ -128,7 +153,6 @@ function testKnowledge(query: Required<Pick<KnowledgeHitTestPayload, 'query_text
128153 :file-count-limit =" knowledge ?.file_count_limit "
129154 :file-size-limit =" knowledge ?.file_size_limit "
130155 :loading =" loading "
131- :allow-images =" knowledge ?.type === KNOWLEDGE_TYPE .BASE "
132156 @submit =" testKnowledge "
133157 />
134158 </el-scrollbar >
@@ -140,37 +164,47 @@ function testKnowledge(query: Required<Pick<KnowledgeHitTestPayload, 'query_text
140164 </div >
141165 <template v-else >
142166 <section class =" mb-6" >
143- <h4 class =" mb-4 " >本次查询</h4 >
167+ <h4 class =" mb-2 " >本次查询</h4 >
144168 <div v-if =" submittedQuery.image_list.length" class =" flex flex-wrap gap-2" >
145169 <template v-for =" image in submittedQuery .image_list " :key =" image .file_id " >
146170 <!-- 预览本次查询图片 -->
147171 <el-image :src =" image.url" :alt =" image.name" fit =" cover" class =" size-12" />
148172 </template >
149173 </div >
150- <p v-if =" submittedQuery.query_text" class =" mt-3 whitespace-pre-wrap break-words" >{{ submittedQuery.query_text }}</p >
174+ <p v-if =" submittedQuery.query_text" class =" mt-2 whitespace-pre-wrap break-words" >{{ submittedQuery.query_text }}</p >
151175 </section >
152- <h4 class =" mb-4 " >查询结果</h4 >
176+ <h4 class =" mb-3 " >查询结果</h4 >
153177 <div v-if =" !resultParagraphs.length" class =" flex-center flex-1" >
154178 <MkEmpty type="search" description="未召回相关知识,请调整检索参数内容" />
155179 </div >
156180 <div v-else class =" space-y-3" >
157181 <template v-for =" (paragraph , paragraphIndex ) in resultParagraphs " :key =" paragraph .id " >
158- <MkSourceCard :title =" paragraph .title || ' -' " >
159- <template #icon >
160- <span >
161- <span >{{ `#${paragraphIndex + 1}` }}</span >
182+ <!-- 查看召回分段详情 -->
183+ <MkSourceCard :title =" paragraph .title || ' -' " class="cursor-pointer" @click =" handleOpenParagraph (paragraph )" >
184+ <template #title =" { title } " >
185+ <span class =" flex-align-center min-w-0 gap-2" >
186+ <h6 class =" shrink-0" >{{ `#${paragraphIndex + 1}` }}</h6 >
162187 <el-divider direction =" vertical" />
188+ <h6 class =" min-w-0 truncate" :title =" title" >{{ title }}</h6 >
163189 </span >
164190 </template >
165191 <template #tag >
166- <span class =" shrink-0 text-primary" >{{ paragraph.similarity.toFixed(3) }}</span >
192+ <el-tag size =" small" type =" info" >{{ paragraph.isImgaeDoc ? '图片向量' : '文本向量' }}</el-tag >
193+ <h6 class =" shrink-0 text-primary" >{{ paragraph.similarity.toFixed(3) }}</h6 >
167194 </template >
195+ <!-- 图片随卡片点击打开分段详情 -->
196+ <el-image
197+ v-if =" paragraph.isImgaeDoc && paragraph.document_image_id"
198+ :src =" getFileUrl(paragraph.document_image_id)"
199+ :alt =" paragraph.title || ''"
200+ fit =" contain"
201+ class =" mb-2"
202+ />
203+ <MdPreview :model-value =" paragraph .content " no-img-zoom-in />
168204
169- <el-scrollbar max-height =" 280" >
170- <MdPreview :model-value =" paragraph .content " />
171- </el-scrollbar >
172205 <template #footer >
173- <div class =" text-sm text-N600" >
206+ <div class =" flex-align-center min-w-0 gap-2 text-sm text-N600" >
207+ <img :src =" paragraph.isImgaeDoc ? fileImageIcon : getFileIconUrl(paragraph.document_name)" alt =" " class =" size-4 shrink-0" />
174208 <span class =" truncate" :title =" paragraph.document_name" >{{ paragraph.document_name }}</span >
175209 </div >
176210 </template >
@@ -181,4 +215,14 @@ function testKnowledge(query: Required<Pick<KnowledgeHitTestPayload, 'query_text
181215 </el-scrollbar >
182216 </section >
183217 </div >
218+ <ParagraphDialog
219+ v-if =" selectedParagraph "
220+ ref="paragraphDialogRef"
221+ title="分段详情"
222+ :knowledge-id =" selectedParagraph .knowledge_id "
223+ :document-id =" selectedParagraph .document_id "
224+ :source-file-id =" selectedParagraph .document_image_id || undefined "
225+ @success =" handleParagraphSuccess "
226+ @closed =" selectedParagraph = undefined "
227+ />
184228</template >
0 commit comments