|
|
@@ -156,6 +156,18 @@
|
|
|
need-view-code-btn
|
|
|
@card-submit="(val) => emit('card-submit', val)"
|
|
|
/>
|
|
|
+
|
|
|
+ <div v-if="isStreamingMessage(item)" class="ml-10px w-fit">
|
|
|
+ <div class="elx-bubble__loading-wrap">
|
|
|
+ <div
|
|
|
+ v-for="(_, index) in 3"
|
|
|
+ :key="index"
|
|
|
+ class="elx-bubble__dot"
|
|
|
+ :style="{ animationDelay: `${index * 0.2}s` }"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
<div v-if="item?.stopped" class="msg-stop-indicator">
|
|
|
{{ t('pages.chat.stopped') }}
|
|
|
</div>
|
|
|
@@ -540,6 +552,9 @@ const getDisplayText = (message: BubbleMessage) =>
|
|
|
|
|
|
const isErrorMessage = (message: BubbleMessage) => !!parseMessageError(message)
|
|
|
|
|
|
+const isStreamingMessage = (message: BubbleMessage) =>
|
|
|
+ message.role === 'ai' && !message.streamCompleted && !message.stopped
|
|
|
+
|
|
|
const getThinkingText = (message: BubbleMessage) => {
|
|
|
const parts = [`${message.thinking || ''}`.trim(), getEmbeddedThinkingText(message)].filter(
|
|
|
Boolean
|
|
|
@@ -691,6 +706,37 @@ const handleAddToKb = (message: BubbleMessage) => {
|
|
|
color: var(--text-tertiary);
|
|
|
}
|
|
|
|
|
|
+ .msg-stream-indicator {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ align-self: flex-start;
|
|
|
+ gap: 6px;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 1.4;
|
|
|
+ color: var(--text-tertiary);
|
|
|
+ }
|
|
|
+
|
|
|
+ .msg-stream-dots {
|
|
|
+ display: inline-flex;
|
|
|
+ gap: 3px;
|
|
|
+
|
|
|
+ i {
|
|
|
+ width: 4px;
|
|
|
+ height: 4px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: currentColor;
|
|
|
+ animation: msg-stream-dot 1s ease-in-out infinite;
|
|
|
+ }
|
|
|
+
|
|
|
+ i:nth-child(2) {
|
|
|
+ animation-delay: 0.15s;
|
|
|
+ }
|
|
|
+
|
|
|
+ i:nth-child(3) {
|
|
|
+ animation-delay: 0.3s;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.msg-content-text :deep(h3) {
|
|
|
margin: 0 0 10px;
|
|
|
font-size: 12px;
|
|
|
@@ -737,6 +783,20 @@ const handleAddToKb = (message: BubbleMessage) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+@keyframes msg-stream-dot {
|
|
|
+ 0%,
|
|
|
+ 80%,
|
|
|
+ 100% {
|
|
|
+ opacity: 0.35;
|
|
|
+ transform: translateY(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ 40% {
|
|
|
+ opacity: 1;
|
|
|
+ transform: translateY(-2px);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.structured-blocks {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
@@ -944,4 +1004,39 @@ const handleAddToKb = (message: BubbleMessage) => {
|
|
|
word-break: break-all;
|
|
|
}
|
|
|
|
|
|
+.elx-bubble__loading-wrap {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ gap: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.elx-bubble__dot {
|
|
|
+ will-change: transform;
|
|
|
+ width: 5px;
|
|
|
+ height: 5px;
|
|
|
+ background-color: var(--elx-bubble-dot-color, var(--el-color-primary));
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: wave 1s infinite ease-in-out;
|
|
|
+}
|
|
|
+
|
|
|
+.dot-2 {
|
|
|
+ animation-delay: 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.dot-3 {
|
|
|
+ animation-delay: 0.4s;
|
|
|
+}
|
|
|
+
|
|
|
+/* 波浪动画 */
|
|
|
+@keyframes wave {
|
|
|
+ 0%,
|
|
|
+ 100% {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ }
|
|
|
+
|
|
|
+ 50% {
|
|
|
+ transform: translateY(2px);
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|