105 lines
2.2 KiB
Vue
105 lines
2.2 KiB
Vue
|
<template>
|
||
|
<div class="message-stream">
|
||
|
{{ displayedContent }}<span v-if="!isFinished" class="blinking-cursor" />
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script lang="ts">
|
||
|
import vue from "../adapter-vue";
|
||
|
import { customerServicePayloadType } from "../interface";
|
||
|
|
||
|
const { ref, watchEffect, onBeforeUnmount, onMounted } = vue;
|
||
|
|
||
|
interface Props {
|
||
|
payload: customerServicePayloadType;
|
||
|
}
|
||
|
|
||
|
export default {
|
||
|
props: {
|
||
|
payload: {
|
||
|
type: Object as () => customerServicePayloadType,
|
||
|
default: () => ({}),
|
||
|
},
|
||
|
},
|
||
|
setup(props: Props) {
|
||
|
const content = ref<string>("");
|
||
|
const displayedContent = ref<string>("");
|
||
|
const isFinished = ref<boolean>(false);
|
||
|
let intervalId: number | null = null;
|
||
|
let currentIndex = 0;
|
||
|
|
||
|
const updateDisplayedContent = () => {
|
||
|
if (intervalId) {
|
||
|
window.clearInterval(intervalId);
|
||
|
}
|
||
|
intervalId = window.setInterval(() => {
|
||
|
if (currentIndex < content.value.length) {
|
||
|
displayedContent.value += content.value[currentIndex];
|
||
|
currentIndex++;
|
||
|
} else {
|
||
|
window.clearInterval(intervalId!);
|
||
|
intervalId = null;
|
||
|
}
|
||
|
}, 50);
|
||
|
};
|
||
|
|
||
|
onMounted(() => {
|
||
|
content.value = props?.payload?.chunks?.join("") ?? "";
|
||
|
displayedContent.value = content.value;
|
||
|
currentIndex = content.value.length;
|
||
|
});
|
||
|
|
||
|
watchEffect(() => {
|
||
|
const newContent = props?.payload?.chunks?.join("") ?? "";
|
||
|
if (newContent.length > currentIndex) {
|
||
|
content.value = newContent;
|
||
|
updateDisplayedContent();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
watchEffect(() => {
|
||
|
isFinished.value = props?.payload?.isFinished === 1;
|
||
|
});
|
||
|
|
||
|
onBeforeUnmount(() => {
|
||
|
if (intervalId) {
|
||
|
window.clearInterval(intervalId);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return {
|
||
|
content,
|
||
|
props,
|
||
|
isFinished,
|
||
|
displayedContent,
|
||
|
};
|
||
|
},
|
||
|
};
|
||
|
</script>
|
||
|
<style lang="scss" scoped>
|
||
|
.message-stream {
|
||
|
word-break: break-all;
|
||
|
font-size: 14px;
|
||
|
|
||
|
.blinking-cursor {
|
||
|
display: inline-block;
|
||
|
width: 1px;
|
||
|
height: 16px;
|
||
|
background-color: black;
|
||
|
animation: blink 1s step-end infinite;
|
||
|
vertical-align: sub;
|
||
|
}
|
||
|
|
||
|
@keyframes blink {
|
||
|
0%,
|
||
|
100% {
|
||
|
background-color: transparent;
|
||
|
}
|
||
|
|
||
|
50% {
|
||
|
background-color: black;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</style>
|