perf: chat

This commit is contained in:
“huailei000”
2023-12-12 18:47:54 +08:00
parent 6b37c71b6d
commit 4b98806b3e
8 changed files with 164 additions and 21 deletions

View File

@@ -89,7 +89,7 @@ export default {
margin-top: 16px;
border: 1px solid #DCDFE6;
border-radius: 12px;
&:hover {
&:has(.el-textarea__inner:focus) {
border: 1px solid var(--color-primary);
}
&>>> .el-textarea {

View File

@@ -115,6 +115,9 @@ export default {
.header-avatar {
width: 100%;
height: 100%;
&>>> img {
background-color: #e5e5e7;
}
}
}
.content {

View File

@@ -1,7 +1,27 @@
<template>
<div class="chat-content">
<div id="scrollRef" class="chat-list">
<div v-if="showIntroduction" class="introduction">
<div v-for="(item, index) in introduction" :key="index" class="introduction-item">
<div class="head">
<i v-if="item.icon" :class="item.icon" />
<span class="title">{{ item.title }}</span>
</div>
<div class="content">
{{ item.content }}
<i class="fa fa-arrow-right" />
</div>
</div>
</div>
<ChatMessage v-for="(item, index) in activeChat.chats" :key="index" :item="item" />
<el-button
v-if="isLoading && socket && socket.readyState === 1"
round
size="small"
class="stop"
icon="fa fa-stop-circle-o"
@click="onStopHandle"
>{{ $tc('common.Stop') }}</el-button>
</div>
<div class="input-box">
<ChatInput @send="onSendHandle" />
@@ -36,11 +56,28 @@ export default {
},
data() {
return {
currentConversationId: ''
socket: {},
currentConversationId: '',
showIntroduction: false,
introduction: [
{
title: this.$t('common.introduction.ConceptTitle'),
content: this.$t('common.introduction.ConceptContent')
},
{
title: this.$t('common.introduction.IdeaTitle'),
content: this.$t('common.introduction.IdeaContent')
},
{
title: this.$t('common.introduction.ArticleTitle'),
content: this.$t('common.introduction.ArticleContent')
}
]
}
},
computed: {
...mapState({
isLoading: state => state.chat.loading,
activeChat: state => state.chat.activeChat
})
},
@@ -59,6 +96,18 @@ export default {
const url = process.env.NODE_ENV === 'development' ? localPath : path
createWebSocket(url, this.onWebSocketMessage)
},
initChatMessage() {
this.showIntroduction = true
const chat = {
message: {
content: this.$t('common.ChatHello'),
role: 'assistant',
create_time: new Date()
}
}
newChatAndAddMessageById(chat)
setLoading(false)
},
onWebSocketMessage(data) {
if (data.type === 'message') {
this.onChatMessage(data)
@@ -91,19 +140,10 @@ export default {
this.socketReadyStateSuccess = false
setLoading(true)
},
initChatMessage() {
const chat = {
message: {
content: this.$t('common.ChatHello'),
role: 'assistant',
create_time: new Date()
}
}
newChatAndAddMessageById(chat)
setLoading(false)
},
onSendHandle(value) {
if (ws.readyState === 1) {
this.showIntroduction = false
this.socket = ws || {}
if (ws?.readyState === 1) {
this.socketReadyStateSuccess = true
const chat = {
message: {
@@ -132,6 +172,22 @@ export default {
this.socketReadyStateSuccess = false
setLoading(true)
}
},
onStopHandle() {
const { protocol, host } = window.location
const { NODE_ENV, VUE_APP_KAEL_WS } = process.env || {}
const api = '/kael/chat/system/interrupt_current_ask/'
const path = `${protocol}://${host}`
const index = VUE_APP_KAEL_WS.indexOf('//')
const localPath = protocol + VUE_APP_KAEL_WS.substring(index, VUE_APP_KAEL_WS.length) + api
const url = NODE_ENV === 'development' ? localPath : path
this.$axios.post(
url,
{ id: this.currentConversationId || '' }
).finally(() => {
removeLoadingMessageInChat()
setLoading(false)
})
}
}
}
@@ -143,11 +199,45 @@ export default {
flex-direction: column;
overflow: hidden;
height: 100%;
.introduction {
padding: 16px 14px 0;
.introduction-item {
padding: 12px 14px;
border-radius: 8px;
margin-top: 16px;
background-color: var(--menu-hover);
&:first-child {
margin-top: 0;
}
.head {
margin-bottom: 2px;
.title {
font-weight: 500;
color: #373739;
}
}
.content {
display: inline-block;
color: #a7a7ab;
word-wrap: break-word;
}
}
}
.chat-list {
flex: 1;
position: relative;
padding: 0 15px;
overflow-y: auto;
user-select: text;
.stop {
position: absolute;
bottom: 6px;
left: 50%;
transform: translateX(-50%);
>>> i {
margin-right: 4px;
}
}
}
.input-box {
height: 154px;

View File

@@ -2,7 +2,10 @@
<div class="chat">
<div class="container">
<div class="header">
<div class="left">
<img :src="robotUrl" alt="">
<span class="title">{{ title }}</span>
</div>
<span class="new" @click="onNewChat">
<i class="el-icon-plus" />
<span>{{ $tc('common.NewChat') }}</span>
@@ -23,6 +26,7 @@
<script>
import Sidebar from './components/Sidebar/index.vue'
import Chat from './components/ChitChat/index.vue'
import { getInputFocus } from './useChat.js'
export default {
components: {
@@ -32,12 +36,15 @@ export default {
props: {
title: {
type: String,
default: 'Chat'
default: function() {
return this.$t('common.Chat')
}
}
},
data() {
return {
active: 'chat',
robotUrl: require('../../../assets/img/robot-assistant.png'),
submenu: [
{
name: 'chat',
@@ -56,6 +63,7 @@ export default {
this.$nextTick(() => {
this.$refs.component.initWebSocket()
this.$refs.component.initChatMessage()
getInputFocus()
})
}
}
@@ -80,8 +88,17 @@ export default {
padding: 0 16px;
overflow: hidden;
border-bottom: 1px solid #ececec;
.left {
img {
width: 22px;
height: 22px;
vertical-align: sub;
}
.title {
display: inline-block;
font-size: 18px;
color: black;
}
}
.new {
display: inline-block;
@@ -101,6 +118,8 @@ export default {
}
.content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
}

View File

@@ -2,7 +2,7 @@
<div ref="drawer" :class="{show: show}" class="drawer">
<div class="modal" :style="{'background-color': modal ? 'rgba(0, 0, 0, .3)' : 'transparent'}" />
<div class="drawer-panel" :style="{'width': width}">
<div ref="dragBox" class="handle-button">
<div v-show="!show" ref="dragBox" class="handle-button">
<i v-if="icon.startsWith('fa') || icon.startsWith('el')" :class="show ? 'el-icon-close': icon" />
<img v-else :src="icon" alt="">
</div>
@@ -141,6 +141,7 @@ export default {
min-width: 260px;
height: 100vh;
user-select: none;
border-radius: 20px 0 0 20px;
box-shadow: 0 0 15px 0 rgba(0, 0, 0, .05);
transition: transform .25s cubic-bezier(.7, .3, .1, 1);
box-shadow: 0 0 8px 4px #00000014;
@@ -188,9 +189,12 @@ export default {
pointer-events: auto;
color: #fff;
background-color: #FFFFFF;
box-shadow: -3px 0px 10px 1px #00000014;
&:hover {
// transition: width, .2s ease .2s;
box-shadow: 0 0 8px 4px #00000014;
cursor: pointer;
&:hover {
// left: -52px !important;
// width: 52px !important;
background-color: rgba(182, 181, 186, .9);
}
i {

View File

@@ -739,6 +739,15 @@
"SyncUser": "Sync User",
"Reconnect": "Reconnect",
"NewChat": "New Chat",
"Chat": "Chat",
"introduction": {
"ConceptTitle": "🤔 Explain a complex concept",
"ConceptContent": "Tell me something about the Big Bang so that I can explain it to my 5-year-old child",
"IdeaTitle": "🧠 Brainstorming unprecedented ideas",
"IdeaContent": "Please provide 10 gift ideas for my friend's birthday",
"ArticleTitle": "💭 Get a catchy title immediately",
"ArticleContent": "Generate five catchy titles for my article on ChatGPT use cases"
},
"imExport": {
"ExportAll": "Export all",
"ExportOnlyFiltered": "Export only filtered",

View File

@@ -740,6 +740,15 @@
"SyncUser": "ユーザーを同期する",
"Reconnect": "再接続",
"NewChat": "新しいチャット",
"Chat": "チャット",
"introduction": {
"ConceptTitle": "🤔 複雑な概念を説明する",
"ConceptContent": "ビッグバンについて教えてください。5歳の子供に説明することができます",
"IdeaTitle": "🧠 これまでにないアイデアを集めて考える",
"IdeaContent": "友達の誕生日に10個のプレゼントアイデアを提供してください",
"ArticleTitle": "💭 すぐに朗らかなタイトルを手に入れる",
"ArticleContent": "ChatGPTの使用例に関する記事を作成するために、5つの朗々としたタイトルを生成します"
},
"imExport": {
"ExportAll": "すべてをエクスポート",
"ExportOnlyFiltered": "検索結果のみエクスポート",

View File

@@ -791,6 +791,15 @@
"SyncUser": "同步用户",
"Reconnect": "重新连接",
"NewChat": "新聊天",
"Chat": "聊天",
"introduction": {
"ConceptTitle": "🤔 解释一个复杂的概念",
"ConceptContent": "告诉我一些关于大爆炸的事情,这样我就可以向我 5 岁的孩子解释",
"IdeaTitle": "🧠 集思广益前所未有的想法",
"IdeaContent": "请为我朋友的生日提供 10 个礼物创意",
"ArticleTitle": "💭 立即获得朗朗上口的标题",
"ArticleContent": "为我撰写有关 ChatGPT 用例的文章生成五个朗朗上口的标题"
},
"imExport": {
"ExportAll": "导出所有",
"ExportOnlyFiltered": "仅导出搜索结果",