mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
feat: add chat log step detail
This commit is contained in:
@@ -300,8 +300,10 @@ export class ChatLogHistoryItem {
|
||||
duration?: number | undefined
|
||||
total_tokens?: number | undefined
|
||||
operate?: string | undefined
|
||||
operate_key?: string | undefined
|
||||
local_operation?: boolean | undefined
|
||||
error?: boolean | undefined
|
||||
message?: any
|
||||
|
||||
constructor()
|
||||
constructor(
|
||||
@@ -311,7 +313,8 @@ export class ChatLogHistoryItem {
|
||||
total_tokens: number | undefined,
|
||||
operate: string | undefined,
|
||||
local_operation: boolean | undefined,
|
||||
error: boolean | undefined
|
||||
error: boolean | undefined,
|
||||
message: any | undefined
|
||||
)
|
||||
constructor(
|
||||
start_time?: Date | string,
|
||||
@@ -320,15 +323,18 @@ export class ChatLogHistoryItem {
|
||||
total_tokens?: number | undefined,
|
||||
operate?: string | undefined,
|
||||
local_operation?: boolean | undefined,
|
||||
error?: boolean | undefined
|
||||
error?: boolean | undefined,
|
||||
message?: any | undefined
|
||||
) {
|
||||
this.start_time = getDate(start_time)
|
||||
this.finish_time = getDate(finish_time)
|
||||
this.duration = duration
|
||||
this.total_tokens = total_tokens
|
||||
this.operate_key = operate
|
||||
this.operate = t('chat.log.' + operate)
|
||||
this.local_operation = !!local_operation
|
||||
this.error = !!error
|
||||
this.message = message
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +379,8 @@ const toChatLogHistoryItem = (data?: any): any | undefined => {
|
||||
data.total_tokens,
|
||||
data.operate,
|
||||
data.local_operation,
|
||||
data.error
|
||||
data.error,
|
||||
data.message
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -767,7 +767,16 @@
|
||||
"FILTER_CUSTOM_PROMPT": "Match Custom Prompts",
|
||||
"EXECUTE_SQL": "Execute SQL",
|
||||
"GENERATE_PICTURE": "Generate Picture"
|
||||
}
|
||||
},
|
||||
"log_system": "Conversation Template",
|
||||
"log_question": "Current Question",
|
||||
"log_answer": "AI Response",
|
||||
"log_history": "History",
|
||||
"find_term_title": "Matched {0} terms",
|
||||
"find_sql_sample_title": "Matched {0} SQL examples",
|
||||
"find_custom_prompt_title": "Matched {0} custom prompts",
|
||||
"query_count_title": "Found {0} data entries",
|
||||
"generate_picture_success": "Image generated"
|
||||
},
|
||||
"about": {
|
||||
"title": "About",
|
||||
|
||||
@@ -767,7 +767,16 @@
|
||||
"FILTER_CUSTOM_PROMPT": "사용자 정의 프롬프트 매칭",
|
||||
"EXECUTE_SQL": "SQL 실행",
|
||||
"GENERATE_PICTURE": "이미지 생성"
|
||||
}
|
||||
},
|
||||
"log_system": "대화 템플릿",
|
||||
"log_question": "현재 질문",
|
||||
"log_answer": "AI 응답",
|
||||
"log_history": "기록",
|
||||
"find_term_title": "{0}개의 용어 매칭됨",
|
||||
"find_sql_sample_title": "{0}개의 SQL 예시 매칭됨",
|
||||
"find_custom_prompt_title": "{0}개의 사용자 정의 프롬프트 매칭됨",
|
||||
"query_count_title": "{0}개의 데이터 항목 발견됨",
|
||||
"generate_picture_success": "이미지가 생성되었습니다"
|
||||
},
|
||||
"about": {
|
||||
"title": "정보",
|
||||
|
||||
@@ -767,7 +767,16 @@
|
||||
"FILTER_CUSTOM_PROMPT": "匹配自定义提示词",
|
||||
"EXECUTE_SQL": "执行 SQL",
|
||||
"GENERATE_PICTURE": "生成图片"
|
||||
}
|
||||
},
|
||||
"log_system": "对话模板",
|
||||
"log_question": "本次提问",
|
||||
"log_answer": "AI 回答",
|
||||
"log_history": "历史记录",
|
||||
"find_term_title":"匹配到 {0} 个术语",
|
||||
"find_sql_sample_title":"匹配到 {0} 个SQL示例",
|
||||
"find_custom_prompt_title":"匹配到 {0} 个自定义提示词",
|
||||
"query_count_title":"查询到 {0} 条数据",
|
||||
"generate_picture_success":"已生成图片"
|
||||
},
|
||||
"about": {
|
||||
"title": "关于",
|
||||
|
||||
@@ -8,6 +8,13 @@ import icon_alarm_clock_colorful from '@/assets/svg/icon_alarm-clock_colorful.sv
|
||||
import { chatApi, type ChatLogHistory } from '@/api/chat.ts'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { debounce } from 'lodash-es'
|
||||
import LogTerm from './execution-component/LogTerm.vue'
|
||||
import LogSQLSample from './execution-component/LogSQLSample.vue'
|
||||
import LogCustomPrompt from './execution-component/LogCustomPrompt.vue'
|
||||
import LogDataQuery from './execution-component/LogDataQuery.vue'
|
||||
import LogChooseTable from './execution-component/LogChooseTable.vue'
|
||||
import LogGeneratePicture from './execution-component/LogGeneratePicture.vue'
|
||||
import LogWithAi from '@/views/chat/execution-component/LogWithAi.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
const logHistory = ref<ChatLogHistory>({})
|
||||
@@ -76,13 +83,8 @@ defineExpose({
|
||||
<div class="title">{{ t('parameter.execution_details') }}</div>
|
||||
|
||||
<div class="list">
|
||||
<div
|
||||
v-for="(ele, index) in logHistory.steps"
|
||||
:key="ele.duration"
|
||||
class="list-item"
|
||||
@click="handleExpand(index)"
|
||||
>
|
||||
<div class="header">
|
||||
<div v-for="(ele, index) in logHistory.steps" :key="ele.duration" class="list-item">
|
||||
<div class="header" @click="handleExpand(index)">
|
||||
<div class="name">
|
||||
<el-icon class="shrink" :class="expandIds.includes(index) && 'expand'" size="10">
|
||||
<icon_expand_right_filled></icon_expand_right_filled>
|
||||
@@ -104,7 +106,15 @@ defineExpose({
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content"></div>
|
||||
<div v-if="expandIds.includes(index)" class="content">
|
||||
<LogTerm v-if="ele.operate_key === 'FILTER_TERMS'" :item="ele" />
|
||||
<LogSQLSample v-else-if="ele.operate_key === 'FILTER_SQL_EXAMPLE'" :item="ele" />
|
||||
<LogCustomPrompt v-else-if="ele.operate_key === 'FILTER_CUSTOM_PROMPT'" :item="ele" />
|
||||
<LogChooseTable v-else-if="ele.operate_key === 'CHOOSE_TABLE'" :item="ele" />
|
||||
<LogDataQuery v-else-if="ele.operate_key === 'EXECUTE_SQL'" :item="ele" />
|
||||
<LogGeneratePicture v-else-if="ele.operate_key === 'GENERATE_PICTURE'" :item="ele" />
|
||||
<LogWithAi v-else :item="ele" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
@@ -175,13 +185,13 @@ defineExpose({
|
||||
}
|
||||
|
||||
100% {
|
||||
height: 504px;
|
||||
min-height: 54px;
|
||||
}
|
||||
}
|
||||
|
||||
&:has(.expand) {
|
||||
height: 504px;
|
||||
animation: expand 0.5s;
|
||||
min-height: 54px;
|
||||
//animation: expand 0.5s;
|
||||
}
|
||||
|
||||
.shrink {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div class="base-container"><slot></slot></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.base-container {
|
||||
background: #f5f6f7;
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<script setup lang="ts">
|
||||
import BaseContent from './BaseContent.vue'
|
||||
import { type ChatLogHistoryItem } from '@/api/chat.ts'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item?: ChatLogHistoryItem
|
||||
error?: string
|
||||
}>(),
|
||||
{
|
||||
item: undefined,
|
||||
error: '',
|
||||
}
|
||||
)
|
||||
|
||||
const schema = computed(() => {
|
||||
return (props.item?.message as string) ?? ''
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContent class="base-container">
|
||||
<template v-if="item.error">
|
||||
{{ error }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-dompurify-html="schema" class="inner-title"></div>
|
||||
</template>
|
||||
</BaseContent>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.inner-title {
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
vertical-align: middle;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.item-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
flex-wrap: nowrap;
|
||||
.inner-item {
|
||||
border: 1px solid #dee0e3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
background: #ffffff;
|
||||
|
||||
.inner-item-title {
|
||||
color: #1f2329;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.inner-item-description {
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,85 @@
|
||||
<script setup lang="ts">
|
||||
import BaseContent from './BaseContent.vue'
|
||||
import { type ChatLogHistoryItem } from '@/api/chat.ts'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item?: ChatLogHistoryItem
|
||||
error?: string
|
||||
}>(),
|
||||
{
|
||||
item: undefined,
|
||||
error: '',
|
||||
}
|
||||
)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const list = computed(() => {
|
||||
return (props.item?.message as Array<any>) ?? []
|
||||
})
|
||||
const title = computed(() => {
|
||||
return t('chat.find_custom_prompt_title', [list.value.length])
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContent class="base-container">
|
||||
<template v-if="item.error">
|
||||
{{ error }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="inner-title">{{ title }}</div>
|
||||
<div v-if="list.length > 0" style="margin-top: 8px" class="item-list">
|
||||
<div v-for="(ele, index) in list" :key="index" class="inner-item">
|
||||
<div v-dompurify-html="ele" class="inner-item-description" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BaseContent>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.inner-title {
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.item-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
flex-wrap: nowrap;
|
||||
.inner-item {
|
||||
border: 1px solid #dee0e3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
background: #ffffff;
|
||||
|
||||
.inner-item-title {
|
||||
color: #1f2329;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.inner-item-description {
|
||||
//color: #646a73;
|
||||
color: #1f2329;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
<script setup lang="ts">
|
||||
import BaseContent from './BaseContent.vue'
|
||||
import { type ChatLogHistoryItem } from '@/api/chat.ts'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item?: ChatLogHistoryItem
|
||||
error?: string
|
||||
}>(),
|
||||
{
|
||||
item: undefined,
|
||||
error: '',
|
||||
}
|
||||
)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const message = computed(() => {
|
||||
return props.item?.message ?? { count: 0 }
|
||||
})
|
||||
const title = computed(() => {
|
||||
return t('chat.query_count_title', [message.value.count])
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContent class="base-container">
|
||||
<template v-if="item.error">
|
||||
{{ error }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="inner-title">{{ title }}</div>
|
||||
</template>
|
||||
</BaseContent>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.inner-title {
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.item-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
flex-wrap: nowrap;
|
||||
.inner-item {
|
||||
border: 1px solid #dee0e3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
background: #ffffff;
|
||||
|
||||
.inner-item-title {
|
||||
color: #1f2329;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.inner-item-description {
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,79 @@
|
||||
<script setup lang="ts">
|
||||
import BaseContent from './BaseContent.vue'
|
||||
import { type ChatLogHistoryItem } from '@/api/chat.ts'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item?: ChatLogHistoryItem
|
||||
error?: string
|
||||
}>(),
|
||||
{
|
||||
item: undefined,
|
||||
error: '',
|
||||
}
|
||||
)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const message = computed(() => {
|
||||
return props.item?.message ?? ''
|
||||
})
|
||||
const title = computed(() => {
|
||||
return t('chat.generate_picture_success')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContent class="base-container">
|
||||
<template v-if="item.error">
|
||||
{{ error }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="inner-title">{{ title }}</div>
|
||||
<div v-if="message.length > 0" class="inner-title">{{ message }}</div>
|
||||
</template>
|
||||
</BaseContent>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.inner-title {
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.item-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
flex-wrap: nowrap;
|
||||
.inner-item {
|
||||
border: 1px solid #dee0e3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
background: #ffffff;
|
||||
|
||||
.inner-item-title {
|
||||
color: #1f2329;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.inner-item-description {
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,93 @@
|
||||
<script setup lang="ts">
|
||||
import BaseContent from './BaseContent.vue'
|
||||
import { type ChatLogHistoryItem } from '@/api/chat.ts'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import SQLComponent from '@/views/chat/component/SQLComponent.vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item?: ChatLogHistoryItem
|
||||
error?: string
|
||||
}>(),
|
||||
{
|
||||
item: undefined,
|
||||
error: '',
|
||||
}
|
||||
)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const list = computed(() => {
|
||||
return (props.item?.message as Array<any>) ?? []
|
||||
})
|
||||
const title = computed(() => {
|
||||
return t('chat.find_sql_sample_title', [list.value.length])
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContent class="base-container">
|
||||
<template v-if="item.error">
|
||||
{{ error }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="inner-title">{{ title }}</div>
|
||||
<div v-if="list.length > 0" style="margin-top: 8px" class="item-list">
|
||||
<div v-for="(ele, index) in list" :key="index" class="inner-item">
|
||||
<div class="inner-item-title">
|
||||
{{ ele.question }}
|
||||
</div>
|
||||
<div class="inner-item-description">
|
||||
<SQLComponent :sql="ele['suggestion-answer']" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BaseContent>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.inner-title {
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.item-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
flex-wrap: nowrap;
|
||||
.inner-item {
|
||||
border: 1px solid #dee0e3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
background: #ffffff;
|
||||
|
||||
.inner-item-title {
|
||||
color: #1f2329;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.inner-item-description {
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
|
||||
.hljs {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,89 @@
|
||||
<script setup lang="ts">
|
||||
import BaseContent from './BaseContent.vue'
|
||||
import { type ChatLogHistoryItem } from '@/api/chat.ts'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item?: ChatLogHistoryItem
|
||||
error?: string
|
||||
}>(),
|
||||
{
|
||||
item: undefined,
|
||||
error: '',
|
||||
}
|
||||
)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const list = computed(() => {
|
||||
return (props.item?.message as Array<any>) ?? []
|
||||
})
|
||||
const title = computed(() => {
|
||||
return t('chat.find_term_title', [list.value.length])
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContent class="base-container">
|
||||
<template v-if="item.error">
|
||||
{{ error }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="inner-title">{{ title }}</div>
|
||||
<div v-if="list.length > 0" style="margin-top: 8px" class="item-list">
|
||||
<div v-for="(ele, index) in list" :key="index" class="inner-item">
|
||||
<div>
|
||||
<div v-for="(word, index2) in ele.words" :key="index2" class="inner-item-title">
|
||||
{{ word }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-dompurify-html="ele.description" class="inner-item-description" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BaseContent>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.inner-title {
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.item-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
flex-wrap: nowrap;
|
||||
.inner-item {
|
||||
border: 1px solid #dee0e3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
background: #ffffff;
|
||||
|
||||
.inner-item-title {
|
||||
color: #1f2329;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.inner-item-description {
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,140 @@
|
||||
<script setup lang="ts">
|
||||
import BaseContent from './BaseContent.vue'
|
||||
import { type ChatLogHistoryItem } from '@/api/chat.ts'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import SQLComponent from '@/views/chat/component/SQLComponent.vue'
|
||||
import { find, findLastIndex } from 'lodash-es'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item?: ChatLogHistoryItem
|
||||
error?: string
|
||||
}>(),
|
||||
{
|
||||
item: undefined,
|
||||
error: '',
|
||||
}
|
||||
)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const data = computed(() => {
|
||||
return (props.item?.message as Array<any>) ?? []
|
||||
})
|
||||
|
||||
// 1. 获取 role 为 system 的一条记录
|
||||
const systemRecord = computed(() => find(data.value, { type: 'system' }))
|
||||
|
||||
// 2. 获取最后一个 role 为 human 的记录
|
||||
const lastHumanIndex = computed(() => findLastIndex(data.value, { type: 'human' }))
|
||||
const lastHumanRecord = computed(() => data.value[lastHumanIndex.value])
|
||||
|
||||
// 3. 获取最后一个 role 为 ai 且在当前对话提问后出现的记录
|
||||
// 使用原生 filter 和 pop
|
||||
const aiRecordsAfterHuman = computed(() =>
|
||||
data.value.slice(lastHumanIndex.value + 1).filter((item) => item.type === 'ai')
|
||||
)
|
||||
const lastAiAfterHuman = computed(() =>
|
||||
aiRecordsAfterHuman.value.length > 0
|
||||
? aiRecordsAfterHuman.value[aiRecordsAfterHuman.value.length - 1]
|
||||
: undefined
|
||||
)
|
||||
// 4. 获取除 system 以外,当前对话提问前的所有记录
|
||||
const recordsBeforeCurrentQuestion = computed(() =>
|
||||
data.value.slice(0, lastHumanIndex.value).filter((item) => item.type !== 'system')
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContent class="base-container">
|
||||
<template v-if="item.error">
|
||||
{{ error }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="item-list">
|
||||
<div class="inner-title">{{ t('chat.log_system') }}</div>
|
||||
<div class="inner-item">
|
||||
<div class="inner-item-title">
|
||||
{{ systemRecord.type }}
|
||||
</div>
|
||||
<div class="inner-item-description">
|
||||
<SQLComponent :sql="systemRecord.content" />
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="recordsBeforeCurrentQuestion.length > 0">
|
||||
<div class="inner-title">{{ t('chat.log_history') }}</div>
|
||||
<div class="inner-item">
|
||||
<div v-for="(ele, index) in recordsBeforeCurrentQuestion" :key="index">
|
||||
<div class="inner-item-title">
|
||||
{{ ele.type }}
|
||||
</div>
|
||||
<div class="inner-item-description">
|
||||
<SQLComponent :sql="ele.content" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="inner-title">{{ t('chat.log_question') }}</div>
|
||||
<div class="inner-item">
|
||||
<div class="inner-item-description">
|
||||
<SQLComponent :sql="lastHumanRecord.content" />
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="lastAiAfterHuman">
|
||||
<div class="inner-title">{{ t('chat.log_answer') }}</div>
|
||||
<div class="inner-item">
|
||||
<div class="inner-item-description">
|
||||
<SQLComponent :sql="lastAiAfterHuman.content" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</BaseContent>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.inner-title {
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.item-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
flex-wrap: nowrap;
|
||||
.inner-item {
|
||||
border: 1px solid #dee0e3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
background: #ffffff;
|
||||
|
||||
.inner-item-title {
|
||||
color: #1f2329;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.inner-item-description {
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
|
||||
.hljs {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user