feat: 统一聊天助手头像
- 工作流运行状态、智能体正式聊天与试运行展示助手头像 - 新增默认机器人头像及消息、空状态回退组件
This commit is contained in:
@@ -11,6 +11,7 @@ import type {
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import ChatThinkingBlock from '../chat-thinking/ChatThinkingBlock.vue';
|
||||
import ChatAssistantAvatar from './ChatAssistantAvatar.vue';
|
||||
import ChatDocumentAttachments from './ChatDocumentAttachments.vue';
|
||||
import ChatErrorNotice from './ChatErrorNotice.vue';
|
||||
import ChatImageAttachments from './ChatImageAttachments.vue';
|
||||
@@ -23,6 +24,7 @@ import ChatToolCard from './ChatToolCard.vue';
|
||||
const props = defineProps<{
|
||||
approvalLoading?: boolean;
|
||||
assistantActionsVisible?: boolean;
|
||||
assistantAvatar?: string;
|
||||
copyable?: boolean;
|
||||
copyAction?: (item: ChatTimelineMessageItem) => boolean | Promise<boolean>;
|
||||
documentLoader?: ChatDocumentLoader;
|
||||
@@ -56,6 +58,18 @@ const alignmentClass = computed(() => {
|
||||
return 'is-assistant';
|
||||
});
|
||||
|
||||
const showAssistantAvatar = computed(
|
||||
() =>
|
||||
messageItem.value?.role === 'assistant' && Boolean(props.assistantAvatar),
|
||||
);
|
||||
|
||||
const showStatusAvatar = computed(
|
||||
() =>
|
||||
props.item.type === 'status' &&
|
||||
props.item.presentation !== 'separator' &&
|
||||
Boolean(props.assistantAvatar),
|
||||
);
|
||||
|
||||
const variantCurrent = computed(() => {
|
||||
const item = messageItem.value;
|
||||
return Number(item?.selectedVariantIndex || item?.variantIndex || 1);
|
||||
@@ -158,68 +172,82 @@ function handleCopyAction() {
|
||||
<div class="chat-timeline-item" :class="alignmentClass">
|
||||
<div
|
||||
v-if="messageItem"
|
||||
class="chat-timeline-item__message"
|
||||
class="chat-timeline-item__message-row"
|
||||
:class="[
|
||||
`is-${messageItem.role}`,
|
||||
{ 'has-variant-navigator': showVariantNavigator },
|
||||
{
|
||||
'has-assistant-avatar': showAssistantAvatar,
|
||||
'has-variant-navigator': showVariantNavigator,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<ChatImageAttachments
|
||||
v-if="messageItem.images?.length"
|
||||
:items="messageItem.images"
|
||||
:image-loader="imageLoader"
|
||||
compact
|
||||
/>
|
||||
<ChatDocumentAttachments
|
||||
v-if="messageItem.documents?.length"
|
||||
:items="messageItem.documents"
|
||||
:document-loader="documentLoader"
|
||||
compact
|
||||
/>
|
||||
<div
|
||||
v-if="hasMessageBubble(messageItem)"
|
||||
class="chat-timeline-item__bubble"
|
||||
v-if="showAssistantAvatar"
|
||||
class="chat-timeline-item__assistant-avatar"
|
||||
>
|
||||
<ChatAssistantAvatar :src="assistantAvatar" />
|
||||
</div>
|
||||
<div
|
||||
class="chat-timeline-item__message"
|
||||
:class="`is-${messageItem.role}`"
|
||||
>
|
||||
<template v-for="part in getMessageParts(messageItem)" :key="part.id">
|
||||
<ChatThinkingBlock
|
||||
v-if="part.type === 'thinking'"
|
||||
:content="part.content"
|
||||
:expanded="resolveThinkingExpanded(part)"
|
||||
:status="part.status"
|
||||
class="chat-timeline-item__thinking"
|
||||
readonly
|
||||
@update:expanded="updateThinkingExpanded(part.id, $event)"
|
||||
<ChatImageAttachments
|
||||
v-if="messageItem.images?.length"
|
||||
:items="messageItem.images"
|
||||
:image-loader="imageLoader"
|
||||
compact
|
||||
/>
|
||||
<ChatDocumentAttachments
|
||||
v-if="messageItem.documents?.length"
|
||||
:items="messageItem.documents"
|
||||
:document-loader="documentLoader"
|
||||
compact
|
||||
/>
|
||||
<div
|
||||
v-if="hasMessageBubble(messageItem)"
|
||||
class="chat-timeline-item__bubble"
|
||||
:class="`is-${messageItem.role}`"
|
||||
>
|
||||
<template v-for="part in getMessageParts(messageItem)" :key="part.id">
|
||||
<ChatThinkingBlock
|
||||
v-if="part.type === 'thinking'"
|
||||
:content="part.content"
|
||||
:expanded="resolveThinkingExpanded(part)"
|
||||
:status="part.status"
|
||||
class="chat-timeline-item__thinking"
|
||||
readonly
|
||||
@update:expanded="updateThinkingExpanded(part.id, $event)"
|
||||
/>
|
||||
<ChatTextBlock
|
||||
v-else
|
||||
:content="part.content"
|
||||
:streaming="messageItem.status === 'streaming'"
|
||||
/>
|
||||
</template>
|
||||
<ChatKnowledgeCard
|
||||
v-if="messageItem.knowledgeItems?.length"
|
||||
:items="messageItem.knowledgeItems"
|
||||
/>
|
||||
<ChatTextBlock
|
||||
v-else
|
||||
:content="part.content"
|
||||
:streaming="messageItem.status === 'streaming'"
|
||||
/>
|
||||
</template>
|
||||
<ChatKnowledgeCard
|
||||
v-if="messageItem.knowledgeItems?.length"
|
||||
:items="messageItem.knowledgeItems"
|
||||
</div>
|
||||
<ChatMessageToolbar
|
||||
v-if="showToolbar && messageItem"
|
||||
:align="messageItem.role === 'user' ? 'end' : 'start'"
|
||||
:allow-copy="copyable"
|
||||
:allow-regenerate="regenerable"
|
||||
:copy-action="copyAction ? handleCopyAction : undefined"
|
||||
:disabled-variant-next="disabledVariantNext"
|
||||
:disabled-variant-previous="disabledVariantPrevious"
|
||||
:regenerate-disabled="regenerateDisabled"
|
||||
:show-variant-navigator="showVariantNavigator"
|
||||
:variant-current="variantCurrent"
|
||||
:variant-loading="variantLoading"
|
||||
:variant-total="variantTotal"
|
||||
@copy="emit('copyMessage', messageItem)"
|
||||
@regenerate="emit('regenerateMessage', messageItem)"
|
||||
@select-next-variant="emit('selectNextVariant', messageItem)"
|
||||
@select-previous-variant="emit('selectPreviousVariant', messageItem)"
|
||||
/>
|
||||
</div>
|
||||
<ChatMessageToolbar
|
||||
v-if="showToolbar && messageItem"
|
||||
:align="messageItem.role === 'user' ? 'end' : 'start'"
|
||||
:allow-copy="copyable"
|
||||
:allow-regenerate="regenerable"
|
||||
:copy-action="copyAction ? handleCopyAction : undefined"
|
||||
:disabled-variant-next="disabledVariantNext"
|
||||
:disabled-variant-previous="disabledVariantPrevious"
|
||||
:regenerate-disabled="regenerateDisabled"
|
||||
:show-variant-navigator="showVariantNavigator"
|
||||
:variant-current="variantCurrent"
|
||||
:variant-loading="variantLoading"
|
||||
:variant-total="variantTotal"
|
||||
@copy="emit('copyMessage', messageItem)"
|
||||
@regenerate="emit('regenerateMessage', messageItem)"
|
||||
@select-next-variant="emit('selectNextVariant', messageItem)"
|
||||
@select-previous-variant="emit('selectPreviousVariant', messageItem)"
|
||||
/>
|
||||
</div>
|
||||
<ChatToolCard
|
||||
v-else-if="item.type === 'tool'"
|
||||
@@ -232,7 +260,16 @@ function handleCopyAction() {
|
||||
v-else-if="item.type === 'knowledge'"
|
||||
:items="item.items"
|
||||
/>
|
||||
<ChatTimelineStatusRow v-else-if="item.type === 'status'" :item="item" />
|
||||
<div
|
||||
v-else-if="item.type === 'status'"
|
||||
class="chat-timeline-item__status-row"
|
||||
:class="{ 'has-assistant-avatar': showStatusAvatar }"
|
||||
>
|
||||
<div v-if="showStatusAvatar" class="chat-timeline-item__assistant-avatar">
|
||||
<ChatAssistantAvatar :src="assistantAvatar" />
|
||||
</div>
|
||||
<ChatTimelineStatusRow :item="item" />
|
||||
</div>
|
||||
<ChatErrorNotice
|
||||
v-else-if="item.type === 'error'"
|
||||
:message="item.message"
|
||||
@@ -244,6 +281,8 @@ function handleCopyAction() {
|
||||
.chat-timeline-item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
--chat-assistant-avatar-size: 28px;
|
||||
--chat-message-max-width: 680px;
|
||||
}
|
||||
|
||||
.chat-timeline-item.is-user {
|
||||
@@ -254,6 +293,46 @@ function handleCopyAction() {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.chat-timeline-item__message-row {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
max-width: min(78%, var(--chat-message-max-width));
|
||||
}
|
||||
|
||||
.chat-timeline-item__message-row.is-user {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.chat-timeline-item__message-row.is-assistant {
|
||||
gap: var(--space-2);
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.chat-timeline-item__message-row.has-assistant-avatar {
|
||||
max-width: min(
|
||||
78%,
|
||||
calc(
|
||||
var(--chat-message-max-width) + var(--chat-assistant-avatar-size) +
|
||||
var(--space-2)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
.chat-timeline-item__assistant-avatar {
|
||||
flex: 0 0 var(--chat-assistant-avatar-size);
|
||||
width: var(--chat-assistant-avatar-size);
|
||||
height: var(--chat-assistant-avatar-size);
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-control);
|
||||
}
|
||||
|
||||
.chat-timeline-item__status-row {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.chat-timeline-item__bubble {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -268,7 +347,7 @@ function handleCopyAction() {
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
min-width: 0;
|
||||
max-width: min(78%, 680px);
|
||||
max-width: var(--chat-message-max-width);
|
||||
}
|
||||
|
||||
.chat-timeline-item__message.is-user {
|
||||
@@ -294,8 +373,23 @@ function handleCopyAction() {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.chat-timeline-item__message.is-assistant.has-variant-navigator {
|
||||
width: min(78%, 680px);
|
||||
.chat-timeline-item__message-row.has-variant-navigator {
|
||||
width: min(78%, var(--chat-message-max-width));
|
||||
}
|
||||
|
||||
.chat-timeline-item__message-row.has-assistant-avatar.has-variant-navigator {
|
||||
width: min(
|
||||
78%,
|
||||
calc(
|
||||
var(--chat-message-max-width) + var(--chat-assistant-avatar-size) +
|
||||
var(--space-2)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
.chat-timeline-item__message-row.has-assistant-avatar.has-variant-navigator
|
||||
.chat-timeline-item__message {
|
||||
width: calc(100% - var(--chat-assistant-avatar-size) - var(--space-2));
|
||||
}
|
||||
|
||||
.chat-timeline-item__bubble.is-assistant,
|
||||
|
||||
Reference in New Issue
Block a user