mirror of
https://github.com/jumpserver/lina.git
synced 2025-09-19 17:54:37 +00:00
perf: chat
This commit is contained in:
@@ -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 {
|
||||
|
@@ -115,6 +115,9 @@ export default {
|
||||
.header-avatar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
&>>> img {
|
||||
background-color: #e5e5e7;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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",
|
||||
|
@@ -740,6 +740,15 @@
|
||||
"SyncUser": "ユーザーを同期する",
|
||||
"Reconnect": "再接続",
|
||||
"NewChat": "新しいチャット",
|
||||
"Chat": "チャット",
|
||||
"introduction": {
|
||||
"ConceptTitle": "🤔 複雑な概念を説明する",
|
||||
"ConceptContent": "ビッグバンについて教えてください。5歳の子供に説明することができます",
|
||||
"IdeaTitle": "🧠 これまでにないアイデアを集めて考える",
|
||||
"IdeaContent": "友達の誕生日に10個のプレゼントアイデアを提供してください",
|
||||
"ArticleTitle": "💭 すぐに朗らかなタイトルを手に入れる",
|
||||
"ArticleContent": "ChatGPTの使用例に関する記事を作成するために、5つの朗々としたタイトルを生成します"
|
||||
},
|
||||
"imExport": {
|
||||
"ExportAll": "すべてをエクスポート",
|
||||
"ExportOnlyFiltered": "検索結果のみエクスポート",
|
||||
|
@@ -791,6 +791,15 @@
|
||||
"SyncUser": "同步用户",
|
||||
"Reconnect": "重新连接",
|
||||
"NewChat": "新聊天",
|
||||
"Chat": "聊天",
|
||||
"introduction": {
|
||||
"ConceptTitle": "🤔 解释一个复杂的概念",
|
||||
"ConceptContent": "告诉我一些关于大爆炸的事情,这样我就可以向我 5 岁的孩子解释",
|
||||
"IdeaTitle": "🧠 集思广益前所未有的想法",
|
||||
"IdeaContent": "请为我朋友的生日提供 10 个礼物创意",
|
||||
"ArticleTitle": "💭 立即获得朗朗上口的标题",
|
||||
"ArticleContent": "为我撰写有关 ChatGPT 用例的文章生成五个朗朗上口的标题"
|
||||
},
|
||||
"imExport": {
|
||||
"ExportAll": "导出所有",
|
||||
"ExportOnlyFiltered": "仅导出搜索结果",
|
||||
|
Reference in New Issue
Block a user