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,10 @@ const queryParams = reactive({
126
161
})
127
162
const total = ref (0 ) // 消息总条数
128
163
const refreshContent = ref (false ) // 内容刷新,主要解决会话消息页面高度不一致导致的滚动功能精度失效
164
+ /** 获悉消息内容 */
165
+ const getMessageContent = computed (() => (item : any ) => jsonParse (item .content ))
129
166
/** 获得消息列表 */
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
167
+ const getMessageList = async () => {
141
168
const res = await KeFuMessageApi .getKeFuMessagePage (queryParams )
142
169
total .value = res .total
143
170
// 情况一:加载最新消息
@@ -146,14 +173,17 @@ const getMessageList = async (val: KeFuConversationRespVO, conversationChange: b
146
173
} else {
147
174
// 情况二:加载历史消息
148
175
for (const item of res .list ) {
149
- if (messageList .value .some ((val ) => val .id === item .id )) {
150
- continue
151
- }
152
- messageList .value .push (item )
176
+ pushMessage (item )
153
177
}
154
178
}
155
179
refreshContent .value = true
156
- await scrollToBottom ()
180
+ }
181
+ /** 添加消息 */
182
+ const pushMessage = (message : any ) => {
183
+ if (messageList .value .some ((val ) => val .id === message .id )) {
184
+ return
185
+ }
186
+ messageList .value .push (message )
157
187
}
158
188
159
189
/** 按照时间倒序,获取消息列表 */
@@ -163,20 +193,44 @@ const getMessageList0 = computed(() => {
163
193
})
164
194
165
195
/** 刷新消息列表 */
166
- const refreshMessageList = async () => {
196
+ const refreshMessageList = async (message ? : any ) => {
167
197
if (! conversation .value ) {
168
198
return
169
199
}
170
200
171
- queryParams .pageNo = 1
172
- await getMessageList (conversation .value , false )
201
+ if (typeof message !== ' undefined' ) {
202
+ // 当前查询会话与消息所属会话不一致则不做处理
203
+ if (message .conversationId !== conversation .value .id ) {
204
+ return
205
+ }
206
+ pushMessage (message )
207
+ } else {
208
+ queryParams .pageNo = 1
209
+ await getMessageList ()
210
+ }
211
+
173
212
if (loadHistory .value ) {
174
213
// 右下角显示有新消息提示
175
214
showNewMessageTip .value = true
215
+ } else {
216
+ // 滚动到最新消息处
217
+ await handleToNewMessage ()
176
218
}
177
219
}
178
-
179
- defineExpose ({ getMessageList , refreshMessageList })
220
+ const getNewMessageList = async (val : KeFuConversationRespVO ) => {
221
+ // 会话切换,重置相关参数
222
+ queryParams .pageNo = 1
223
+ messageList .value = []
224
+ total .value = 0
225
+ loadHistory .value = false
226
+ refreshContent .value = false
227
+ // 设置会话相关属性
228
+ conversation .value = val
229
+ queryParams .conversationId = val .id
230
+ // 获取消息
231
+ await refreshMessageList ()
232
+ }
233
+ defineExpose ({ getNewMessageList , refreshMessageList })
180
234
const showKeFuMessageList = computed (() => ! isEmpty (conversation .value )) // 是否显示聊天区域
181
235
const skipGetMessageList = computed (() => {
182
236
// 已加载到最后一页的话则不触发新的消息获取
@@ -221,9 +275,7 @@ const sendMessage = async (msg: any) => {
221
275
await KeFuMessageApi .sendKeFuMessage (msg )
222
276
message .value = ' '
223
277
// 加载消息列表
224
- await getMessageList (conversation .value , false )
225
- // 滚动到最新消息处
226
- await scrollToBottom ()
278
+ await refreshMessageList ()
227
279
}
228
280
229
281
/** 滚动到底部 */
@@ -248,17 +300,24 @@ const handleToNewMessage = async () => {
248
300
await scrollToBottom ()
249
301
}
250
302
251
- /** 加载历史消息 */
252
303
const loadHistory = ref (false ) // 加载历史消息
253
- const handleScroll = async ({ scrollTop }) => {
304
+ /** 处理消息列表滚动事件(debounce 限流) */
305
+ const handleScroll = debounce (({ scrollTop }) => {
254
306
if (skipGetMessageList .value ) {
255
307
return
256
308
}
257
309
// 触顶自动加载下一页数据
258
- if (scrollTop === 0 ) {
259
- await handleOldMessage ()
310
+ if (Math . floor ( scrollTop ) === 0 ) {
311
+ handleOldMessage ()
260
312
}
261
- }
313
+ const wrap = scrollbarRef .value ?.wrapRef
314
+ // 触底重置
315
+ if (Math .abs (wrap ! .scrollHeight - wrap ! .clientHeight - wrap ! .scrollTop ) < 1 ) {
316
+ loadHistory .value = false
317
+ refreshMessageList ()
318
+ }
319
+ }, 200 )
320
+ /** 加载历史消息 */
262
321
const handleOldMessage = async () => {
263
322
// 记录已有页面高度
264
323
const oldPageHeight = innerRef .value ?.clientHeight
@@ -268,7 +327,7 @@ const handleOldMessage = async () => {
268
327
loadHistory .value = true
269
328
// 加载消息列表
270
329
queryParams .pageNo += 1
271
- await getMessageList (conversation . value , false )
330
+ await getMessageList ()
272
331
// 等页面加载完后,获得上一页最后一条消息的位置,控制滚动到它所在位置
273
332
scrollbarRef .value ! .setScrollTop (innerRef .value ! .clientHeight - oldPageHeight )
274
333
}
0 commit comments