40
40
v-if =" item.senderType === UserTypeEnum.MEMBER"
41
41
:src =" conversation.userAvatar"
42
42
alt =" avatar"
43
+ class =" w-60px h-60px"
43
44
/>
44
45
<div
45
46
:class =" { 'kefu-message': KeFuMessageContentTypeEnum.TEXT === item.contentType }"
46
47
class =" p-10px"
47
48
>
48
49
<!-- 文本消息 -->
49
- <TextMessageItem :message =" item" />
50
+ <MessageItem :message =" item" >
51
+ <template v-if =" KeFuMessageContentTypeEnum .TEXT === item .contentType " >
52
+ <div
53
+ v-dompurify-html =" replaceEmoji(item.content)"
54
+ class =" flex items-center"
55
+ ></div >
56
+ </template >
57
+ </MessageItem >
50
58
<!-- 图片消息 -->
51
- <ImageMessageItem :message =" item" />
59
+ <MessageItem :message =" item" >
60
+ <el-image
61
+ v-if =" KeFuMessageContentTypeEnum.IMAGE === item.contentType"
62
+ :initial-index =" 0"
63
+ :preview-src-list =" [item.content]"
64
+ :src =" item.content"
65
+ class =" w-200px"
66
+ fit =" contain"
67
+ preview-teleported
68
+ />
69
+ </MessageItem >
52
70
<!-- 商品消息 -->
53
- <ProductMessageItem :message =" item" />
71
+ <MessageItem :message =" item" >
72
+ <ProductItem
73
+ v-if =" KeFuMessageContentTypeEnum.PRODUCT === item.contentType"
74
+ :picUrl =" getMessageContent(item).picUrl"
75
+ :price =" getMessageContent(item).price"
76
+ :skuText =" getMessageContent(item).introduction"
77
+ :title =" getMessageContent(item).spuName"
78
+ :titleWidth =" 400"
79
+ class =" max-w-70%"
80
+ priceColor =" #FF3000"
81
+ />
82
+ </MessageItem >
54
83
<!-- 订单消息 -->
55
- <OrderMessageItem :message =" item" />
84
+ <MessageItem :message =" item" >
85
+ <OrderItem
86
+ v-if =" KeFuMessageContentTypeEnum.ORDER === item.contentType"
87
+ :message =" item"
88
+ class =" max-w-70%"
89
+ />
90
+ </MessageItem >
56
91
</div >
57
92
<el-avatar
58
93
v-if =" item.senderType === UserTypeEnum.ADMIN"
@@ -97,24 +132,24 @@ import { KeFuMessageApi, KeFuMessageRespVO } from '@/api/mall/promotion/kefu/mes
97
132
import { KeFuConversationRespVO } from ' @/api/mall/promotion/kefu/conversation'
98
133
import EmojiSelectPopover from ' ./tools/EmojiSelectPopover.vue'
99
134
import PictureSelectUpload from ' ./tools/PictureSelectUpload.vue'
100
- import TextMessageItem from ' ./message/TextMessageItem.vue'
101
- import ImageMessageItem from ' ./message/ImageMessageItem.vue'
102
- import ProductMessageItem from ' ./message/ProductMessageItem.vue'
103
- import OrderMessageItem from ' ./message/OrderMessageItem.vue'
104
- import { Emoji } from ' ./tools/emoji'
135
+ import ProductItem from ' ./message/ProductItem.vue'
136
+ import OrderItem from ' ./message/OrderItem.vue'
137
+ import { Emoji , useEmoji } from ' ./tools/emoji'
105
138
import { KeFuMessageContentTypeEnum } from ' ./tools/constants'
106
139
import { isEmpty } from ' @/utils/is'
107
140
import { UserTypeEnum } from ' @/utils/constants'
108
141
import { formatDate } from ' @/utils/formatTime'
109
142
import dayjs from ' dayjs'
110
143
import relativeTime from ' dayjs/plugin/relativeTime'
144
+ import { debounce } from ' lodash-es'
145
+ import { jsonParse } from ' @/utils'
111
146
112
147
dayjs .extend (relativeTime )
113
148
114
149
defineOptions ({ name: ' KeFuMessageList' })
115
150
116
151
const message = ref (' ' ) // 消息弹窗
117
-
152
+ const { replaceEmoji } = useEmoji ()
118
153
const messageTool = useMessage ()
119
154
const messageList = ref <KeFuMessageRespVO []>([]) // 消息列表
120
155
const conversation = ref <KeFuConversationRespVO >({} as KeFuConversationRespVO ) // 用户会话
@@ -126,18 +161,11 @@ const queryParams = reactive({
126
161
})
127
162
const total = ref (0 ) // 消息总条数
128
163
const refreshContent = ref (false ) // 内容刷新,主要解决会话消息页面高度不一致导致的滚动功能精度失效
164
+
165
+ /** 获悉消息内容 */
166
+ const getMessageContent = computed (() => (item : any ) => jsonParse (item .content ))
129
167
/** 获得消息列表 */
130
- const getMessageList = async (val : KeFuConversationRespVO , conversationChange : boolean ) => {
131
- // 会话切换,重置相关参数
132
- if (conversationChange ) {
133
- queryParams .pageNo = 1
134
- messageList .value = []
135
- total .value = 0
136
- loadHistory .value = false
137
- refreshContent .value = false
138
- }
139
- conversation .value = val
140
- queryParams .conversationId = val .id
168
+ const getMessageList = async () => {
141
169
const res = await KeFuMessageApi .getKeFuMessagePage (queryParams )
142
170
total .value = res .total
143
171
// 情况一:加载最新消息
@@ -146,14 +174,18 @@ const getMessageList = async (val: KeFuConversationRespVO, conversationChange: b
146
174
} else {
147
175
// 情况二:加载历史消息
148
176
for (const item of res .list ) {
149
- if (messageList .value .some ((val ) => val .id === item .id )) {
150
- continue
151
- }
152
- messageList .value .push (item )
177
+ pushMessage (item )
153
178
}
154
179
}
155
180
refreshContent .value = true
156
- await scrollToBottom ()
181
+ }
182
+
183
+ /** 添加消息 */
184
+ const pushMessage = (message : any ) => {
185
+ if (messageList .value .some ((val ) => val .id === message .id )) {
186
+ return
187
+ }
188
+ messageList .value .push (message )
157
189
}
158
190
159
191
/** 按照时间倒序,获取消息列表 */
@@ -163,20 +195,46 @@ const getMessageList0 = computed(() => {
163
195
})
164
196
165
197
/** 刷新消息列表 */
166
- const refreshMessageList = async () => {
198
+ const refreshMessageList = async (message ? : any ) => {
167
199
if (! conversation .value ) {
168
200
return
169
201
}
170
202
171
- queryParams .pageNo = 1
172
- await getMessageList (conversation .value , false )
203
+ if (typeof message !== ' undefined' ) {
204
+ // 当前查询会话与消息所属会话不一致则不做处理
205
+ if (message .conversationId !== conversation .value .id ) {
206
+ return
207
+ }
208
+ pushMessage (message )
209
+ } else {
210
+ queryParams .pageNo = 1
211
+ await getMessageList ()
212
+ }
213
+
173
214
if (loadHistory .value ) {
174
215
// 右下角显示有新消息提示
175
216
showNewMessageTip .value = true
217
+ } else {
218
+ // 滚动到最新消息处
219
+ await handleToNewMessage ()
176
220
}
177
221
}
178
222
179
- defineExpose ({ getMessageList , refreshMessageList })
223
+ const getNewMessageList = async (val : KeFuConversationRespVO ) => {
224
+ // 会话切换,重置相关参数
225
+ queryParams .pageNo = 1
226
+ messageList .value = []
227
+ total .value = 0
228
+ loadHistory .value = false
229
+ refreshContent .value = false
230
+ // 设置会话相关属性
231
+ conversation .value = val
232
+ queryParams .conversationId = val .id
233
+ // 获取消息
234
+ await refreshMessageList ()
235
+ }
236
+ defineExpose ({ getNewMessageList , refreshMessageList })
237
+
180
238
const showKeFuMessageList = computed (() => ! isEmpty (conversation .value )) // 是否显示聊天区域
181
239
const skipGetMessageList = computed (() => {
182
240
// 已加载到最后一页的话则不触发新的消息获取
@@ -221,9 +279,7 @@ const sendMessage = async (msg: any) => {
221
279
await KeFuMessageApi .sendKeFuMessage (msg )
222
280
message .value = ' '
223
281
// 加载消息列表
224
- await getMessageList (conversation .value , false )
225
- // 滚动到最新消息处
226
- await scrollToBottom ()
282
+ await refreshMessageList ()
227
283
}
228
284
229
285
/** 滚动到底部 */
@@ -248,17 +304,24 @@ const handleToNewMessage = async () => {
248
304
await scrollToBottom ()
249
305
}
250
306
251
- /** 加载历史消息 */
252
307
const loadHistory = ref (false ) // 加载历史消息
253
- const handleScroll = async ({ scrollTop }) => {
308
+ /** 处理消息列表滚动事件(debounce 限流) */
309
+ const handleScroll = debounce (({ scrollTop }) => {
254
310
if (skipGetMessageList .value ) {
255
311
return
256
312
}
257
313
// 触顶自动加载下一页数据
258
- if (scrollTop === 0 ) {
259
- await handleOldMessage ()
314
+ if (Math . floor ( scrollTop ) === 0 ) {
315
+ handleOldMessage ()
260
316
}
261
- }
317
+ const wrap = scrollbarRef .value ?.wrapRef
318
+ // 触底重置
319
+ if (Math .abs (wrap ! .scrollHeight - wrap ! .clientHeight - wrap ! .scrollTop ) < 1 ) {
320
+ loadHistory .value = false
321
+ refreshMessageList ()
322
+ }
323
+ }, 200 )
324
+ /** 加载历史消息 */
262
325
const handleOldMessage = async () => {
263
326
// 记录已有页面高度
264
327
const oldPageHeight = innerRef .value ?.clientHeight
@@ -268,7 +331,7 @@ const handleOldMessage = async () => {
268
331
loadHistory .value = true
269
332
// 加载消息列表
270
333
queryParams .pageNo += 1
271
- await getMessageList (conversation . value , false )
334
+ await getMessageList ()
272
335
// 等页面加载完后,获得上一页最后一条消息的位置,控制滚动到它所在位置
273
336
scrollbarRef .value ! .setScrollTop (innerRef .value ! .clientHeight - oldPageHeight )
274
337
}
0 commit comments