Files
chatai/src/components/chat.vue
2023-05-15 01:24:18 +08:00

189 lines
4.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<section class="chat">
<el-row align="middle mar-b16">
<div class="modelInfo">
<img :src="config.model.avatar" alt=""/>
<div class="fill mar-l8">
<b v-text="config.model.name"/>
<div class="desc" v-text="config.model.desc"/>
</div>
</div>
<el-row justify="end" class="fill mar-l8 gap-8">
<!--聊天操作栏-->
<div class="optIcon clear"/>
<div class="optIcon setting" @click="$emit('setting')"/>
</el-row>
</el-row>
<thinking-bar v-show="loading"/>
<chat-content class="fill" :list="chatHistory"/>
<el-row class="mar-t8" align="middle">
<chat-input class="fill mar-r8" v-model="inputText" @enter="handleSend"/>
<el-button type="primary" @click="handleSend">发送</el-button>
</el-row>
</section>
</template>
<script>
import {dayjs} from "element-plus";
import ChatContent from "../components/chatContent";
import ChatInput from "../components/chatInput";
import ThinkingBar from "../components/thinkingBar";
import {USER_AVATAR} from "../utils/env";
export default {
name: "chat",
components: {ChatContent, ChatInput, ThinkingBar},
props: {
config: {default: () => ({})}
},
data() {
return {
loading: false,
inputText: "",
chatHistory: []
}
},
methods: {
handleSend() {
if (!!this.inputText) {
const ai = this.config.model
const myMsg = {
avatar: USER_AVATAR,
name: "我",
time: dayjs().format("YYYY-MM-DD HH:mm:ss"),
msg: this.inputText,
chatType: 0, //信息类型0文字1图片
uid: "me", //uid
role: "user"
}
this.chatHistory.push(myMsg)
this.loading = true
const aiMsg = {
avatar: ai.avatar,
name: ai.name,
time: dayjs().format("YYYY-MM-DD HH:mm:ss"),
msg: "",
chatType: 0, //信息类型0文字1图片
uid: "ai", //uid
role: "assistant"
}
this.chatHistory.push(aiMsg)
if (this.config.stream) {
ai.chatStream(this.chatHistory).then(reader => this.streamOutput(reader, this.chatHistory.at(-1))).finally(() => this.loading = false)
} else {
ai.chat(this.chatHistory).then(reply => {
const decodeArr = reply.split("")
decodeArr.forEach(e => this.chatHistory.at(-1).msg += e)
}).finally(() => this.loading = false)
}
} else {
this.$message.error("请不要发送空消息!")
}
},
streamOutput(reader, chat) {
return reader.read().then(({done, value}) => {
if (done) {
return;
}
if (!chat.reminder) {
chat.reminder = ""
}
let decode = new TextDecoder().decode(value)
decode = chat.reminder + decode
let decodedArray = decode.split("data: ");
let longstr = "";
decodedArray.forEach(decoded => {
decoded = decoded.trim();
try {
if (longstr != "") {
decoded = longstr + decoded;
longstr = "";
}
} catch (e) {
longstr = decoded;
decoded = "";
}
if (!!decoded && decoded !== "[DONE]") {
const choices = JSON.parse(decoded).choices
if (choices?.length > 0) {
const response = choices[0].delta.content || "";
chat.msg += response
}
}
})
return this.streamOutput(reader, chat)
})
}
}
}
</script>
<style lang="scss" scoped>
.chat {
color: #fff;
display: flex;
flex-direction: column;
.modelInfo {
display: flex;
align-items: center;
line-height: 1.4;
& > img {
border: 2px solid #fff;
border-radius: 50%;
padding: 4px;
width: 45px;
height: 45px;
}
b {
font-size: 20px;
}
.desc {
color: #999;
font-size: 14px;
}
}
.optIcon {
box-sizing: border-box;
height: 40px;
min-width: 40px;
background-size: 24px 24px;
background-repeat: no-repeat;
background-position: top center;
cursor: pointer;
opacity: .6;
padding-top: 24px;
&:after {
font-size: 12px;
display: block;
text-align: center;
}
&:hover {
opacity: 1;
}
&.clear {
background-image: url("../assets/clear.svg");
&:after {
content: "清空历史";
}
}
&.setting {
background-image: url("../assets/setting.svg");
&:after {
content: "设置";
}
}
}
}
</style>