fix: Optimize small screen dialogue style

This commit is contained in:
wangdan-fit2cloud 2025-03-19 16:07:02 +08:00 committed by GitHub
parent 470105f895
commit 96562b9f16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 203 additions and 181 deletions

View File

@ -1,66 +1,71 @@
<template>
<div class="flex align-center mt-16" v-if="!isWorkFlow(props.type)">
<span class="mr-4 color-secondary">{{ $t('chat.KnowledgeSource.title') }}</span>
<el-divider direction="vertical" />
<el-button type="primary" class="mr-8" link @click="openParagraph(data)">
<AppIcon iconName="app-reference-outlined" class="mr-4"></AppIcon>
{{ $t('chat.KnowledgeSource.referenceParagraph') }}
{{ data.paragraph_list?.length || 0 }}</el-button
>
</div>
<div class="mt-8" v-if="!isWorkFlow(props.type)">
<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">
<div class="flex-between">
<div class="flex">
<img :src="getImgUrl(item && item?.document_name)" alt="" width="20" />
<div class="ml-4 ellipsis-1" :title="item?.document_name" v-if="!item.source_url">
<p>{{ item && item?.document_name }}</p>
</div>
<div class="ml-8" v-else>
<a
:href="getNormalizedUrl(item?.source_url)"
target="_blank"
class="ellipsis"
:title="item?.document_name?.trim()"
>
<span :title="item?.document_name?.trim()">{{ item?.document_name }}</span>
</a>
<div class="chat-knowledge-source">
<div class="flex align-center mt-16" v-if="!isWorkFlow(props.type)">
<span class="mr-4 color-secondary">{{ $t('chat.KnowledgeSource.title') }}</span>
<el-divider direction="vertical" />
<el-button type="primary" class="mr-8" link @click="openParagraph(data)">
<AppIcon iconName="app-reference-outlined" class="mr-4"></AppIcon>
{{ $t('chat.KnowledgeSource.referenceParagraph') }}
{{ data.paragraph_list?.length || 0 }}</el-button
>
</div>
<div class="mt-8" v-if="!isWorkFlow(props.type)">
<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">
<div class="flex-between">
<div class="flex">
<img :src="getImgUrl(item && item?.document_name)" alt="" width="20" />
<div class="ml-4 ellipsis-1" :title="item?.document_name" v-if="!item.source_url">
<p>{{ item && item?.document_name }}</p>
</div>
<div class="ml-8" v-else>
<a
:href="getNormalizedUrl(item?.source_url)"
target="_blank"
class="ellipsis"
:title="item?.document_name?.trim()"
>
<span :title="item?.document_name?.trim()">{{ item?.document_name }}</span>
</a>
</div>
</div>
</div>
</div>
</el-card>
</el-col>
</template>
</el-row>
</div>
<div
class="border-t color-secondary flex-between mt-12"
style="padding-top: 12px; padding-bottom: 8px"
>
<div>
<span class="mr-8">
{{ $t('chat.KnowledgeSource.consume') }}: {{ data?.message_tokens + data?.answer_tokens }}
</span>
<span> {{ $t('chat.KnowledgeSource.consumeTime') }}: {{ data?.run_time?.toFixed(2) }} s</span>
</el-card>
</el-col>
</template>
</el-row>
</div>
<el-button
v-if="isWorkFlow(props.type)"
type="primary"
link
@click="openExecutionDetail(data.execution_details)"
>
<el-icon class="mr-4"><Document /></el-icon>
{{ $t('chat.executionDetails.title') }}</el-button
<div
class="execution-details border-t color-secondary flex-between mt-12"
style="padding-top: 12px; padding-bottom: 8px"
>
<div>
<span class="mr-8">
{{ $t('chat.KnowledgeSource.consume') }}: {{ data?.message_tokens + data?.answer_tokens }}
</span>
<span>
{{ $t('chat.KnowledgeSource.consumeTime') }}: {{ data?.run_time?.toFixed(2) }} s</span
>
</div>
<el-button
v-if="isWorkFlow(props.type)"
type="primary"
link
@click="openExecutionDetail(data.execution_details)"
style="padding: 0;"
>
<el-icon class="mr-4"><Document /></el-icon>
{{ $t('chat.executionDetails.title') }}</el-button
>
</div>
<!-- 知识库引用 dialog -->
<ParagraphSourceDialog ref="ParagraphSourceDialogRef" />
<!-- 执行详情 dialog -->
<ExecutionDetailDialog ref="ExecutionDetailDialogRef" />
</div>
<!-- 知识库引用 dialog -->
<ParagraphSourceDialog ref="ParagraphSourceDialogRef" />
<!-- 执行详情 dialog -->
<ExecutionDetailDialog ref="ExecutionDetailDialogRef" />
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
@ -107,13 +112,11 @@ const uniqueParagraphList = computed(() => {
})
</script>
<style lang="scss" scoped>
.source_dataset-button {
background: var(--app-text-color-light-1);
border: 1px solid #ffffff;
&:hover {
border: 1px solid var(--el-color-primary);
background: var(--el-color-primary-light-9);
color: var(--el-text-color-primary);
@media only screen and (max-width: 430px) {
.chat-knowledge-source {
.execution-details {
display: block;
}
}
}
</style>

View File

@ -34,9 +34,11 @@
{{ $t('chat.tip.answerLoading') }} <span class="dotting"></span>
</p>
<!-- 知识来源 -->
<div v-if="showSource(chatRecord) && index === chatRecord.answer_text_list.length - 1">
<KnowledgeSource :data="chatRecord" :type="application.type" />
</div>
<KnowledgeSource
:data="chatRecord"
:type="application.type"
v-if="showSource(chatRecord) && index === chatRecord.answer_text_list.length - 1"
/>
</el-card>
</div>
</template>

View File

@ -1,82 +1,88 @@
<template>
<div>
<div class="chat-operation-button flex-between">
<el-text type="info">
<span class="ml-4">{{ datetimeFormat(data.create_time) }}</span>
</el-text>
<div>
<!-- 语音播放 -->
<span v-if="tts">
<el-tooltip
effect="dark"
:content="$t('chat.operation.play')"
placement="top"
v-if="!audioPlayerStatus"
>
<el-button text :disabled="!data?.write_ed" @click="playAnswerText(data?.answer_text)">
<AppIcon iconName="app-video-play"></AppIcon>
</el-button>
</el-tooltip>
<el-tooltip v-else effect="dark" :content="$t('chat.operation.pause')" placement="top">
<el-button type="primary" text :disabled="!data?.write_ed" @click="pausePlayAnswerText()">
<AppIcon iconName="app-video-pause"></AppIcon>
</el-button>
</el-tooltip>
<el-divider direction="vertical" />
</span>
<span v-if="type == 'ai-chat' || type == 'log'">
<el-tooltip effect="dark" :content="$t('chat.operation.regeneration')" placement="top">
<el-button :disabled="chat_loading" text @click="regeneration">
<el-icon><RefreshRight /></el-icon>
</el-button>
</el-tooltip>
<el-divider direction="vertical" />
<el-tooltip effect="dark" :content="$t('common.copy')" placement="top">
<el-button text @click="copyClick(data?.answer_text.trim())">
<AppIcon iconName="app-copy"></AppIcon>
</el-button>
</el-tooltip>
<el-divider direction="vertical" />
<el-tooltip
effect="dark"
:content="$t('chat.operation.like')"
placement="top"
v-if="buttonData?.vote_status === '-1'"
>
<el-button text @click="voteHandle('0')" :disabled="loading">
<AppIcon iconName="app-like"></AppIcon>
</el-button>
</el-tooltip>
<el-tooltip
effect="dark"
:content="$t('chat.operation.cancelLike')"
placement="top"
v-if="buttonData?.vote_status === '0'"
>
<el-button text @click="voteHandle('-1')" :disabled="loading">
<AppIcon iconName="app-like-color"></AppIcon>
</el-button>
</el-tooltip>
<el-divider direction="vertical" v-if="buttonData?.vote_status === '-1'" />
<el-tooltip
effect="dark"
:content="$t('chat.operation.oppose')"
placement="top"
v-if="buttonData?.vote_status === '-1'"
>
<el-button text @click="voteHandle('1')" :disabled="loading">
<AppIcon iconName="app-oppose"></AppIcon>
</el-button>
</el-tooltip>
<el-tooltip
effect="dark"
:content="$t('chat.operation.cancelOppose')"
placement="top"
v-if="buttonData?.vote_status === '1'"
>
<el-button text @click="voteHandle('-1')" :disabled="loading">
<AppIcon iconName="app-oppose-color"></AppIcon>
</el-button>
</el-tooltip>
</span>
</div>
<!-- 先渲染不然不能播放 -->
<audio ref="audioPlayer" v-for="item in audioList" :key="item" controls hidden="hidden"></audio>
</div>
<div>
<!-- 语音播放 -->
<span v-if="tts">
<el-tooltip effect="dark" :content="$t('chat.operation.play')" placement="top" v-if="!audioPlayerStatus">
<el-button text :disabled="!data?.write_ed" @click="playAnswerText(data?.answer_text)">
<AppIcon iconName="app-video-play"></AppIcon>
</el-button>
</el-tooltip>
<el-tooltip v-else effect="dark" :content="$t('chat.operation.pause')" placement="top">
<el-button type="primary" text :disabled="!data?.write_ed" @click="pausePlayAnswerText()">
<AppIcon iconName="app-video-pause"></AppIcon>
</el-button>
</el-tooltip>
<el-divider direction="vertical" />
</span>
<span v-if="type == 'ai-chat' || type == 'log'">
<el-tooltip effect="dark" :content="$t('chat.operation.regeneration')" placement="top">
<el-button :disabled="chat_loading" text @click="regeneration">
<el-icon><RefreshRight /></el-icon>
</el-button>
</el-tooltip>
<el-divider direction="vertical" />
<el-tooltip effect="dark" :content="$t('common.copy')" placement="top">
<el-button text @click="copyClick(data?.answer_text.trim())">
<AppIcon iconName="app-copy"></AppIcon>
</el-button>
</el-tooltip>
<el-divider direction="vertical" />
<el-tooltip
effect="dark"
:content="$t('chat.operation.like')"
placement="top"
v-if="buttonData?.vote_status === '-1'"
>
<el-button text @click="voteHandle('0')" :disabled="loading">
<AppIcon iconName="app-like"></AppIcon>
</el-button>
</el-tooltip>
<el-tooltip
effect="dark"
:content="$t('chat.operation.cancelLike')"
placement="top"
v-if="buttonData?.vote_status === '0'"
>
<el-button text @click="voteHandle('-1')" :disabled="loading">
<AppIcon iconName="app-like-color"></AppIcon>
</el-button>
</el-tooltip>
<el-divider direction="vertical" v-if="buttonData?.vote_status === '-1'" />
<el-tooltip
effect="dark"
:content="$t('chat.operation.oppose')"
placement="top"
v-if="buttonData?.vote_status === '-1'"
>
<el-button text @click="voteHandle('1')" :disabled="loading">
<AppIcon iconName="app-oppose"></AppIcon>
</el-button>
</el-tooltip>
<el-tooltip
effect="dark"
:content="$t('chat.operation.cancelOppose')"
placement="top"
v-if="buttonData?.vote_status === '1'"
>
<el-button text @click="voteHandle('-1')" :disabled="loading">
<AppIcon iconName="app-oppose-color"></AppIcon>
</el-button>
</el-tooltip>
</span>
</div>
<!-- 先渲染不然不能播放 -->
<audio ref="audioPlayer" v-for="item in audioList" :key="item" controls hidden="hidden"></audio>
</template>
<script setup lang="ts">
import { nextTick, onMounted, ref } from 'vue'
@ -158,9 +164,7 @@ function markdownToPlainText(md: string) {
}
function removeFormRander(text: string) {
return text
.replace(/<form_rander>[\s\S]*?<\/form_rander>/g, '')
.trim()
return text.replace(/<form_rander>[\s\S]*?<\/form_rander>/g, '').trim()
}
const playAnswerText = (text: string) => {
@ -175,7 +179,7 @@ const playAnswerText = (text: string) => {
audioPlayerStatus.value = true
//
audioList.value = text.split(/(<audio[^>]*><\/audio>)/).filter((item) => item.trim().length > 0)
nextTick(()=>{
nextTick(() => {
// console.log(audioList.value, audioPlayer.value)
playAnswerTextPart()
})
@ -190,7 +194,8 @@ const playAnswerTextPart = () => {
}
if (audioList.value[currentAudioIndex.value].includes('<audio')) {
if (audioPlayer.value) {
audioPlayer.value[currentAudioIndex.value].src = audioList.value[currentAudioIndex.value].match(/src="([^"]*)"/)?.[1] || ''
audioPlayer.value[currentAudioIndex.value].src =
audioList.value[currentAudioIndex.value].match(/src="([^"]*)"/)?.[1] || ''
audioPlayer.value[currentAudioIndex.value].play() //
audioPlayer.value[currentAudioIndex.value].onended = () => {
currentAudioIndex.value += 1
@ -201,7 +206,10 @@ const playAnswerTextPart = () => {
if (audioList.value[currentAudioIndex.value] !== utterance.value?.text) {
window.speechSynthesis.cancel()
}
if (window.speechSynthesis.paused && audioList.value[currentAudioIndex.value] === utterance.value?.text) {
if (
window.speechSynthesis.paused &&
audioList.value[currentAudioIndex.value] === utterance.value?.text
) {
window.speechSynthesis.resume()
return
}
@ -225,7 +233,11 @@ const playAnswerTextPart = () => {
return
}
applicationApi
.postTextToSpeech((props.applicationId as string) || (id as string), { text: audioList.value[currentAudioIndex.value] }, loading)
.postTextToSpeech(
(props.applicationId as string) || (id as string),
{ text: audioList.value[currentAudioIndex.value] },
loading
)
.then(async (res: any) => {
if (res.type === 'application/json') {
const text = await res.text()
@ -284,9 +296,20 @@ onMounted(() => {
})
bus.emit('pause-autoplay')
//
if (props.tts && props.tts_autoplay && buttonData.value.write_ed && !buttonData.value.update_time) {
if (
props.tts &&
props.tts_autoplay &&
buttonData.value.write_ed &&
!buttonData.value.update_time
) {
playAnswerText(buttonData.value.answer_text)
}
})
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
@media only screen and (max-width: 430px) {
.chat-operation-button {
display: block;
}
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="chat-operation-button">
<LogOperationButton
v-if="type === 'log'"
v-bind:data="chatRecord"
@ -10,37 +10,31 @@
:type="type"
/>
<div class="flex-between mt-8" v-else>
<div>
<el-button
type="primary"
v-if="chatRecord.is_stop && !chatRecord.write_ed"
@click="startChat(chatRecord)"
link
>{{ $t('chat.operation.continue') }}
</el-button>
<el-button
type="primary"
v-else-if="!chatRecord.write_ed"
@click="stopChat(chatRecord)"
link
>{{ $t('chat.operation.stopChat') }}
</el-button>
</div>
</div>
<div v-if="chatRecord.write_ed && 500 != chatRecord.status" class="flex-between">
<ChatOperationButton
:tts="application.tts_model_enable"
:tts_type="application.tts_type"
:tts_autoplay="application.tts_autoplay"
:data="chatRecord"
:type="type"
:applicationId="application.id"
:chatId="chatRecord.chat_id"
:chat_loading="loading"
@regeneration="regenerationChart(chatRecord)"
/>
<div class="mt-8" v-else>
<el-button
type="primary"
v-if="chatRecord.is_stop && !chatRecord.write_ed"
@click="startChat(chatRecord)"
link
>{{ $t('chat.operation.continue') }}
</el-button>
<el-button type="primary" v-else-if="!chatRecord.write_ed" @click="stopChat(chatRecord)" link
>{{ $t('chat.operation.stopChat') }}
</el-button>
</div>
<ChatOperationButton
v-if="chatRecord.write_ed && 500 != chatRecord.status"
:tts="application.tts_model_enable"
:tts_type="application.tts_type"
:tts_autoplay="application.tts_autoplay"
:data="chatRecord"
:type="type"
:applicationId="application.id"
:chatId="chatRecord.chat_id"
:chat_loading="loading"
@regeneration="regenerationChart(chatRecord)"
/>
</div>
</template>
<script setup lang="ts">