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; margin-top: 16px;
border: 1px solid #DCDFE6; border: 1px solid #DCDFE6;
border-radius: 12px; border-radius: 12px;
&:hover { &:has(.el-textarea__inner:focus) {
border: 1px solid var(--color-primary); border: 1px solid var(--color-primary);
} }
&>>> .el-textarea { &>>> .el-textarea {

View File

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

View File

@@ -1,7 +1,27 @@
<template> <template>
<div class="chat-content"> <div class="chat-content">
<div id="scrollRef" class="chat-list"> <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" /> <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>
<div class="input-box"> <div class="input-box">
<ChatInput @send="onSendHandle" /> <ChatInput @send="onSendHandle" />
@@ -36,11 +56,28 @@ export default {
}, },
data() { data() {
return { 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: { computed: {
...mapState({ ...mapState({
isLoading: state => state.chat.loading,
activeChat: state => state.chat.activeChat activeChat: state => state.chat.activeChat
}) })
}, },
@@ -59,6 +96,18 @@ export default {
const url = process.env.NODE_ENV === 'development' ? localPath : path const url = process.env.NODE_ENV === 'development' ? localPath : path
createWebSocket(url, this.onWebSocketMessage) 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) { onWebSocketMessage(data) {
if (data.type === 'message') { if (data.type === 'message') {
this.onChatMessage(data) this.onChatMessage(data)
@@ -91,19 +140,10 @@ export default {
this.socketReadyStateSuccess = false this.socketReadyStateSuccess = false
setLoading(true) setLoading(true)
}, },
initChatMessage() {
const chat = {
message: {
content: this.$t('common.ChatHello'),
role: 'assistant',
create_time: new Date()
}
}
newChatAndAddMessageById(chat)
setLoading(false)
},
onSendHandle(value) { onSendHandle(value) {
if (ws.readyState === 1) { this.showIntroduction = false
this.socket = ws || {}
if (ws?.readyState === 1) {
this.socketReadyStateSuccess = true this.socketReadyStateSuccess = true
const chat = { const chat = {
message: { message: {
@@ -132,6 +172,22 @@ export default {
this.socketReadyStateSuccess = false this.socketReadyStateSuccess = false
setLoading(true) 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; flex-direction: column;
overflow: hidden; overflow: hidden;
height: 100%; 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 { .chat-list {
flex: 1; flex: 1;
position: relative;
padding: 0 15px; padding: 0 15px;
overflow-y: auto; overflow-y: auto;
user-select: text; user-select: text;
.stop {
position: absolute;
bottom: 6px;
left: 50%;
transform: translateX(-50%);
>>> i {
margin-right: 4px;
}
}
} }
.input-box { .input-box {
height: 154px; height: 154px;

View File

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

View File

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

View File

@@ -739,6 +739,15 @@
"SyncUser": "Sync User", "SyncUser": "Sync User",
"Reconnect": "Reconnect", "Reconnect": "Reconnect",
"NewChat": "New Chat", "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": { "imExport": {
"ExportAll": "Export all", "ExportAll": "Export all",
"ExportOnlyFiltered": "Export only filtered", "ExportOnlyFiltered": "Export only filtered",

View File

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

View File

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