1
1
<!-- AI 对话 -->
2
2
<template >
3
- <el-aside width =" 260px" class =" conversation-container" style =" height : 100% ;" >
4
-
3
+ <el-aside width =" 260px" class =" conversation-container" style =" height : 100% " >
5
4
<!-- 左顶部:对话 -->
6
- <div style =" height : 100% ; " >
5
+ <div style =" height : 100% " >
7
6
<el-button class =" w-1/1 btn-new-conversation" type =" primary" @click =" createConversation" >
8
- <Icon icon =" ep:plus" class =" mr-5px" />
7
+ <Icon icon =" ep:plus" class =" mr-5px" />
9
8
新建对话
10
9
</el-button >
11
10
18
17
@keyup =" searchConversation"
19
18
>
20
19
<template #prefix >
21
- <Icon icon =" ep:search" />
20
+ <Icon icon =" ep:search" />
22
21
</template >
23
22
</el-input >
24
23
25
24
<!-- 左中间:对话列表 -->
26
25
<div class =" conversation-list" >
27
-
28
26
<el-empty v-if =" loading" description =" ." :v-loading =" loading" />
29
27
30
28
<div v-for =" conversationKey in Object.keys(conversationMap)" :key =" conversationKey" >
31
- <div class =" conversation-item classify-title" v-if =" conversationMap[conversationKey].length" >
29
+ <div
30
+ class =" conversation-item classify-title"
31
+ v-if =" conversationMap[conversationKey].length"
32
+ >
32
33
<el-text class =" mx-1" size =" small" tag =" b" >{{ conversationKey }}</el-text >
33
34
</div >
34
35
<div
40
41
@mouseout =" hoverConversationId = ''"
41
42
>
42
43
<div
43
- :class =" conversation.id === activeConversationId ? 'conversation active' : 'conversation'"
44
+ :class ="
45
+ conversation.id === activeConversationId ? 'conversation active' : 'conversation'
46
+ "
44
47
>
45
48
<div class =" title-wrapper" >
46
- <img class =" avatar" :src =" conversation.roleAvatar || roleAvatarDefaultImg" />
49
+ <img class =" avatar" :src =" conversation.roleAvatar || roleAvatarDefaultImg" />
47
50
<span class =" title" >{{ conversation.title }}</span >
48
51
</div >
49
52
<div class =" button-wrapper" v-show =" hoverConversationId === conversation.id" >
50
- <el-button class =" btn" link @click.stop =" handlerTop(conversation)" >
53
+ <el-button class =" btn" link @click.stop =" handlerTop(conversation)" >
51
54
<el-icon title =" 置顶" v-if =" !conversation.pinned" ><Top /></el-icon >
52
55
<el-icon title =" 置顶" v-if =" conversation.pinned" ><Bottom /></el-icon >
53
56
</el-button >
54
57
<el-button class =" btn" link @click.stop =" updateConversationTitle(conversation)" >
55
- <el-icon title =" 编辑" >
56
- <Icon icon =" ep:edit" />
58
+ <el-icon title =" 编辑" >
59
+ <Icon icon =" ep:edit" />
57
60
</el-icon >
58
61
</el-button >
59
62
<el-button class =" btn" link @click.stop =" deleteChatConversation(conversation)" >
60
- <el-icon title =" 删除对话" >
61
- <Icon icon =" ep:delete" />
63
+ <el-icon title =" 删除对话" >
64
+ <Icon icon =" ep:delete" />
62
65
</el-icon >
63
66
</el-button >
64
67
</div >
65
68
</div >
66
69
</div >
67
70
</div >
68
71
<!-- 底部站位 -->
69
- <div style =" height : 160px ; width : 100% ; " ></div >
72
+ <div style =" height : 160px ; width : 100% " ></div >
70
73
</div >
71
-
72
74
</div >
73
75
74
76
<!-- 左底部:工具栏 -->
75
77
<!-- TODO @fan:下面两个 icon,可以使用类似 <Icon icon="ep:question-filled" /> 替代哈 -->
76
78
<div class =" tool-box" >
77
79
<div @click =" handleRoleRepository" >
78
- <Icon icon =" ep:user" />
80
+ <Icon icon =" ep:user" />
79
81
<el-text size =" small" >角色仓库</el-text >
80
82
</div >
81
83
<div @click =" handleClearConversation" >
82
- <Icon icon =" ep:delete" />
84
+ <Icon icon =" ep:delete" />
83
85
<el-text size =" small" >清空未置顶对话</el-text >
84
86
</div >
85
87
</div >
88
90
89
91
<!-- 角色仓库抽屉 -->
90
92
<el-drawer v-model =" drawer" title =" 角色仓库" size =" 754px" >
91
- <Role />
93
+ <Role />
92
94
</el-drawer >
93
-
94
95
</el-aside >
95
96
</template >
96
97
97
98
<script setup lang="ts">
98
- import {ChatConversationApi , ChatConversationVO } from ' @/api/ai/chat/conversation'
99
- import {ref } from " vue" ;
100
- import Role from " @/views/ai/chat/role/index.vue" ;
101
- import {Bottom , Top } from " @element-plus/icons-vue" ;
99
+ import { ChatConversationApi , ChatConversationVO } from ' @/api/ai/chat/conversation'
100
+ import { ref } from ' vue'
101
+ import Role from ' @/views/ai/chat/role/index.vue'
102
+ import { Bottom , Top } from ' @element-plus/icons-vue'
102
103
import roleAvatarDefaultImg from ' @/assets/ai/gpt.svg'
103
104
104
105
const message = useMessage () // 消息弹窗
@@ -107,8 +108,8 @@ const message = useMessage() // 消息弹窗
107
108
const searchName = ref <string >(' ' ) // 对话搜索
108
109
const activeConversationId = ref <string | null >(null ) // 选中的对话,默认为 null
109
110
const hoverConversationId = ref <string | null >(null ) // 悬浮上去的对话
110
- const conversationList = ref ([] as ChatConversationVO []) // 对话列表
111
- const conversationMap = ref <any >({}) // 对话分组 (置顶、今天、三天前、一星期前、一个月前)
111
+ const conversationList = ref ([] as ChatConversationVO []) // 对话列表
112
+ const conversationMap = ref <any >({}) // 对话分组 (置顶、今天、三天前、一星期前、一个月前)
112
113
const drawer = ref <boolean >(false ) // 角色仓库抽屉 TODO @fan:roleDrawer 会不会好点哈
113
114
const loading = ref <boolean >(false ) // 加载中
114
115
const loadingTime = ref <any >() // 加载中定时器
@@ -138,7 +139,7 @@ const searchConversation = async (e) => {
138
139
conversationMap .value = await conversationTimeGroup (conversationList .value )
139
140
} else {
140
141
// 过滤
141
- const filterValues = conversationList .value .filter (item => {
142
+ const filterValues = conversationList .value .filter (( item ) => {
142
143
return item .title .includes (searchName .value .trim ())
143
144
})
144
145
conversationMap .value = await conversationTimeGroup (filterValues )
@@ -150,7 +151,7 @@ const searchConversation = async (e) => {
150
151
*/
151
152
const handleConversationClick = async (id : string ) => {
152
153
// 过滤出选中的对话
153
- const filterConversation = conversationList .value .filter (item => {
154
+ const filterConversation = conversationList .value .filter (( item ) => {
154
155
return item .id === id
155
156
})
156
157
// 回调 onConversationClick
@@ -211,28 +212,28 @@ const getChatConversationList = async () => {
211
212
const conversationTimeGroup = async (list : ChatConversationVO []) => {
212
213
// 排序、指定、时间分组(今天、一天前、三天前、七天前、30天前)
213
214
const groupMap = {
214
- ' 置顶 ' : [],
215
- ' 今天 ' : [],
216
- ' 一天前' : [],
217
- ' 三天前' : [],
218
- ' 七天前' : [],
219
- ' 三十天前' : []
215
+ 置顶 : [],
216
+ 今天 : [],
217
+ 一天前: [],
218
+ 三天前: [],
219
+ 七天前: [],
220
+ 三十天前: []
220
221
}
221
222
// 当前时间的时间戳
222
- const now = Date .now ();
223
+ const now = Date .now ()
223
224
// 定义时间间隔常量(单位:毫秒)
224
- const oneDay = 24 * 60 * 60 * 1000 ;
225
- const threeDays = 3 * oneDay ;
226
- const sevenDays = 7 * oneDay ;
227
- const thirtyDays = 30 * oneDay ;
225
+ const oneDay = 24 * 60 * 60 * 1000
226
+ const threeDays = 3 * oneDay
227
+ const sevenDays = 7 * oneDay
228
+ const thirtyDays = 30 * oneDay
228
229
for (const conversation: ChatConversationVO of list ) {
229
230
// 置顶
230
231
if (conversation .pinned ) {
231
232
groupMap[' 置顶' ].push(conversation)
232
233
continue
233
234
}
234
235
// 计算时间差(单位:毫秒)
235
- const diff = now - conversation.updateTime;
236
+ const diff = now - conversation.updateTime
236
237
// 根据时间间隔判断
237
238
if (diff < oneDay) {
238
239
groupMap[' 今天' ].push(conversation)
@@ -271,7 +272,7 @@ const createConversation = async () => {
271
272
*/
272
273
const updateConversationTitle = async (conversation: ChatConversationVO ) => {
273
274
// 1. 二次确认
274
- const {value} = await ElMessageBox .prompt (' 修改标题' , {
275
+ const { value } = await ElMessageBox .prompt (' 修改标题' , {
275
276
inputPattern: / ^ [\s\S ] * . * \S [\s\S ] * $ / , // 判断非空,且非空格
276
277
inputErrorMessage: ' 标题不能为空' ,
277
278
inputValue: conversation .title
@@ -285,7 +286,7 @@ const updateConversationTitle = async (conversation: ChatConversationVO) => {
285
286
// 3. 刷新列表
286
287
await getChatConversationList ()
287
288
// 4. 过滤当前切换的
288
- const filterConversationList = conversationList .value .filter (item => {
289
+ const filterConversationList = conversationList .value .filter (( item ) => {
289
290
return item .id === conversation .id
290
291
})
291
292
if (filterConversationList .length > 0 ) {
@@ -310,8 +311,7 @@ const deleteChatConversation = async (conversation: ChatConversationVO) => {
310
311
await getChatConversationList ()
311
312
// 回调
312
313
emits (' onConversationDelete' , conversation )
313
- } catch {
314
- }
314
+ } catch {}
315
315
}
316
316
317
317
/**
@@ -343,16 +343,13 @@ const handleRoleRepository = async () => {
343
343
*/
344
344
const handleClearConversation = async () => {
345
345
// TODO @fan:可以使用 await message.confirm( 简化,然后使用 await 改成同步的逻辑,会更简洁
346
- ElMessageBox .confirm (
347
- ' 确认后对话会全部清空,置顶的对话除外。' ,
348
- ' 确认提示' ,
349
- {
350
- confirmButtonText: ' 确认' ,
351
- cancelButtonText: ' 取消' ,
352
- type: ' warning' ,
353
- })
346
+ ElMessageBox .confirm (' 确认后对话会全部清空,置顶的对话除外。' , ' 确认提示' , {
347
+ confirmButtonText: ' 确认' ,
348
+ cancelButtonText: ' 取消' ,
349
+ type: ' warning'
350
+ })
354
351
.then (async () => {
355
- await ChatConversationApi .deleteMyAllExceptPinned ()
352
+ await ChatConversationApi .deleteChatConversationMyByUnpinned ()
356
353
ElMessage ({
357
354
message: ' 操作成功!' ,
358
355
type: ' success'
@@ -364,8 +361,7 @@ const handleClearConversation = async () => {
364
361
// 回调 方法
365
362
emits (' onConversationClear' )
366
363
})
367
- .catch (() => {
368
- })
364
+ .catch (() => {})
369
365
}
370
366
371
367
// ============ 组件 onMounted
@@ -377,7 +373,7 @@ watch(activeId, async (newValue, oldValue) => {
377
373
})
378
374
379
375
// 定义 public 方法
380
- defineExpose ({createConversation })
376
+ defineExpose ({ createConversation })
381
377
382
378
onMounted (async () => {
383
379
// 获取 对话列表
@@ -394,11 +390,9 @@ onMounted(async () => {
394
390
}
395
391
}
396
392
})
397
-
398
393
< / script >
399
394
400
395
< style scoped lang = " scss" >
401
-
402
396
.conversation - container {
403
397
position: relative ;
404
398
display : flex ;
0 commit comments