该篇主要为搭建聊天容器,实现对话显示,使用技术有vue,vuex,tailwind
类定义使用了TS,编译出来的 JavaScript 可以运行在任何浏览器上,且方便管理不同的类
Message
messageType定义了发给后端的信息,包含的信息有
- idx用于定义一条message在一个conversation中是第几条
- loading表示该对话是否在加载中
- role表示的是说话角色,这里我们按照智谱清言的定义方式,其中assistant为ai的回答,user为用户的问题,system为系统设定(system在实际中不常使用)
- speech为回答数组,用于ai重新生成回答时使用
- timestamp为消息发送时间
export interface messageType {
idx: number;
loading: boolean;
role: string;
timestamp?: number;
suitable: number[];
speeches: string[];
}
ConvType
convType在多个message集合中,加入以下属性扩展
- id这里的id表示的是当前有几个会话
- title指的是会话的标题
- timestamp表示会话创建时间
- msgList表示messageType的数组
- selected表示该会话是否被选择,可以用于样式的更新
export interface convType {
id: number;
title: string;
timestamp?: number;
msgList?: messageType[];
selected?:boolean;
editable?:boolean;
}
StateType
主要用于管理会话的状态
- conversationList表示会话的列表
- conversation表示当前对话是否被选中
- chatStatus指当前会话状态
export interface stateType { conversationList: convType[]|null; conversation?: convType|null; chatStatus?: "chatting"|"available"; chatMsg: string; selectedIdx?: number; }
输入框
功能:
- 信息输入,用户输入信息,输入框要随着输入信息而扩大
- 问题发送,这个功能比较麻烦,需要和后端和其他模块进行交互
- 重新回答:点击之后重新生成回答
<input type="checkbox" :id="option.value" @change="selectOption(option)" v-model="option.selected"
:value="option.value" class="p-1 rounded-md hover:bg-gray-100 dark:hover:bg-gray-900">
<button @click.stop.prevent="send" :disabled="convLoading"
class="absolute p-1 rounded-md text-gray-500 bottom-1.5 right-1 md:bottom-2.5 md:right-2 hover:bg-gray-100 dark:hover:text-gray-400 dark:hover:bg-gray-900 disabled:hover:bg-transparent dark:disabled:hover:bg-transparent">
<div v-if="convLoading" class="text-2xl" style="line-height: 1.3rem;">
<span class="load_dot1">·</span><span class="load_dot2">·</span><span class="load_dot3">·</span>
</div>
<svg v-else stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round"
stroke-linejoin="round" class="h-4 w-4 mr-1" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
<line x1="22" y1="2" x2="11" y2="13"></line>
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
</svg>
</button>
<button v-if="!convLoading && this.messageList.length > 0" @click.stop.prevent="chatRepeat" id="chatRepeat"
class="btn flex justify-center gap-2 btn-neutral border-0 md:border">
<svg stroke="currentColor" fill="none" stroke-width="1.5" viewBox="0 0 24 24" stroke-linecap="round"
stroke-linejoin="round" class="h-3 w-3" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
<polyline points="1 4 1 10 7 10"></polyline>
<polyline points="23 20 23 14 17 14"></polyline>
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"></path>
</svg>
<p class="none">Regenerate response</p>
</button>
首先将chatMsg和textarea使用v-model进行双向绑定,对于chatMsg进行监听,在chatMsg变化的时候调用方法改变文本框高度
接着实现修改高度的方法,实现的changeHeight方法如下
changeHeight() {
var elem = this.$refs.inputChat;
elem.style.height = '24px';
var scrollHeight = elem.scrollHeight;
if (24 >= scrollHeight || this.chatMsg.length == 0) {
this.resetHeight();
return;
}
elem.style.removeProperty("overflow-y")
elem.style.height = scrollHeight + 'px';
console.log(scrollHeight)
},
信息初始化
实现流程:生成两条信息,分别为用户提问和ai回答,用户提问的信息内容为chatMsg即输入框中的内容
addInitMessage() {
this.addMessage({
"idx": 0,
"role": "user",
"message": this.chatMsg
})
var message = {
"idx": 0,
"loading": true,
"role": "assistant",
"suitable": [0],
"speeches": [""]
}
this.addMessage(message)
},
聊天实现
chat() {
// console.log("bus-send",that.conversation)
bus.emit('bus-send')
const chatMsgCopy = this.chatMsg;
this.chatMsg = "";
aiLongChat(this.model, chatMsgCopy, this.conversation.id).then((res) => {
var tmpMessage = this.conversation.msgList[this.conversation.msgList?.length - 1]
tmpMessage["speeches"][0] += res.data.message
tmpMessage["loading"] = false;
this.conversation.msgList[this.conversation.msgList?.length - 1] = tmpMessage;
this.updateConversation(this.conversation);
this.handleScrollBottom();
console.log(res.data.message)
}).catch((err) => {
console.log(err)
})
this.convLoading = false;
},
发送实现
send() {
if (this.chatMsg.trim().length == 0) {
return;
}
if (this.convLoading) {
return;
}
this.convLoading = true;
this.chatMsg = this.chatMsg.trim().replace(/\n/g, "")
this.addInitMessage()
// 滚动到最下面
this.handleScrollBottom();
//this.streamChat();
this.chat();
//this.convLoading = false;
},