mirror of
https://github.com/jumpserver/lina.git
synced 2025-09-20 02:31:43 +00:00
perf: chat
This commit is contained in:
@@ -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 {
|
||||||
|
@@ -115,6 +115,9 @@ export default {
|
|||||||
.header-avatar {
|
.header-avatar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
&>>> img {
|
||||||
|
background-color: #e5e5e7;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
|
@@ -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;
|
||||||
|
@@ -2,7 +2,10 @@
|
|||||||
<div class="chat">
|
<div class="chat">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<span class="title">{{ title }}</span>
|
<div class="left">
|
||||||
|
<img :src="robotUrl" alt="">
|
||||||
|
<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;
|
||||||
.title {
|
.left {
|
||||||
display: inline-block;
|
img {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
vertical-align: sub;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
|
box-shadow: 0 0 8px 4px #00000014;
|
||||||
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
cursor: pointer;
|
// left: -52px !important;
|
||||||
|
// width: 52px !important;
|
||||||
background-color: rgba(182, 181, 186, .9);
|
background-color: rgba(182, 181, 186, .9);
|
||||||
}
|
}
|
||||||
i {
|
i {
|
||||||
|
@@ -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",
|
||||||
|
@@ -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": "検索結果のみエクスポート",
|
||||||
|
@@ -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": "仅导出搜索结果",
|
||||||
|
Reference in New Issue
Block a user