4
4
<div class =" kefu-title" >{{ keFuConversation.userNickname }}</div >
5
5
</el-header >
6
6
<el-main class =" kefu-content" style =" overflow : visible " >
7
- <el-scrollbar ref =" scrollbarRef" always height =" calc(100vh - 495px)" >
7
+ <div
8
+ v-show =" loadingMore"
9
+ class =" loadingMore flex justify-center items-center cursor-pointer"
10
+ @click =" handleOldMessage"
11
+ >
12
+ 加载更多
13
+ </div >
14
+ <el-scrollbar ref =" scrollbarRef" always height =" calc(100vh - 495px)" @scroll =" handleScroll" >
8
15
<div ref =" innerRef" class =" w-[100%] pb-3px" >
9
- <div v-for =" (item, index) in messageList " :key =" item.id" class =" w-[100%]" >
16
+ <div v-for =" (item, index) in getMessageList0 " :key =" item.id" class =" w-[100%]" >
10
17
<div class =" flex justify-center items-center mb-20px" >
11
18
<!-- 日期 -->
12
19
<div
58
65
</div >
59
66
</div >
60
67
</el-scrollbar >
68
+ <div
69
+ v-show =" showNewMessageTip"
70
+ class =" newMessageTip flex items-center cursor-pointer"
71
+ @click =" handleToNewMessage"
72
+ >
73
+ <span >有新消息</span >
74
+ <Icon class =" ml-5px" icon =" ep:bottom" />
75
+ </div >
61
76
</el-main >
62
77
<el-footer height =" 230px" >
63
78
<div class =" h-[100%]" >
@@ -101,23 +116,47 @@ const messageTool = useMessage()
101
116
const message = ref (' ' ) // 消息
102
117
const messageList = ref <KeFuMessageRespVO []>([]) // 消息列表
103
118
const keFuConversation = ref <KeFuConversationRespVO >({} as KeFuConversationRespVO ) // 用户会话
104
- // 获得消息 TODO puhui999: 先不考虑下拉加载历史消息
119
+ const showNewMessageTip = ref (false ) // 显示有新消息提示
120
+ const queryParams = reactive ({
121
+ pageNo: 1 ,
122
+ conversationId: 0
123
+ })
124
+ const total = ref (0 ) // 消息总条数
125
+ // 获得消息
105
126
const getMessageList = async (conversation : KeFuConversationRespVO ) => {
106
127
keFuConversation .value = conversation
107
- const { list } = await KeFuMessageApi .getKeFuMessagePage ({
108
- pageNo: 1 ,
109
- conversationId: conversation .id
110
- })
111
- messageList .value = list .reverse ()
112
- // TODO puhui999: 首次加载时滚动到最新消息,如果加载的是历史消息则不滚动
128
+ queryParams .conversationId = conversation .id
129
+ const messageTotal = messageList .value .length
130
+ if (total .value > 0 && messageTotal > 0 && messageTotal === total .value ) {
131
+ return
132
+ }
133
+ const res = await KeFuMessageApi .getKeFuMessagePage (queryParams )
134
+ total .value = res .total
135
+ for (const item of res .list ) {
136
+ if (messageList .value .some ((val ) => val .id === item .id )) {
137
+ continue
138
+ }
139
+ messageList .value .push (item )
140
+ }
113
141
await scrollToBottom ()
114
142
}
143
+ const getMessageList0 = computed (() => {
144
+ messageList .value .sort ((a : any , b : any ) => a .createTime - b .createTime )
145
+ return messageList .value
146
+ })
147
+
115
148
// 刷新消息列表
116
- const refreshMessageList = () => {
149
+ const refreshMessageList = async () => {
117
150
if (! keFuConversation .value ) {
118
151
return
119
152
}
120
- getMessageList (keFuConversation .value )
153
+
154
+ queryParams .pageNo = 1
155
+ await getMessageList (keFuConversation .value )
156
+ if (loadHistory .value ) {
157
+ // 有下角显示有新消息提示
158
+ showNewMessageTip .value = true
159
+ }
121
160
}
122
161
defineExpose ({ getMessageList , refreshMessageList })
123
162
// 是否显示聊天区域
@@ -140,7 +179,7 @@ const handleSendPicture = async (picUrl: string) => {
140
179
const handleSendMessage = async () => {
141
180
// 1. 校验消息是否为空
142
181
if (isEmpty (unref (message .value ))) {
143
- messageTool .warning (' 请输入消息后再发送哦!' )
182
+ messageTool .notifyWarning (' 请输入消息后再发送哦!' )
144
183
return
145
184
}
146
185
// 2. 组织发送消息
@@ -167,12 +206,41 @@ const innerRef = ref<HTMLDivElement>()
167
206
const scrollbarRef = ref <InstanceType <typeof ElScrollbarType >>()
168
207
// 滚动到底部
169
208
const scrollToBottom = async () => {
170
- // 1. 滚动到最新消息
209
+ // 1. 首次加载时滚动到最新消息,如果加载的是历史消息则不滚动
210
+ if (loadHistory .value ) {
211
+ return
212
+ }
213
+ // 2.1 滚动到最新消息,关闭新消息提示
171
214
await nextTick ()
172
215
scrollbarRef .value ! .setScrollTop (innerRef .value ! .clientHeight )
173
- // 2. 消息已读
216
+ showNewMessageTip .value = false
217
+ // 2.2 消息已读
174
218
await KeFuMessageApi .updateKeFuMessageReadStatus (keFuConversation .value .id )
175
219
}
220
+ // 查看新消息
221
+ const handleToNewMessage = async () => {
222
+ loadHistory .value = false
223
+ await scrollToBottom ()
224
+ }
225
+
226
+ const loadingMore = ref (false ) // 滚动到顶部加载更多
227
+ const loadHistory = ref (false ) // 加载历史消息
228
+ const handleScroll = async ({ scrollTop }) => {
229
+ const messageTotal = messageList .value .length
230
+ if (total .value > 0 && messageTotal > 0 && messageTotal === total .value ) {
231
+ return
232
+ }
233
+ // 距顶 20 加载下一页数据
234
+ loadingMore .value = scrollTop < 20
235
+ }
236
+ const handleOldMessage = async () => {
237
+ loadHistory .value = true
238
+ // 加载消息列表
239
+ queryParams .pageNo += 1
240
+ await getMessageList (keFuConversation .value )
241
+ loadingMore .value = false
242
+ // TODO puhui999: 等页面加载完后,获得上一页最后一条消息的位置,控制滚动到它所在位置
243
+ }
176
244
/**
177
245
* 是否显示时间
178
246
* @param {*} item - 数据
@@ -196,6 +264,32 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
196
264
}
197
265
198
266
& -content {
267
+ position : relative ;
268
+
269
+ .loadingMore {
270
+ position : absolute ;
271
+ top : 0 ;
272
+ left : 0 ;
273
+ width : 100% ;
274
+ height : 50px ;
275
+ background-color : #eee ;
276
+ color : #666 ;
277
+ text-align : center ;
278
+ line-height : 50px ;
279
+ transform : translateY (-100% );
280
+ transition : transform 0.3s ease-in-out ;
281
+ }
282
+
283
+ .newMessageTip {
284
+ position : absolute ;
285
+ bottom : 35px ;
286
+ right : 35px ;
287
+ background-color : #fff ;
288
+ padding : 10px ;
289
+ border-radius : 30px ;
290
+ box-shadow : 0 2px 4px rgba (0 , 0 , 0 , 0.1 ); /* 阴影效果 */
291
+ }
292
+
199
293
.ss-row-left {
200
294
justify-content : flex-start ;
201
295
0 commit comments