Add File
This commit is contained in:
174
frontend/src/views/chat/answer/BaseAnswer.vue
Normal file
174
frontend/src/views/chat/answer/BaseAnswer.vue
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { type ChatMessage } from '@/api/chat.ts'
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import MdComponent from '@/views/chat/component/MdComponent.vue'
|
||||||
|
import icon_up_outlined from '@/assets/svg/icon_up_outlined.svg'
|
||||||
|
import icon_down_outlined from '@/assets/svg/icon_down_outlined.svg'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
message: ChatMessage
|
||||||
|
loading?: boolean
|
||||||
|
reasoningName:
|
||||||
|
| 'sql_answer'
|
||||||
|
| 'chart_answer'
|
||||||
|
| 'analysis_thinking'
|
||||||
|
| 'predict'
|
||||||
|
| Array<'sql_answer' | 'chart_answer' | 'analysis_thinking' | 'predict'>
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
loading: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const show = ref<boolean>(false)
|
||||||
|
|
||||||
|
const reasoningContent = computed<Array<string>>(() => {
|
||||||
|
const names: Array<'sql_answer' | 'chart_answer' | 'analysis_thinking' | 'predict'> = []
|
||||||
|
if (typeof props.reasoningName === 'string') {
|
||||||
|
names.push(props.reasoningName)
|
||||||
|
} else {
|
||||||
|
props.reasoningName.forEach((item) => {
|
||||||
|
names.push(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const result: Array<string> = []
|
||||||
|
names.forEach((item) => {
|
||||||
|
if (props.message?.record) {
|
||||||
|
if (props.message?.record[item]) {
|
||||||
|
result.push(props.message?.record[item] ?? '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
const hasReasoning = computed<boolean>(() => {
|
||||||
|
if (reasoningContent.value.length > 0) {
|
||||||
|
for (let i = 0; i < reasoningContent.value.length; i++) {
|
||||||
|
if (reasoningContent.value[i] && reasoningContent.value[i].trim() !== '') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
function clickShow() {
|
||||||
|
show.value = !show.value
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.message.isTyping) {
|
||||||
|
show.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="base-answer-block">
|
||||||
|
<el-button v-if="message.isTyping || hasReasoning" class="thinking-btn" @click="clickShow">
|
||||||
|
<div class="thinking-btn-inner">
|
||||||
|
<span v-if="message.isTyping">{{ t('qa.thinking') }}</span>
|
||||||
|
<span v-else>{{ t('qa.thinking_step') }}</span>
|
||||||
|
<span class="btn-icon">
|
||||||
|
<el-icon v-if="show">
|
||||||
|
<icon_up_outlined />
|
||||||
|
</el-icon>
|
||||||
|
<el-icon v-else>
|
||||||
|
<icon_down_outlined />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</el-button>
|
||||||
|
<div v-if="hasReasoning && show" class="reasoning-content">
|
||||||
|
<div v-for="(reason, _index) in reasoningContent" :key="_index" class="reasoning">
|
||||||
|
<MdComponent :message="reason" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="answer-container">
|
||||||
|
<slot></slot>
|
||||||
|
<el-button v-if="message.isTyping" style="min-width: unset" type="primary" link loading />
|
||||||
|
<slot name="tool"></slot>
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.base-answer-block {
|
||||||
|
.thinking-btn {
|
||||||
|
height: 32px;
|
||||||
|
padding: 5px 12px;
|
||||||
|
|
||||||
|
--ed-button-text-color: rgba(31, 35, 41, 1);
|
||||||
|
--ed-button-hover-text-color: var(--ed-button-text-color);
|
||||||
|
--ed-button-active-text-color: var(--ed-button-text-color);
|
||||||
|
--ed-button-bg-color: rgba(255, 255, 255, 1);
|
||||||
|
--ed-button-hover-bg-color: rgba(245, 246, 247, 1);
|
||||||
|
--ed-button-active-bg-color: rgba(239, 240, 241, 1);
|
||||||
|
--ed-button-border-color: rgba(217, 220, 223, 1);
|
||||||
|
--ed-button-hover-border-color: var(--ed-button-border-color);
|
||||||
|
--ed-button-active-border-color: var(--ed-button-border-color);
|
||||||
|
|
||||||
|
--ed-button-font-weight: 400;
|
||||||
|
|
||||||
|
.thinking-btn-inner {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
line-height: 22px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.btn-icon {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reasoning-content {
|
||||||
|
margin-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-left: 9px;
|
||||||
|
border-left: 1px solid rgba(31, 35, 41, 0.15);
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.reasoning {
|
||||||
|
width: 100%;
|
||||||
|
line-height: 22px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: rgba(143, 149, 158, 1) !important;
|
||||||
|
|
||||||
|
.markdown-body {
|
||||||
|
color: rgba(143, 149, 158, 1) !important;
|
||||||
|
line-height: 22px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid rgba(31, 35, 41, 0.15);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-bottom: unset;
|
||||||
|
border-bottom: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-container {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: rgba(31, 35, 41, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user