js部分
async function sendMessage(content: string): Promise<void> {
if (!content) {
return;
}
chatInfo.value.push({ role: 1, message: content });
chatInfo.value.push({ role: 0, message: '' });
scrollToBottom();
inputDisabled.value = true;
messageSpinning.value = true;
inputValue.value = '';
await robotChatApi(content).then(
(res) => {
if (res.data.result) {
const data = res.data.result?.split('');
let index = 0;
let timer = null;
timer = window.requestAnimationFrame(function fn() {
if (index < data.length) {
messageSpinning.value = false;
chatInfo.value[chatInfo.value.length - 1].message += data[index++];
scrollToBottom();
timer = requestAnimationFrame(fn);
} else {
cancelAnimationFrame(timer);
messageSpinning.value = false;
}
});
}
},
(error) => {
console.error(error);
inputDisabled.value = false;
messageSpinning.value = false;
if (chatInfo.value[chatInfo.value.length - 1].message === '') {
chatInfo.value.pop();
}
}
);
}
html部分
<template>
<div class="chat-box">
<div class="chat-main">
<div class="chat-content" ref="scrollRef">
<ul v-for="(item, index) in chatInfo" :key="index">
<li :class="item.role === 0 ? 'left' : 'right'">
<span v-if="item.role === 0">
<img src="@/assets/images/robot.png" class="avatar" />
</span>
<span class="content">
<a-spin
size="small"
:spinning="messageSpinning && item.role === 0 && index === chatInfo.length - 1"
class="load" />
{{ item.message }}
</span>
<span style="vertical-align: top" v-if="item.role === 1">
<img src="@/assets/images/user.png" class="avatar" />
</span>
</li>
</ul>
</div>
<div class="send-box">
<a-input
:disabled="inputDisabled"
validationType="string"
v-model:value="inputValue"
@keyup.enter="sendMessage(inputValue)">
<template v-slot:suffix>
<SendOutlined @click="sendMessage(inputValue)" class="svg-icon" />
</template>
</a-input>
</div>
</div>
</div>
</template>
css部分
<style scoped lang="scss">
.chat-box {
width: 100%;
display: flex;
flex-direction: column;
overflow: scroll;
.chat-main {
width: 100%;
margin: auto;
.chat-content {
height: calc(100vh - 196px);
overflow-x: hidden;
overflow-y: auto;
ul {
list-style: none;
padding: 10px;
margin: 0;
font-size: 14px;
line-height: 20px;
}
li.left {
margin-right: 20px;
display: flex;
}
.load {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
li.left .content {
display: inline-block;
border-radius: 5px;
background-color: #fff;
padding: 10px 15px;
margin-left: 8px;
max-width: 80%;
position: relative;
}
li.right {
margin-left: 20px;
flex-wrap: wrap;
display: flex;
justify-content: flex-end;
}
li.right .content {
display: inline-block;
border-radius: 5px;
background-color: #07c160;
padding: 10px 15px;
margin-right: 8px;
max-width: 80%;
color: #fff;
}
li + li {
margin-top: 20px;
}
}
}
}
</style>