Skip to content

Commit 72b6e11

Browse files
committed
feat: split create new chat step
1 parent 8968f68 commit 72b6e11

File tree

5 files changed

+228
-34
lines changed

5 files changed

+228
-34
lines changed

backend/apps/chat/api/chat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ async def start_chat(session: SessionDep, current_user: CurrentUser, create_chat
6363
return create_chat(session, current_user, create_chat_obj)
6464
except Exception as e:
6565
raise HTTPException(
66-
status_code=400,
66+
status_code=500,
6767
detail=str(e)
6868
)
6969

backend/apps/chat/curd/chat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def create_chat(session: SessionDep, current_user: CurrentUser, create_chat_obj:
7979
raise Exception("Datasource cannot be None")
8080

8181
if not create_chat_obj.question or create_chat_obj.question.strip() == '':
82-
raise Exception("Question cannot be Empty")
82+
create_chat_obj.question = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
8383

8484
chat = Chat(create_time=datetime.datetime.now(),
8585
create_by=current_user.id,

backend/apps/chat/models/chat_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class ChatRecord(SQLModel, table=True):
5252

5353
class CreateChat(BaseModel):
5454
id: int = None
55-
question: str = ''
55+
question: str = None
5656
datasource: int = None
5757

5858

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<script setup lang="ts">
2+
import { chatApi, ChatInfo } from '@/api/chat.ts'
3+
import { onMounted, ref } from 'vue'
4+
import { datasourceApi } from '@/api/datasource.ts'
5+
import DatasourceItemCard from '../ds/DatasourceItemCard.vue'
6+
7+
const dsList = ref<Array<any>>([])
8+
9+
const emits = defineEmits(['onChatCreated'])
10+
11+
function listDs() {
12+
datasourceApi.list().then((res) => {
13+
dsList.value = res
14+
})
15+
}
16+
17+
const dialogVisible = ref(false)
18+
19+
const DatasourceListRef = ref()
20+
21+
const innerDs = ref()
22+
23+
const loading = ref(false)
24+
25+
function showDs() {
26+
listDs()
27+
dialogVisible.value = true
28+
}
29+
30+
function hideDs() {
31+
innerDs.value = undefined
32+
dialogVisible.value = false
33+
}
34+
35+
function selectDsInDialog(ds: any) {
36+
innerDs.value = ds.id
37+
}
38+
39+
function confirmSelectDs() {
40+
if (innerDs.value) {
41+
createChat(innerDs.value)
42+
}
43+
}
44+
45+
function createChat(datasource: number) {
46+
loading.value = true
47+
chatApi
48+
.startChat({
49+
datasource: datasource,
50+
})
51+
.then((res) => {
52+
const chat: ChatInfo | undefined = chatApi.toChatInfo(res)
53+
if (chat == undefined) {
54+
throw Error('chat is undefined')
55+
}
56+
emits('onChatCreated', chat)
57+
hideDs()
58+
})
59+
.catch((e) => {
60+
console.error(e)
61+
})
62+
.finally(() => {
63+
loading.value = false
64+
})
65+
}
66+
67+
onMounted(() => {
68+
listDs()
69+
})
70+
71+
defineExpose({
72+
showDs,
73+
hideDs,
74+
})
75+
</script>
76+
77+
<template>
78+
<div>
79+
<el-drawer
80+
ref="DatasourceListRef"
81+
v-model="dialogVisible"
82+
direction="btt"
83+
:close-on-press-escape="false"
84+
:close-on-click-modal="false"
85+
destroy-on-close
86+
:show-close="false"
87+
size="100%"
88+
>
89+
<template #header>
90+
<div>
91+
<div>Choose Datasource</div>
92+
</div>
93+
</template>
94+
<el-scrollbar v-loading="loading">
95+
<div class="ds-row-container">
96+
<template v-for="(item, _index) in dsList" :key="_index">
97+
<DatasourceItemCard
98+
:ds="item"
99+
class="ds-card"
100+
:class="[item?.id === innerDs ? 'ds-card-selected' : '']"
101+
@click="selectDsInDialog(item)"
102+
/>
103+
</template>
104+
</div>
105+
</el-scrollbar>
106+
<template #footer>
107+
<div class="dialog-footer">
108+
<el-button :disabled="loading" @click="hideDs">Cancel</el-button>
109+
<el-button
110+
type="primary"
111+
:disabled="loading || innerDs === undefined"
112+
@click="confirmSelectDs"
113+
>
114+
Confirm
115+
</el-button>
116+
</div>
117+
</template>
118+
</el-drawer>
119+
</div>
120+
</template>
121+
122+
<style scoped lang="less">
123+
.welcome-content {
124+
padding: 12px;
125+
}
126+
127+
.sub {
128+
color: grey;
129+
font-size: 0.8em;
130+
}
131+
132+
.ds-select-row {
133+
display: flex;
134+
align-items: center;
135+
flex-direction: row;
136+
justify-content: space-between;
137+
}
138+
139+
.ds-row-container {
140+
display: grid;
141+
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
142+
gap: 24px;
143+
}
144+
145+
.ds-card {
146+
cursor: pointer;
147+
}
148+
149+
.ds-card-selected {
150+
box-shadow: 0 1px 3px var(--ed-color-primary-light-5);
151+
border: 1px solid var(--ed-color-primary-light-5);
152+
}
153+
</style>

frontend/src/views/chat/index.vue

Lines changed: 72 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,20 @@
2424
</el-aside>
2525
<el-container :loading="loading">
2626
<el-main class="chat-record-list">
27-
<el-scrollbar ref="chatListRef">
27+
<div v-if="computedMessages.length == 0" class="welcome-content-block">
28+
<div class="welcome-content">
29+
<div class="logo">SQLBot</div>
30+
<div>{{ t('qa.greeting') }}</div>
31+
<div class="sub">{{ t('qa.description') }}</div>
32+
<el-button size="large" type="primary" @click="createNewChat">
33+
<el-icon>
34+
<Plus />
35+
</el-icon>
36+
{{ t('qa.New Conversation') }}
37+
</el-button>
38+
</div>
39+
</div>
40+
<el-scrollbar v-if="computedMessages.length > 0" ref="chatListRef">
2841
<template v-for="(message, _index) in computedMessages" :key="_index">
2942
<ChatRow
3043
v-model:datasource="currentChat.datasource"
@@ -83,7 +96,7 @@
8396
</template>
8497
</el-scrollbar>
8598
</el-main>
86-
<el-footer class="chat-footer">
99+
<el-footer v-if="computedMessages.length > 0" class="chat-footer">
87100
<div style="height: 24px">
88101
<template v-if="currentChat.datasource && currentChat.datasource_name">
89102
{{ t('ds.title') }}:{{ currentChat.datasource_name }}
@@ -115,6 +128,8 @@
115128
</div>
116129
</el-footer>
117130
</el-container>
131+
132+
<ChatCreator ref="chatCreatorRef" @on-chat-created="onChatCreated" />
118133
</el-container>
119134
</template>
120135

@@ -127,6 +142,7 @@ import ChatRow from './ChatRow.vue'
127142
import ChatAnswer from './ChatAnswer.vue'
128143
import MdComponent from './component/MdComponent.vue'
129144
import PredictChartBlock from './component/PredictChartBlock.vue'
145+
import ChatCreator from './ChatCreator.vue'
130146
import { useI18n } from 'vue-i18n'
131147
import { find } from 'lodash-es'
132148
@@ -135,6 +151,7 @@ const { t } = useI18n()
135151
const inputMessage = ref('')
136152
137153
const chatListRef = ref()
154+
const chatCreatorRef = ref()
138155
139156
function scrollToBottom() {
140157
nextTick(() => {
@@ -155,17 +172,18 @@ const isAnalysisTyping = ref<boolean>(false)
155172
const isPredictTyping = ref<boolean>(false)
156173
157174
const computedMessages = computed<Array<ChatMessage>>(() => {
158-
const welcome: ChatMessage = {
175+
const firstMessage: ChatMessage = {
159176
role: 'assistant',
160177
create_time: currentChat.value?.create_time,
161178
content: currentChat.value?.datasource,
162179
isTyping: false,
163180
isWelcome: true,
164181
}
165-
const messages: Array<ChatMessage> = [welcome]
182+
const messages: Array<ChatMessage> = []
166183
if (currentChatId.value === undefined) {
167184
return messages
168185
}
186+
messages.push(firstMessage)
169187
for (let i = 0; i < currentChat.value.records.length; i++) {
170188
const record = currentChat.value.records[i]
171189
if (record.question !== undefined) {
@@ -186,12 +204,17 @@ const computedMessages = computed<Array<ChatMessage>>(() => {
186204
return messages
187205
})
188206
189-
const createNewChat = () => {
207+
const goEmpty = () => {
190208
currentChat.value = new ChatInfo()
191209
currentChatId.value = undefined
192210
inputMessage.value = ''
193211
}
194212
213+
const createNewChat = () => {
214+
goEmpty()
215+
chatCreatorRef.value?.showDs()
216+
}
217+
195218
function getChatList() {
196219
loading.value = true
197220
chatApi
@@ -229,9 +252,12 @@ function onChatDeleted(id: number) {
229252
for (let i = 0; i < chatList.value.length; i++) {
230253
if (chatList.value[i].id === id) {
231254
chatList.value.splice(i, 1)
232-
return
255+
break
233256
}
234257
}
258+
if (id === currentChatId.value) {
259+
goEmpty()
260+
}
235261
}
236262
237263
function onChatRenamed(chat: Chat) {
@@ -245,6 +271,12 @@ function onChatRenamed(chat: Chat) {
245271
}
246272
}
247273
274+
function onChatCreated(chat: ChatInfo) {
275+
chatList.value.unshift(chat)
276+
currentChatId.value = chat.id
277+
currentChat.value = chat
278+
}
279+
248280
onMounted(() => {
249281
getChatList()
250282
})
@@ -268,32 +300,9 @@ const sendMessage = async () => {
268300
currentChat.value.records.push(currentRecord)
269301
inputMessage.value = ''
270302
271-
let error = false
303+
let error: boolean = false
272304
if (currentChatId.value === undefined) {
273-
await chatApi
274-
.startChat({
275-
question: currentRecord.question.trim(),
276-
datasource: currentChat.value.datasource,
277-
})
278-
.then((res) => {
279-
const chat = chatApi.toChatInfo(res)
280-
if (chat !== undefined) {
281-
chatList.value.unshift(chat)
282-
currentChatId.value = chat.id
283-
chat.records.push(currentRecord)
284-
currentChat.value = chat
285-
} else {
286-
error = true
287-
}
288-
})
289-
.catch((e) => {
290-
isTyping.value = false
291-
error = true
292-
console.error(e)
293-
})
294-
.finally(() => {
295-
loading.value = false
296-
})
305+
error = true
297306
}
298307
if (error) return
299308
@@ -683,4 +692,36 @@ const handleCtrlEnter = (e: KeyboardEvent) => {
683692
line-height: 1.7692307692;
684693
padding: 16px 22px;
685694
}
695+
696+
.welcome-content-block {
697+
height: 100%;
698+
width: 100%;
699+
700+
display: flex;
701+
justify-content: center;
702+
align-items: center;
703+
704+
.welcome-content {
705+
padding: 12px;
706+
707+
width: fit-content;
708+
display: flex;
709+
gap: 16px;
710+
align-items: center;
711+
flex-direction: column;
712+
713+
.logo {
714+
line-height: 60px;
715+
font-size: 3em;
716+
font-weight: bold;
717+
color: var(--el-color-primary);
718+
text-align: left;
719+
}
720+
721+
.sub {
722+
color: grey;
723+
font-size: 0.8em;
724+
}
725+
}
726+
}
686727
</style>

0 commit comments

Comments
 (0)