feat: chat list sidebar

This commit is contained in:
ulleo
2025-07-15 17:00:40 +08:00
parent 7907751c1d
commit d7d28b7070
10 changed files with 512 additions and 188 deletions
+2 -1
View File
@@ -630,7 +630,8 @@ def run_task(llm_service: LLMService, in_chat: bool = True):
brief = rename_chat(session=llm_service.session,
rename_object=RenameChat(id=llm_service.get_record().chat_id,
brief=llm_service.chat_question.question.strip()[:20]))
yield orjson.dumps({'type': 'brief', 'brief': brief}).decode() + '\n\n'
if in_chat:
yield orjson.dumps({'type': 'brief', 'brief': brief}).decode() + '\n\n'
# select datasource if datasource is none
if not llm_service.ds:
@@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 1.125C13.3492 1.125 16.875 4.65076 16.875 9C16.875 13.3492 13.3492 16.875 9 16.875H1.875C1.46079 16.875 1.125 16.5392 1.125 16.125V9C1.125 4.65076 4.65076 1.125 9 1.125ZM2.625 15.375H9C12.5208 15.375 15.375 12.5208 15.375 8.99998C15.375 5.47917 12.5208 2.62498 9 2.62498C5.47918 2.62498 2.625 5.47917 2.625 8.99998V15.375Z" fill="currentColor"/>
<path d="M8.625 5.625C8.41789 5.625 8.25 5.79289 8.25 6V8.25H6C5.79289 8.25 5.625 8.41789 5.625 8.625V9.375C5.625 9.58211 5.79289 9.75 6 9.75H8.25V12C8.25 12.2071 8.41789 12.375 8.625 12.375H9.375C9.58211 12.375 9.75 12.2071 9.75 12V9.75H12C12.2071 9.75 12.375 9.58211 12.375 9.375V8.625C12.375 8.41789 12.2071 8.25 12 8.25H9.75V6C9.75 5.79289 9.58211 5.625 9.375 5.625H8.625Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 861 B

+2 -2
View File
@@ -48,7 +48,7 @@
"chart_selected": "Selected {0}"
},
"qa": {
"New Conversation": "New Conversation",
"new_chat": "New Chat",
"title": "Smart Query",
"placeholder": "Enter your question",
"ask": "Ask",
@@ -243,4 +243,4 @@
"back_community": "Restore to Community Edition",
"confirm_tips": "Are you sure to restore to Community Edition?"
}
}
}
+7 -2
View File
@@ -51,7 +51,7 @@
"chart_selected": "已选{0}"
},
"qa": {
"New Conversation": "开启新问数",
"new_chat": "新建对话",
"title": "智能问数",
"placeholder": "请输入您的问题",
"ask": "提问",
@@ -65,7 +65,12 @@
"view_more": "查看更多",
"selected_datasource": "已选择数据源",
"empty_datasource": "数据源为空,请新建后再开启智能问数!",
"datasource_not_exist": "数据源不存在"
"datasource_not_exist": "数据源不存在",
"guess_u_ask": "猜你想问:",
"continue_to_ask": "继续提问:",
"data_analysis": "数据预测",
"data_predict": "数据分析",
"chat_search": "搜索"
},
"ds": {
"title": "数据源",
@@ -0,0 +1,282 @@
<script setup lang="ts">
import { Search } from '@element-plus/icons-vue'
import ChatList from '@/views/chat/ChatList.vue'
import { useI18n } from 'vue-i18n'
import { computed, ref } from 'vue'
import { Chat, chatApi, ChatInfo } from '@/api/chat.ts'
import { filter, includes } from 'lodash-es'
import ChatCreator from '@/views/chat/ChatCreator.vue'
import { useAssistantStore } from '@/stores/assistant'
import icon_sidebar_outlined from '@/assets/svg/icon_sidebar_outlined.svg'
import icon_new_chat_outlined from '@/assets/svg/icon_new_chat_outlined.svg'
const props = withDefaults(
defineProps<{
inPopover?: boolean
chatList?: Array<ChatInfo>
currentChatId?: number
currentChat?: ChatInfo
loading?: boolean
}>(),
{
chatList: () => [],
currentChatId: undefined,
currentChat: () => new ChatInfo(),
loading: false,
inPopover: false,
}
)
const emits = defineEmits([
'goEmpty',
'onChatCreated',
'onClickHistory',
'onChatDeleted',
'onChatRenamed',
'onClickSideBarBtn',
'update:loading',
'update:chatList',
'update:currentChat',
'update:currentChatId',
])
const assistantStore = useAssistantStore()
const isAssistant = computed(() => assistantStore.getAssistant)
const search = ref<string>()
const _currentChatId = computed({
get() {
return props.currentChatId
},
set(v) {
emits('update:currentChatId', v)
},
})
const _currentChat = computed({
get() {
return props.currentChat
},
set(v) {
emits('update:currentChat', v)
},
})
const _chatList = computed({
get() {
return props.chatList
},
set(v) {
emits('update:chatList', v)
},
})
const computedChatList = computed<Array<ChatInfo>>(() => {
if (search.value && search.value.length > 0) {
return filter(_chatList.value, (c) => includes(c.brief, search.value))
} else {
return _chatList.value
}
})
const _loading = computed({
get() {
return props.loading
},
set(v) {
emits('update:loading', v)
},
})
const { t } = useI18n()
function onClickSideBarBtn() {
emits('onClickSideBarBtn')
}
function onChatCreated(chat: ChatInfo) {
_chatList.value.unshift(chat)
_currentChatId.value = chat.id
_currentChat.value = chat
emits('onChatCreated', chat)
}
const chatCreatorRef = ref()
function goEmpty() {
_currentChat.value = new ChatInfo()
_currentChatId.value = undefined
emits('goEmpty')
}
const createNewChat = async () => {
goEmpty()
if (isAssistant.value) {
const assistantChat = await assistantStore.setChat()
if (assistantChat) {
onChatCreated(assistantChat)
}
return
} else {
chatCreatorRef.value?.showDs()
}
}
function onClickHistory(chat: Chat) {
_currentChat.value = new ChatInfo(chat)
if (chat !== undefined && chat.id !== undefined && !_loading.value) {
_currentChatId.value = chat.id
_loading.value = true
chatApi
.get(chat.id)
.then((res) => {
const info = chatApi.toChatInfo(res)
if (info) {
_currentChat.value = info
// scrollToBottom()
emits('onClickHistory', info)
}
})
.finally(() => {
_loading.value = false
})
}
}
function onChatDeleted(id: number) {
for (let i = 0; i < _chatList.value.length; i++) {
if (_chatList.value[i].id === id) {
_chatList.value.splice(i, 1)
break
}
}
if (id === _currentChatId.value) {
goEmpty()
}
emits('onChatDeleted', id)
}
function onChatRenamed(chat: Chat) {
_chatList.value.forEach((c: Chat) => {
if (c.id === chat.id) {
c.brief = chat.brief
}
})
if (_currentChat.value.id === chat.id) {
_currentChat.value.brief = chat.brief
}
emits('onChatRenamed', chat)
}
</script>
<template>
<el-container class="chat-container-right-container">
<el-header class="chat-list-header" :class="{ 'in-popover': inPopover }">
<div v-if="!inPopover" class="title">
<div>{{ t('qa.title') }}</div>
<el-button link type="primary" class="icon-btn" @click="onClickSideBarBtn">
<el-icon>
<icon_sidebar_outlined />
</el-icon>
</el-button>
</div>
<el-button class="btn" type="primary" @click="createNewChat">
<el-icon style="margin-right: 6px">
<icon_new_chat_outlined />
</el-icon>
{{ t('qa.new_chat') }}
</el-button>
<el-input
v-model="search"
:prefix-icon="Search"
class="search"
name="quick-search"
autocomplete="off"
:placeholder="t('qa.chat_search')"
/>
</el-header>
<el-main class="chat-list">
<ChatList
v-model:loading="_loading"
:current-chat-id="_currentChatId"
:chat-list="computedChatList"
@chat-selected="onClickHistory"
@chat-deleted="onChatDeleted"
@chat-renamed="onChatRenamed"
/>
</el-main>
<ChatCreator v-if="!isAssistant" ref="chatCreatorRef" @on-chat-created="onChatCreated" />
</el-container>
</template>
<style scoped lang="less">
.chat-container-right-container {
background: rgba(245, 246, 247, 1);
height: 100%;
.icon-btn {
min-width: unset;
width: 26px;
height: 26px;
font-size: 18px;
&:hover {
background: rgba(31, 35, 41, 0.1);
}
}
.chat-list-header {
--ed-header-padding: 16px;
--ed-header-height: calc(16px + 24px + 16px + 40px + 16px + 32px + 16px);
&.in-popover {
--ed-header-height: calc(16px + 40px + 16px + 32px + 16px);
}
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 16px;
.title {
height: 24px;
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
font-weight: 500;
}
.btn {
width: 100%;
height: 40px;
font-size: 16px;
font-weight: 500;
--ed-button-text-color: rgba(28, 186, 144, 1);
--ed-button-bg-color: rgba(28, 186, 144, 0.1);
--ed-button-border-color: rgba(164, 227, 211, 1);
--ed-button-hover-bg-color: rgba(210, 241, 233, 1);
--ed-button-hover-text-color: rgba(28, 186, 144, 1);
--ed-button-hover-border-color: rgba(28, 186, 144, 1);
--ed-button-active-bg-color: rgba(164, 227, 211, 1);
--ed-button-active-border-color: rgba(28, 186, 144, 1);
}
.search {
height: 32px;
width: 100%;
}
}
.chat-list {
padding: 0 0 20px 0;
}
}
</style>
+5 -5
View File
@@ -1,10 +1,7 @@
<script setup lang="ts">
import ChatBlock from './ChatBlock.vue'
import { ChatInfo, type ChatMessage } from '@/api/chat.ts'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const props = defineProps<{
msg: ChatMessage
currentChat: ChatInfo
@@ -16,7 +13,10 @@ const show = computed(() => {
</script>
<template>
<ChatBlock v-if="show">
<span v-if="show">
<slot name="footer"></slot>
</span>
<!-- <ChatBlock v-if="show">
<div class="welcome-content">
<div class="ds-select-row">
<div>{{ t('qa.selected_datasource') }}:</div>
@@ -28,7 +28,7 @@ const show = computed(() => {
<template #footer>
<slot name="footer"></slot>
</template>
</ChatBlock>
</ChatBlock>-->
</template>
<style scoped lang="less">
+30 -20
View File
@@ -1,37 +1,46 @@
<script setup lang="ts">
import ChatBlock from './ChatBlock.vue'
import ChatRecordFirst from './ChatRecordFirst.vue'
// import ChatBlock from './ChatBlock.vue'
// import ChatRecordFirst from './ChatRecordFirst.vue'
import { ChatInfo, type ChatMessage } from '@/api/chat.ts'
import { UserFilled } from '@element-plus/icons-vue'
// import { UserFilled } from '@element-plus/icons-vue'
import logo_fold from '@/assets/LOGO-fold.svg'
withDefaults(
defineProps<{
msg: ChatMessage
currentChat: ChatInfo
hideAvatar?: boolean
}>(),
{}
{
hideAvatar: false,
}
)
</script>
<template>
<div class="chat-row" :class="{ 'right-to-left': msg.role === 'user' }">
<el-avatar v-if="msg.role === 'assistant'" class="ai-avatar" shape="square">SQLBot</el-avatar>
<el-avatar v-if="msg.role === 'user'" class="user-avatar" shape="square">
<el-avatar v-if="msg.role === 'assistant'" class="ai-avatar" shape="square">
<el-icon>
<UserFilled />
<logo_fold v-if="!hideAvatar" />
</el-icon>
</el-avatar>
<ChatBlock v-if="!msg.first_chat" :msg="msg" :class="{ 'row-full': msg.role === 'assistant' }">
<slot></slot>
<template #footer>
<slot name="footer"></slot>
</template>
</ChatBlock>
<ChatRecordFirst v-else :current-chat="currentChat" :msg="msg">
<template #footer>
<slot name="footer"></slot>
</template>
</ChatRecordFirst>
<!-- <el-avatar v-if="msg.role === 'user'" class="user-avatar" shape="square">-->
<!-- <el-icon>-->
<!-- <UserFilled v-if="!hideAvatar" />-->
<!-- </el-icon>-->
<!-- </el-avatar>-->
<slot></slot>
<!-- <ChatBlock v-if="!msg.first_chat" :msg="msg" :class="{ 'row-full': msg.role === 'assistant' }">-->
<!-- <slot></slot>-->
<!-- <template #footer>-->
<!-- <slot name="footer"></slot>-->
<!-- </template>-->
<!-- </ChatBlock>-->
<!-- <ChatRecordFirst v-else :current-chat="currentChat" :msg="msg">-->
<!-- <template #footer>-->
<!-- <slot name="footer"></slot>-->
<!-- </template>-->
<!-- </ChatRecordFirst>-->
</div>
</template>
@@ -55,10 +64,11 @@ withDefaults(
}
.ai-avatar {
background: var(--el-color-primary);
font-size: 28px;
background: transparent;
}
.user-avatar {
background: var(--ed-color-primary);
font-size: 28px;
}
</style>
@@ -1,13 +1,16 @@
<script setup lang="ts">
import { computed } from 'vue'
import { endsWith, startsWith } from 'lodash-es'
import { useI18n } from 'vue-i18n'
const props = withDefaults(
defineProps<{
questions?: string
firstChat?: boolean
}>(),
{
questions: '[]',
firstChat: false,
}
)
const computedQuestions = computed<string>(() => {
@@ -24,6 +27,8 @@ const computedQuestions = computed<string>(() => {
const emits = defineEmits(['clickQuestion'])
const { t } = useI18n()
function clickQuestion(question: string): void {
emits('clickQuestion', question)
}
@@ -31,7 +36,8 @@ function clickQuestion(question: string): void {
<template>
<div v-if="computedQuestions.length > 0" class="recommend-questions">
<div>推荐提问:</div>
<div v-if="firstChat">{{ t('qa.guess_u_ask') }}</div>
<div v-else>{{ t('qa.continue_to_ask') }}</div>
<div v-for="(question, index) in computedQuestions" :key="index">
<span class="question" @click="clickQuestion(question)">
{{ question }}
@@ -0,0 +1,50 @@
<script setup lang="ts">
import type { ChatMessage } from '@/api/chat.ts'
import { useI18n } from 'vue-i18n'
const props = defineProps<{
message?: ChatMessage
}>()
const { t } = useI18n()
function clickAnalysis() {
console.log('analysis_record_id: ' + props.message?.record?.analysis_record_id)
}
function clickPredict() {
console.log('predict_record_id: ' + props.message?.record?.predict_record_id)
}
</script>
<template>
<div class="question">
<span v-if="message?.record?.analysis_record_id" class="prefix-title" @click="clickAnalysis">
{{ t('qa.data_analysis') }}
</span>
<span v-else-if="message?.record?.predict_record_id" class="prefix-title" @click="clickPredict">
{{ t('qa.data_predict') }}
</span>
<span>{{ message?.content }}</span>
</div>
</template>
<style scoped lang="less">
.question {
display: flex;
flex-direction: row;
gap: 8px;
border-radius: 16px;
height: 48px;
line-height: 24px;
font-size: 16px;
padding: 12px 16px;
color: rgba(31, 35, 41, 1);
background: rgba(245, 246, 247, 1);
word-wrap: break-word;
white-space: pre-wrap;
.prefix-title {
color: rgba(28, 186, 144, 1);
}
}
</style>
+123 -157
View File
@@ -1,36 +1,51 @@
<template>
<el-container class="chat-container no-padding">
<el-aside v-if="!isAssistant" class="chat-container-left">
<el-container class="chat-container-right-container">
<el-header class="chat-list-header">
<div class="title">
<div>智能问数</div>
<el-button link type="primary" class="icon-btn">
<el-icon>
<icon_sidebar_outlined />
</el-icon>
</el-button>
</div>
<el-button class="btn" type="primary" @click="createNewChat">
<el-icon>
<Plus />
</el-icon>
{{ t('qa.New Conversation') }}
</el-button>
<el-input v-model="search" class="search" placeholder="placeholder" />
</el-header>
<el-main class="chat-list">
<ChatList
v-model:loading="loading"
:current-chat-id="currentChatId"
:chat-list="chatList"
@chat-selected="onClickHistory"
@chat-deleted="onChatDeleted"
@chat-renamed="onChatRenamed"
/>
</el-main>
</el-container>
<el-aside v-if="!isAssistant && chatListSideBarShow" class="chat-container-left">
<ChatListContainer
v-model:chat-list="chatList"
v-model:current-chat-id="currentChatId"
v-model:current-chat="currentChat"
v-model:loading="loading"
:in-popover="!chatListSideBarShow"
@go-empty="goEmpty"
@on-chat-created="onChatCreated"
@on-click-history="onClickHistory"
@on-chat-deleted="onChatDeleted"
@on-chat-renamed="onChatRenamed"
@on-click-side-bar-btn="hideSideBar"
/>
</el-aside>
<div v-if="!isAssistant && !chatListSideBarShow" class="hidden-sidebar-btn">
<el-popover :width="280" placement="bottom-start" popper-style="padding: 0; height: 654px">
<template #reference>
<el-button link type="primary" class="icon-btn" @click="showSideBar">
<el-icon>
<icon_sidebar_outlined />
</el-icon>
</el-button>
</template>
<ChatListContainer
v-model:chat-list="chatList"
v-model:current-chat-id="currentChatId"
v-model:current-chat="currentChat"
v-model:loading="loading"
:in-popover="!chatListSideBarShow"
@go-empty="goEmpty"
@on-chat-created="onChatCreated"
@on-click-history="onClickHistory"
@on-chat-deleted="onChatDeleted"
@on-chat-renamed="onChatRenamed"
@on-click-side-bar-btn="hideSideBar"
/>
</el-popover>
<el-tooltip effect="dark" :content="t('qa.new_chat')" placement="bottom">
<el-button link type="primary" class="icon-btn" @click="createNewChat">
<el-icon>
<icon_new_chat_outlined />
</el-icon>
</el-button>
</el-tooltip>
</div>
<el-container :loading="loading">
<el-main class="chat-record-list">
<div v-if="computedMessages.length == 0" class="welcome-content-block">
@@ -47,14 +62,21 @@
<el-icon>
<Plus />
</el-icon>
{{ t('qa.New Conversation') }}
{{ t('qa.new_chat') }}
</el-button>
</div>
</div>
<el-scrollbar v-if="computedMessages.length > 0" ref="chatListRef">
<template v-for="(message, _index) in computedMessages" :key="_index">
<ChatRow :current-chat="currentChat" :msg="message">
<template v-if="message.role === 'assistant'">
<ChatRow :current-chat="currentChat" :msg="message" :hide-avatar="message.first_chat">
<RecommendQuestion
v-if="message.role === 'assistant' && message.first_chat"
:questions="message.recommended_question"
:first-chat="message.first_chat"
@click-question="quickAsk"
/>
<UserChat v-if="message.role === 'user'" :message="message" />
<template v-if="message.role === 'assistant' && !message.first_chat">
<ChatAnswer
v-if="
message?.record?.analysis_record_id == undefined &&
@@ -83,39 +105,41 @@
</div>
</template>
</ChatAnswer>
</template>
<div
v-if="
message?.record?.analysis_record_id != undefined ||
message?.record?.predict_record_id != undefined
"
class="analysis-container"
>
<template v-if="message.record?.analysis || isAnalysisTyping">
<MdComponent :message="message.record?.analysis_thinking" />
<MdComponent :message="message.record?.analysis" />
</template>
<el-divider
<div
v-if="
(message.record?.analysis || isAnalysisTyping) &&
(message.record?.predict || isPredictTyping)
message?.record?.analysis_record_id != undefined ||
message?.record?.predict_record_id != undefined
"
/>
<template v-if="message.record?.predict || isPredictTyping">
<MdComponent :message="message.record?.predict" />
<MdComponent :message="message.record?.predict_content" />
<!-- <PredictChartBlock-->
<!-- :id="message.record?.id + '-predict'"-->
<!-- :data="message.record?.predict_data ?? '[]'"-->
<!-- :message="message"-->
<!-- :chart-type="data.chartType"-->
<!-- />-->
</template>
</div>
class="analysis-container"
>
<template v-if="message.record?.analysis || isAnalysisTyping">
<MdComponent :message="message.record?.analysis_thinking" />
<MdComponent :message="message.record?.analysis" />
</template>
<el-divider
v-if="
(message.record?.analysis || isAnalysisTyping) &&
(message.record?.predict || isPredictTyping)
"
/>
<template v-if="message.record?.predict || isPredictTyping">
<MdComponent :message="message.record?.predict" />
<MdComponent :message="message.record?.predict_content" />
<!-- <PredictChartBlock-->
<!-- :id="message.record?.id + '-predict'"-->
<!-- :data="message.record?.predict_data ?? '[]'"-->
<!-- :message="message"-->
<!-- :chart-type="data.chartType"-->
<!-- />-->
</template>
</div>
</template>
<template v-if="message.role === 'assistant'" #footer>
<RecommendQuestion
:questions="message.recommended_question"
:first-chat="message.first_chat"
@click-question="quickAsk"
/>
</template>
@@ -159,7 +183,7 @@
</el-footer>
</el-container>
<ChatCreator v-if="!isAssistant" ref="chatCreatorRef" @on-chat-created="onChatCreated" />
<ChatCreator v-if="!isAssistant" ref="chatCreatorRef" @on-chat-created="onChatCreatedQuick" />
</el-container>
</template>
@@ -167,16 +191,17 @@
import { computed, nextTick, onMounted, ref } from 'vue'
import { Plus, Position } from '@element-plus/icons-vue'
import { Chat, chatApi, ChatInfo, type ChatMessage, ChatRecord, questionApi } from '@/api/chat'
import ChatList from './ChatList.vue'
import ChatRow from './ChatRow.vue'
import ChatAnswer from './ChatAnswer.vue'
import UserChat from './chat-block/UserChat.vue'
import MdComponent from './component/MdComponent.vue'
// import PredictChartBlock from './component/PredictChartBlock.vue'
import RecommendQuestion from './RecommendQuestion.vue'
import ChatCreator from './ChatCreator.vue'
import ChatListContainer from './ChatListContainer.vue'
import ChatCreator from '@/views/chat/ChatCreator.vue'
import { useI18n } from 'vue-i18n'
import icon_sidebar_outlined from '@/assets/svg/icon_sidebar_outlined.svg'
import { endsWith, find, startsWith } from 'lodash-es'
import icon_new_chat_outlined from '@/assets/svg/icon_new_chat_outlined.svg'
import icon_sidebar_outlined from '@/assets/svg/icon_sidebar_outlined.svg'
import { useAssistantStore } from '@/stores/assistant'
const assistantStore = useAssistantStore()
@@ -219,6 +244,7 @@ const computedMessages = computed<Array<ChatMessage>>(() => {
messages.push({
role: 'user',
create_time: record.create_time,
record: record,
content: record.question,
})
}
@@ -236,13 +262,9 @@ const computedMessages = computed<Array<ChatMessage>>(() => {
})
const goEmpty = () => {
currentChat.value = new ChatInfo()
currentChatId.value = undefined
inputMessage.value = ''
}
const search = ref<string>()
const createNewChat = async () => {
goEmpty()
if (isAssistant.value) {
@@ -252,6 +274,7 @@ const createNewChat = async () => {
}
return
}
console.log(chatCreatorRef.value)
chatCreatorRef.value?.showDs()
}
@@ -268,70 +291,41 @@ function getChatList() {
}
function onClickHistory(chat: Chat) {
currentChat.value = new ChatInfo(chat)
if (chat !== undefined && chat.id !== undefined && !loading.value) {
currentChatId.value = chat.id
loading.value = true
chatApi
.get(chat.id)
.then((res) => {
const info = chatApi.toChatInfo(res)
if (info) {
currentChat.value = info
scrollToBottom()
}
})
.finally(() => {
loading.value = false
})
}
console.log('click history', chat)
scrollToBottom()
}
function onChatDeleted(id: number) {
for (let i = 0; i < chatList.value.length; i++) {
if (chatList.value[i].id === id) {
chatList.value.splice(i, 1)
break
}
}
if (id === currentChatId.value) {
goEmpty()
}
console.log('deleted', id)
}
function onChatRenamed(chat: Chat) {
chatList.value.forEach((c: Chat) => {
if (c.id === chat.id) {
c.brief = chat.brief
}
})
if (currentChat.value.id === chat.id) {
currentChat.value.brief = chat.brief
}
console.log('renamed', chat)
}
function onChatCreated(chat: ChatInfo) {
const chatListSideBarShow = ref<boolean>(true)
function hideSideBar() {
chatListSideBarShow.value = false
}
function showSideBar() {
chatListSideBarShow.value = true
}
function onChatCreatedQuick(chat: ChatInfo) {
chatList.value.unshift(chat)
currentChatId.value = chat.id
currentChat.value = chat
onChatCreated(chat)
}
function onChatCreated(chat: ChatInfo) {
if (chat.records.length === 1) {
getRecommendQuestions(chat.records[0].id)
}
}
async function getRecommendQuestions(record_id?: number) {
/*chatApi.recommendQuestions(record_id).then((res) => {
if (res && res.length > 0 && startsWith(res.trim(), '[') && endsWith(res.trim(), ']')) {
if (currentChat.value?.records) {
for (let record of currentChat.value.records) {
if (record.id === record_id) {
record.recommended_question = res
}
}
}
}
})*/
const response = await chatApi.recommendQuestions(record_id)
const reader = response.body.getReader()
const decoder = new TextDecoder()
@@ -827,6 +821,15 @@ defineExpose({
<style lang="less" scoped>
.chat-container {
height: 100%;
position: relative;
.hidden-sidebar-btn {
z-index: 1;
position: absolute;
padding: 16px;
top: 0;
left: 0;
}
.icon-btn {
min-width: unset;
@@ -834,6 +837,10 @@ defineExpose({
height: 26px;
font-size: 18px;
--ed-button-text-color: rgba(31, 35, 41, 1);
--ed-button-hover-text-color: var(--ed-button-text-color);
--ed-button-active-text-color: var(--ed-button-text-color);
--ed-button-hover-link-text-color: var(--ed-button-text-color);
&:hover {
background: rgba(31, 35, 41, 0.1);
}
@@ -842,49 +849,8 @@ defineExpose({
.chat-container-left {
--ed-aside-width: 280px;
border-radius: 12px 0 0 12px;
//box-shadow: 0 0 3px #d7d7d7;
//z-index: 1;
background: rgba(245, 246, 247, 1);
.chat-container-right-container {
height: 100%;
.chat-list-header {
--ed-header-padding: 16px;
--ed-header-height: calc(16px + 24px + 16px + 40px + 16px + 32px + 16px);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 16px;
.title {
height: 24px;
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
font-weight: 500;
}
.btn {
width: 100%;
height: 40px;
}
.search {
height: 32px;
width: 100%;
}
}
.chat-list {
padding: 0 0 20px 0;
}
}
}
.chat-record-list {