11<template >
2- <h2 >this is chatbi page</h2 >
3- </template >
2+ <div class =" chat-container" >
3+ <!-- Sidebar for chat history -->
4+ <div class =" chat-sidebar" >
5+ <div class =" sidebar-header" >
6+ <el-button type =" primary" class =" new-chat" @click =" createNewChat" >
7+ <el-icon ><Plus /></el-icon >
8+ New Conversation
9+ </el-button >
10+ <div class =" search-box" >
11+ <el-input
12+ v-model =" searchKeyword"
13+ placeholder =" Search conversation history"
14+ :prefix-icon =" Search"
15+ />
16+ </div >
17+ </div >
18+
19+ <div class =" history-list" >
20+ <div
21+ v-for =" chat in filteredHistory"
22+ :key =" chat.id"
23+ class =" history-item"
24+ :class =" { active: currentChatId === chat.id }"
25+ @click =" switchChat(chat.id)"
26+ >
27+ <span class =" chat-title" >{{ chat.title }}</span >
28+ <el-dropdown trigger =" hover" @command =" handleCommand($event, chat.id)" >
29+ <span class =" more-actions" >
30+ <el-icon ><More /></el-icon >
31+ </span >
32+ <template #dropdown >
33+ <el-dropdown-menu >
34+ <el-dropdown-item command =" rename" >Rename</el-dropdown-item >
35+ <el-dropdown-item command =" delete" divided >Delete</el-dropdown-item >
36+ </el-dropdown-menu >
37+ </template >
38+ </el-dropdown >
39+ </div >
40+ </div >
41+ </div >
42+
43+ <!-- Main chat area -->
44+ <div class =" chat-main" >
45+ <div class =" chat-messages" ref =" messagesRef" >
46+ <template v-if =" currentChat ?.messages ?.length " >
47+ <div
48+ v-for =" (msg, index) in currentChat.messages"
49+ :key =" index"
50+ class =" message"
51+ :class =" msg.role"
52+ >
53+ <div class =" message-content" >{{ msg.content }}</div >
54+ </div >
55+ </template >
56+ <div v-else class =" empty-state" >
57+ <p >Welcome to SQLBot</p >
58+ <p class =" sub-text" >You can ask questions in natural language, for example:</p >
59+ <div class =" examples" >
60+ <div class =" example-item" >View this month's sales growth compared to last month</div >
61+ <div class =" example-item" >Analyze sales staff performance by region</div >
62+ <div class =" example-item" >Calculate the relationship between customer purchase frequency and average order value</div >
63+ </div >
64+ </div >
65+ </div >
66+
67+ <div class =" chat-input" >
68+ <div class =" input-wrapper" >
69+ <el-input
70+ v-model =" inputMessage"
71+ type =" textarea"
72+ :rows =" 1"
73+ :autosize =" { minRows: 1, maxRows: 8 }"
74+ placeholder =" Press Enter to send, Ctrl + Enter for new line"
75+ @keydown.enter.exact.prevent =" sendMessage"
76+ @keydown.ctrl.enter.exact.prevent =" handleCtrlEnter"
77+ />
78+ <div class =" input-actions" >
79+ <el-button circle type =" primary" class =" send-btn" @click =" sendMessage" >
80+ <el-icon ><Position /></el-icon >
81+ </el-button >
82+ </div >
83+ </div >
84+ </div >
85+ </div >
86+ </div >
87+ </template >
88+
89+ <script setup lang="ts">
90+ import { ref , computed , nextTick } from ' vue'
91+ import { Plus , Search , More , Position } from ' @element-plus/icons-vue'
92+
93+ interface ChatMessage {
94+ role: ' user' | ' assistant'
95+ content: string
96+ }
97+
98+ interface Chat {
99+ id: string
100+ title: string
101+ messages: ChatMessage []
102+ }
103+
104+ const chatHistory = ref <Chat []>([])
105+ const currentChatId = ref (' ' )
106+ const searchKeyword = ref (' ' )
107+ const inputMessage = ref (' ' )
108+
109+ const filteredHistory = computed (() => {
110+ return chatHistory .value .filter (chat =>
111+ chat .title .toLowerCase ().includes (searchKeyword .value .toLowerCase ())
112+ )
113+ })
114+
115+ const currentChat = computed (() => {
116+ return chatHistory .value .find (chat => chat .id === currentChatId .value )
117+ })
118+
119+ const createNewChat = () => {
120+ const newChat: Chat = {
121+ id: Date .now ().toString (),
122+ title: ` New Chat ${chatHistory .value .length + 1 } ` ,
123+ messages: []
124+ }
125+ chatHistory .value .unshift (newChat )
126+ currentChatId .value = newChat .id
127+ }
128+
129+ const switchChat = (chatId : string ) => {
130+ currentChatId .value = chatId
131+ }
132+
133+ const sendMessage = () => {
134+ if (! inputMessage .value .trim ()) return
135+
136+ const chat = currentChat .value
137+ if (! chat ) return
138+
139+ chat .messages .push ({
140+ role: ' user' ,
141+ content: inputMessage .value
142+ })
143+
144+
145+ inputMessage .value = ' '
146+ }
147+
148+ const handleCommand = (command : string , chatId : string ) => {
149+ switch (command ) {
150+ case ' rename' :
151+ break
152+ case ' delete' :
153+ chatHistory .value = chatHistory .value .filter (chat => chat .id !== chatId )
154+ if (currentChatId .value === chatId ) {
155+ currentChatId .value = chatHistory .value [0 ]?.id || ' '
156+ }
157+ break
158+ }
159+ }
160+
161+ const handleCtrlEnter = (e : KeyboardEvent ) => {
162+ const textarea = e .target as HTMLTextAreaElement
163+ const start = textarea .selectionStart
164+ const end = textarea .selectionEnd
165+ const value = textarea .value
166+
167+ inputMessage .value = value .substring (0 , start ) + ' \n ' + value .substring (end )
168+
169+ nextTick (() => {
170+ textarea .selectionStart = textarea .selectionEnd = start + 1
171+ })
172+ }
173+ </script >
174+
175+ <style lang="less" scoped>
176+ .chat-container {
177+ height : 100% ;
178+ display : flex ;
179+ background-color : var (--white );
180+ border-radius : var (--border-radius );
181+ box-shadow : var (--shadow );
182+ overflow : hidden ;
183+
184+ .chat-sidebar {
185+ width : 260px ;
186+ border-right : 1px solid #e6e6e6 ;
187+ display : flex ;
188+ flex-direction : column ;
189+
190+ .sidebar-header {
191+ padding : 16px ;
192+ border-bottom : 1px solid #e6e6e6 ;
193+
194+ .new-chat {
195+ width : 100% ;
196+ margin-bottom : 12px ;
197+ }
198+
199+ .search-box {
200+ :deep(.el-input__wrapper ) {
201+ background-color : #f5f5f5 ;
202+ }
203+ }
204+ }
205+
206+ .history-list {
207+ flex : 1 ;
208+ overflow-y : auto ;
209+ padding : 8px ;
210+
211+ .history-item {
212+ display : flex ;
213+ align-items : center ;
214+ justify-content : space-between ;
215+ padding : 8px 12px ;
216+ margin-bottom : 4px ;
217+ border-radius : 6px ;
218+ cursor : pointer ;
219+
220+ & :hover {
221+ background-color : #f5f5f5 ;
222+ }
223+
224+ & .active {
225+ background-color : #e6f4ff ;
226+ }
227+
228+ .chat-title {
229+ flex : 1 ;
230+ overflow : hidden ;
231+ text-overflow : ellipsis ;
232+ white-space : nowrap ;
233+ }
234+
235+ .more-actions {
236+ opacity : 0 ;
237+ padding : 4px ;
238+ }
239+
240+ & :hover .more-actions {
241+ opacity : 1 ;
242+ }
243+ }
244+ }
245+ }
246+
247+ .chat-main {
248+ flex : 1 ;
249+ display : flex ;
250+ flex-direction : column ;
251+
252+ .chat-messages {
253+ flex : 1 ;
254+ overflow-y : auto ;
255+ padding : 24px ;
256+
257+ .message {
258+ margin-bottom : 24px ;
259+
260+ & .user {
261+ text-align : right ;
262+ .message-content {
263+ background-color : #e6f4ff ;
264+ }
265+ }
266+
267+ & .assistant .message-content {
268+ background-color : #f5f5f5 ;
269+ }
270+
271+ .message-content {
272+ display : inline-block ;
273+ padding : 12px 16px ;
274+ border-radius : 8px ;
275+ max-width : 80% ;
276+ }
277+ }
278+
279+ .empty-state {
280+ text-align : center ;
281+ color : #666 ;
282+ padding : 40px 0 ;
283+
284+ .sub-text {
285+ margin : 16px 0 ;
286+ }
287+
288+ .examples {
289+ .example-item {
290+ background-color : #f5f5f5 ;
291+ padding : 12px ;
292+ margin : 8px auto ;
293+ max-width : 400px ;
294+ border-radius : 8px ;
295+ cursor : pointer ;
296+
297+ & :hover {
298+ background-color : #e6f4ff ;
299+ }
300+ }
301+ }
302+ }
303+ }
304+
305+ .chat-input {
306+ padding : 16px 24px ;
307+ border-top : 1px solid var (--el-border-color-lighter );
308+ background : #fff ;
309+
310+ .input-wrapper {
311+ position : relative ;
312+ border-radius : 12px ;
313+ border : 1px solid var (--el-border-color );
314+ background : #f5f5f5 ;
315+ transition : all 0.3s ;
316+
317+ & :hover , & :focus-within {
318+ border-color : var (--el-border-color-darker );
319+ box-shadow : 0 2px 8px rgba (0 ,0 ,0 ,0.06 );
320+ }
321+
322+ :deep(.el-textarea__inner ) {
323+ padding : 16px ;
324+ font-size : 14px ;
325+ // line-height: 1.6;
326+ // min-height: 80px;
327+ resize : none ;
328+ border : none ;
329+ background : transparent ;
330+ box-shadow : none !important ;
331+
332+ & :focus {
333+ box-shadow : none !important ;
334+ }
335+ }
336+
337+ :deep(.el-textarea__wrapper ) {
338+ box-shadow : none !important ;
339+ padding : 0 ;
340+ background : transparent ;
341+ }
342+ .input-actions {
343+ display : flex ;
344+ align-items : center ;
345+ justify-content : end ;
346+ height : 36px ;
347+ padding : 8px ;
348+ }
349+ }
350+
351+
352+ }
353+ }
354+ }
355+ </style >
0 commit comments