feat: execution detail

This commit is contained in:
wangdan-fit2cloud 2025-07-04 16:29:00 +08:00
parent 519e0dc60b
commit 005a6229fb
11 changed files with 125 additions and 207 deletions

View File

@ -1,56 +0,0 @@
<template>
<el-dialog
class="execution-details-dialog responsive-dialog"
:title="$t('chat.executionDetails.title')"
v-model="dialogVisible"
destroy-on-close
append-to-body
align-center
@click.stop
>
<ExecutionDetailContent :detail="detail" :type="type" />
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch, onBeforeUnmount } from 'vue'
import { cloneDeep } from 'lodash'
import ExecutionDetailContent from './component/ExecutionDetailContent.vue'
const props = defineProps<{
type?: string
}>()
const dialogVisible = ref(false)
const detail = ref<any[]>([])
watch(dialogVisible, (bool) => {
if (!bool) {
detail.value = []
}
})
const open = (data: any) => {
detail.value = cloneDeep(data)
dialogVisible.value = true
}
onBeforeUnmount(() => {
dialogVisible.value = false
})
defineExpose({ open })
</script>
<style lang="scss">
.execution-details-dialog {
.el-dialog__header {
padding-bottom: 16px;
}
.execution-details {
max-height: calc(100vh - 260px);
.arrow-icon {
transition: 0.2s;
}
}
}
</style>

View File

@ -1,60 +0,0 @@
<template>
<el-dialog
class="paragraph-source responsive-dialog"
:title="$t('chat.paragraphSource.title')"
v-model="dialogVisible"
destroy-on-close
append-to-body
align-center
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<div class="mb-8">
<ParagraphSourceContent :detail="detail"/>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch, onBeforeUnmount } from 'vue'
import { cloneDeep } from 'lodash'
import { arraySort } from '@/utils/array'
import ParagraphSourceContent from './component/ParagraphSourceContent.vue'
const emit = defineEmits(['refresh'])
const dialogVisible = ref(false)
const detail = ref<any>({})
watch(dialogVisible, (bool) => {
if (!bool) {
detail.value = {}
}
})
const open = (data: any, id?: string) => {
detail.value = cloneDeep(data)
detail.value.paragraph_list = id
? detail.value.paragraph_list.filter((v: any) => v.knowledge_id === id)
: detail.value.paragraph_list
detail.value.paragraph_list = arraySort(detail.value.paragraph_list, 'similarity', true)
dialogVisible.value = true
}
onBeforeUnmount(() => {
dialogVisible.value = false
})
defineExpose({ open })
</script>
<style lang="scss">
.paragraph-source {
padding: 0;
.el-dialog__header {
padding: 24px 24px 0 24px;
}
.el-dialog__body {
padding: 8px !important;
}
.paragraph-source-height {
max-height: calc(100vh - 260px);
}
}
</style>

View File

@ -1,30 +0,0 @@
<template>
<el-scrollbar>
<div class="paragraph-source-height p-16 pb-0">
<el-form label-position="top">
<el-form-item :label="$t('chat.paragraphSource.question')">
<el-input :value="props.detail?.problem_text" disabled />
</el-form-item>
<el-form-item :label="$t('chat.paragraphSource.optimizationQuestion')">
<el-input :value="props.detail?.padding_problem_text" disabled />
</el-form-item>
<el-form-item :label="$t('chat.KnowledgeSource.referenceParagraph')">
<div v-if="props.detail?.paragraph_list.length > 0" class="w-full">
<template v-for="(item, index) in props.detail.paragraph_list" :key="index">
<ParagraphCard :data="item" :content="item.content" :index="index" />
</template>
</div>
<span v-else> - </span>
</el-form-item>
</el-form>
</div>
</el-scrollbar>
</template>
<script setup lang="ts">
import ParagraphCard from '@/components/ai-chat/component/ParagraphCard.vue'
const props = defineProps<{
detail?: any
}>()
</script>

View File

@ -40,7 +40,7 @@
{{ $t('chat.tip.answerLoading') }} <span class="dotting"></span>
</p>
<!-- 知识来源 -->
<KnowledgeSource
<KnowledgeSourceComponent
:data="chatRecord"
:type="application.type"
:executionIsRightPanel="props.executionIsRightPanel"
@ -72,7 +72,7 @@
</div>
</template>
<script setup lang="ts">
import KnowledgeSource from '@/components/ai-chat/KnowledgeSource.vue'
import KnowledgeSourceComponent from '@/components/ai-chat/component/knowledge-source-component/index.vue'
import MdRenderer from '@/components/markdown/MdRenderer.vue'
import OperationButton from '@/components/ai-chat/component/operation-button/index.vue'
import { type chatType } from '@/api/type/application'

View File

@ -1,6 +1,6 @@
<template>
<el-scrollbar>
<div class="execution-details">
<div class="execution-details p-8">
<template
v-if="isWorkFlow(props.type)"
v-for="(item, index) in arraySort(props.detail ?? [], 'index')"
@ -692,14 +692,38 @@
</el-card>
</template>
<template v-else v-for="(item, index) in arraySort(props.detail ?? [], 'index')">
<div class="card-never border-r-6 mb-12">
<h5 class="p-8-12">
{{ '-' }}
</h5>
<div class="p-8-12 border-t-dashed lighter">
<div class="mb-8">
<span class="color-secondary"> {{ $t('chat.paragraphSource.question') }}:</span>
{{ item.question || '-' }}
</div>
<template v-if="item.message_list?.length > 0">
<p
class="mt-4 mb-4"
v-for="(content, contentIndex) in item.message_list"
:key="contentIndex"
>
<span class="color-secondary mr-4">{{ content.role }}:</span
><span>{{ content.content }}</span>
</p>
</template>
<template v-else> -</template>
</div>
</div>
</template>
</div>
</el-scrollbar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import ParagraphCard from '@/components/ai-chat/component/ParagraphCard.vue'
import ParagraphCard from '@/components/ai-chat/component/knowledge-source-component/ParagraphCard.vue'
import { arraySort } from '@/utils/array'
import { iconComponent } from '@/workflow/icons/utils'
import { WorkflowType } from '@/enums/application'
@ -716,6 +740,7 @@ const current = ref<number | string>('')
</script>
<style lang="scss" scoped>
.execution-details {
max-height: calc(100vh - 260px);
.arrow-icon {
transition: 0.2s;
}

View File

@ -1,15 +1,13 @@
<template>
<CardBox
shadow="never"
:title="data.title || '-'"
:title="index + 1 + '.' + data.title || '-'"
class="paragraph-source-card cursor mb-8 paragraph-source-card-height"
:class="data.is_active ? '' : 'disabled'"
:showIcon="false"
>
<template #icon>
<el-avatar class="mr-12 avatar-light" :size="22"> {{ index + 1 + '' }}</el-avatar>
</template>
<template #tag>
<div class="primary">
<div class="color-primary">
{{ score?.toFixed(3) || data.similarity?.toFixed(3) }}
</div>
</template>
@ -19,7 +17,7 @@
</el-scrollbar>
<template #footer>
<div class="footer-content flex-between">
<el-card shadow="never" style="--el-card-padding: 8px" class="w-full mb-12">
<el-text class="flex align-center item">
<img :src="getImgUrl(data?.document_name?.trim())" alt="" width="20" class="mr-4" />
@ -39,15 +37,14 @@
</span>
</template>
</el-text>
<div class="flex align-center item" style="line-height: 32px">
</el-card>
<div class="flex align-center border-t" style="padding: 12px 0 8px">
<el-avatar class="mr-8 avatar-blue" shape="square" :size="18">
<img src="@/assets/knowledge/icon_document.svg" style="width: 58%" alt="" />
</el-avatar>
<span class="ellipsis-1 break-all" :title="data?.knowledge_name">
{{ data?.knowledge_name }}</span
>
</div>
{{ data?.knowledge_name || '-' }}
</span>
</div>
</template>
</CardBox>
@ -86,30 +83,13 @@ const parsedMeta = computed(() => {
const meta = computed(() => (isMetaObject.value ? props.data.meta : parsedMeta.value))
</script>
<style lang="scss" scoped>
.paragraph-source-card {
.footer-content {
.item {
max-width: 50%;
}
}
}
.paragraph-source-card-height {
height: 260px;
height: 300px;
}
@media only screen and (max-width: 768px) {
.paragraph-source-card-height {
height: 285px;
}
.paragraph-source-card {
.footer-content {
display: block;
.item {
max-width: 100%;
}
}
}
}
// @media only screen and (max-width: 768px) {
// .paragraph-source-card-height {
// height: 285px;
// }
// }
</style>

View File

@ -0,0 +1,25 @@
<template>
<el-scrollbar>
<div class="paragraph-source-height p-8">
<div v-if="props.detail?.paragraph_list.length > 0" class="w-full">
<template v-for="(item, index) in props.detail.paragraph_list" :key="index">
<ParagraphCard :data="item" :content="item.content" :index="index" />
</template>
</div>
<span v-else> - </span>
</div>
</el-scrollbar>
</template>
<script setup lang="ts">
import ParagraphCard from '@/components/ai-chat/component/knowledge-source-component/ParagraphCard.vue'
const props = defineProps<{
detail?: any
}>()
</script>
<style lang="scss" scoped>
.paragraph-source-height {
max-height: calc(100vh - 260px);
}
</style>

View File

@ -13,10 +13,10 @@
<el-row :gutter="8" v-if="uniqueParagraphList?.length">
<template v-for="(item, index) in uniqueParagraphList" :key="index">
<el-col :span="12" class="mb-8">
<el-card shadow="never" class="file-List-card" data-width="40">
<el-card shadow="never" style="--el-card-padding: 8px">
<div class="flex-between">
<div class="flex">
<img :src="getImgUrl(item && item?.document_name)" alt="" width="20" />
<div class="flex align-center">
<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">
<p>{{ item && item?.document_name }}</p>
</div>
@ -24,7 +24,7 @@
<a
:href="getNormalizedUrl(item?.source_url)"
target="_blank"
class="ellipsis"
class="ellipsis-1"
:title="item?.document_name?.trim()"
>
<span :title="item?.document_name?.trim()">{{ item?.document_name }}</span>
@ -60,18 +60,31 @@
{{ $t('chat.executionDetails.title') }}</el-button
>
</div>
<!-- 知识库引用 dialog -->
<ParagraphSourceDialog ref="ParagraphSourceDialogRef" />
<!-- 执行详情 dialog -->
<ExecutionDetailDialog ref="ExecutionDetailDialogRef" :type="type" />
<!-- 知识库引用/执行详情 dialog -->
<el-dialog
class="chat-source-dialog"
:title="dialogTitle"
v-model="dialogVisible"
destroy-on-close
append-to-body
align-center
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<div class="mb-8">
<component :is="currentComponent" :detail="currentChatDetail" :type="type"></component>
</div>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import ParagraphSourceDialog from './ParagraphSourceDialog.vue'
import ExecutionDetailDialog from './ExecutionDetailDialog.vue'
import { isWorkFlow } from '@/utils/application'
import { computed, ref, shallowRef } from 'vue'
import { cloneDeep } from 'lodash'
import ExecutionDetailContent from './ExecutionDetailContent.vue'
import ParagraphSourceContent from './ParagraphSourceContent.vue'
import { arraySort } from '@/utils/array'
import { getImgUrl, getNormalizedUrl } from '@/utils/common'
import { t } from '@/locales'
const props = defineProps({
data: {
type: Object,
@ -89,21 +102,34 @@ const props = defineProps({
const emit = defineEmits(['openExecutionDetail', 'openParagraph'])
const ParagraphSourceDialogRef = ref()
const ExecutionDetailDialogRef = ref()
const dialogVisible = ref(false)
const dialogTitle = ref('')
const currentComponent = shallowRef<any>(null)
const currentChatDetail = ref<any>(null)
function openParagraph(row: any, id?: string) {
dialogTitle.value = t('chat.KnowledgeSource.title')
const obj = cloneDeep(row)
obj.paragraph_list = id
? obj.paragraph_list.filter((v: any) => v.knowledge_id === id)
: obj.paragraph_list
obj.paragraph_list = arraySort(obj.paragraph_list, 'similarity', true)
if (props.executionIsRightPanel) {
emit('openParagraph')
return
}
ParagraphSourceDialogRef.value.open(row, id)
currentComponent.value = ParagraphSourceContent
currentChatDetail.value = obj
dialogVisible.value = true
}
function openExecutionDetail(row: any) {
dialogTitle.value = t('chat.executionDetails.title')
if (props.executionIsRightPanel) {
emit('openExecutionDetail')
return
}
ExecutionDetailDialogRef.value.open(row)
currentComponent.value = ExecutionDetailContent
currentChatDetail.value = row
dialogVisible.value = true
}
const uniqueParagraphList = computed(() => {
const seen = new Set()

View File

@ -34,7 +34,7 @@
</div>
<div class="card-footer flex-between" v-if="$slots.footer || $slots.mouseEnter">
<div>
<div style="flex: 1">
<slot name="footer"></slot>
</div>
<div @mouseenter="subHoveredEnter">

View File

@ -214,3 +214,11 @@
.auto-tooltip-popper {
max-width: 500px;
}
// 带滚动条dialog
.chat-source-dialog {
padding: 16px !important;
.el-dialog__header {
padding: 4px 16px 12px 12px;
}
}

View File

@ -328,8 +328,8 @@ import { useRouter } from 'vue-router'
import ResetPassword from '@/layout/layout-header/avatar/ResetPassword.vue'
import { t } from '@/locales'
import type { ResetCurrentUserPasswordRequest } from '@/api/type/user'
import ExecutionDetailContent from '@/components/ai-chat/component/ExecutionDetailContent.vue'
import ParagraphSourceContent from '@/components/ai-chat/component/ParagraphSourceContent.vue'
import ExecutionDetailContent from '@/components/ai-chat/component/knowledge-source-component/ExecutionDetailContent.vue'
import ParagraphSourceContent from '@/components/ai-chat/component/knowledge-source-component/ParagraphSourceContent.vue'
import { cloneDeep } from 'lodash'
useResize()