Skip to content

Commit e4b57bd

Browse files
YunaiVgitee-org
authored andcommitted
!579 【功能完善】商城: 客服缓存
Merge pull request !579 from puhui999/dev-crm
2 parents 31cef4f + df34f65 commit e4b57bd

File tree

5 files changed

+119
-41
lines changed

5 files changed

+119
-41
lines changed

src/api/mall/promotion/kefu/conversation/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export const KeFuConversationApi = {
2121
getConversationList: async () => {
2222
return await request.get({ url: '/promotion/kefu-conversation/list' })
2323
},
24+
// 获得客服会话
25+
getConversation: async (id: number) => {
26+
return await request.get({ url: `/promotion/kefu-conversation/get?id=` + id })
27+
},
2428
// 客服会话置顶
2529
updateConversationPinned: async (data: any) => {
2630
return await request.put({
@@ -30,6 +34,6 @@ export const KeFuConversationApi = {
3034
},
3135
// 删除客服会话
3236
deleteConversation: async (id: number) => {
33-
return await request.delete({ url: `/promotion/kefu-conversation/delete?id=${id}`})
37+
return await request.delete({ url: `/promotion/kefu-conversation/delete?id=${id}` })
3438
}
3539
}

src/store/modules/mall/kefu.ts

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { store } from '@/store'
22
import { defineStore } from 'pinia'
33
import { KeFuConversationApi, KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
44
import { KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
5+
import { isEmpty } from '@/utils/is'
56

6-
// TODO puhui999: 待优化完善
77
interface MallKefuInfoVO {
88
conversationList: KeFuConversationRespVO[] // 会话列表
99
conversationMessageList: Map<number, KeFuMessageRespVO[]> // 会话消息
@@ -18,20 +18,92 @@ export const useMallKefuStore = defineStore('mall-kefu', {
1818
getConversationList(): KeFuConversationRespVO[] {
1919
return this.conversationList
2020
},
21-
getConversationMessageList(): Map<number, KeFuMessageRespVO[]> {
22-
return this.conversationMessageList
21+
getConversationMessageList(): (conversationId: number) => KeFuMessageRespVO[] | undefined {
22+
return (conversationId: number) => this.conversationMessageList.get(conversationId)
2323
}
2424
},
2525
actions: {
26+
//======================= 会话消息相关 =======================
27+
/** 缓存历史消息 */
28+
saveMessageList(conversationId: number, messageList: KeFuMessageRespVO[]) {
29+
this.conversationMessageList.set(conversationId, messageList)
30+
},
31+
//======================= 会话相关 =======================
32+
/** 加载会话缓存列表 */
2633
async setConversationList() {
27-
const list = await KeFuConversationApi.getConversationList()
28-
list.sort((a: KeFuConversationRespVO, _) => (a.adminPinned ? -1 : 1))
29-
this.conversationList = list
34+
this.conversationList = await KeFuConversationApi.getConversationList()
35+
this.conversationSort()
36+
},
37+
/** 更新会话缓存已读 */
38+
async updateConversationStatus(conversationId: number) {
39+
if (isEmpty(this.conversationList)) {
40+
return
41+
}
42+
const conversation = this.conversationList.find((item) => item.id === conversationId)
43+
conversation && (conversation.adminUnreadMessageCount = 0)
44+
},
45+
/** 更新会话缓存 */
46+
async updateConversation(conversationId: number) {
47+
if (isEmpty(this.conversationList)) {
48+
return
49+
}
50+
51+
const conversation = await KeFuConversationApi.getConversation(conversationId)
52+
this.deleteConversation(conversationId)
53+
conversation && this.conversationList.push(conversation)
54+
this.conversationSort()
55+
},
56+
/** 删除会话缓存 */
57+
deleteConversation(conversationId: number) {
58+
const index = this.conversationList.findIndex((item) => item.id === conversationId)
59+
// 存在则删除
60+
if (index > -1) {
61+
this.conversationList.splice(index, 1)
62+
}
63+
},
64+
conversationSort() {
65+
this.conversationList.sort((obj1, obj2) => {
66+
// 如果 obj1.adminPinned 为 true,obj2.adminPinned 为 false,obj1 应该排在前面
67+
if (obj1.adminPinned && !obj2.adminPinned) return -1
68+
// 如果 obj1.adminPinned 为 false,obj2.adminPinned 为 true,obj2 应该排在前面
69+
if (!obj1.adminPinned && obj2.adminPinned) return 1
70+
71+
// 如果 obj1.adminPinned 和 obj2.adminPinned 都为 true,比较 adminUnreadMessageCount 的值
72+
if (obj1.adminPinned && obj2.adminPinned) {
73+
return obj1.adminUnreadMessageCount - obj2.adminUnreadMessageCount
74+
}
75+
76+
// 如果 obj1.adminPinned 和 obj2.adminPinned 都为 false,比较 adminUnreadMessageCount 的值
77+
if (!obj1.adminPinned && !obj2.adminPinned) {
78+
return obj1.adminUnreadMessageCount - obj2.adminUnreadMessageCount
79+
}
80+
81+
// 如果 obj1.adminPinned 为 true,obj2.adminPinned 为 true,且 b 都大于 0,比较 adminUnreadMessageCount 的值
82+
if (
83+
obj1.adminPinned &&
84+
obj2.adminPinned &&
85+
obj1.adminUnreadMessageCount > 0 &&
86+
obj2.adminUnreadMessageCount > 0
87+
) {
88+
return obj1.adminUnreadMessageCount - obj2.adminUnreadMessageCount
89+
}
90+
91+
// 如果 obj1.adminPinned 为 false,obj2.adminPinned 为 false,且 b 都大于 0,比较 adminUnreadMessageCount 的值
92+
if (
93+
!obj1.adminPinned &&
94+
!obj2.adminPinned &&
95+
obj1.adminUnreadMessageCount > 0 &&
96+
obj2.adminUnreadMessageCount > 0
97+
) {
98+
return obj1.adminUnreadMessageCount - obj2.adminUnreadMessageCount
99+
}
100+
101+
return 0
102+
})
30103
}
31-
// async setConversationMessageList(conversationId: number) {}
32104
}
33105
})
34106

35-
export const useUserStoreWithOut = () => {
107+
export const useMallKefuStoreWithOut = () => {
36108
return useMallKefuStore(store)
37109
}

src/views/mall/promotion/kefu/components/KeFuConversationList.vue

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<template>
22
<el-aside class="kefu p-5px h-100%" width="260px">
3-
<div class="color-[#999] font-bold my-10px">会话记录({{ conversationList.length }})</div>
3+
<div class="color-[#999] font-bold my-10px"
4+
>会话记录({{ kefuStore.getConversationList.length }})
5+
</div>
46
<div
5-
v-for="item in conversationList"
7+
v-for="item in kefuStore.getConversationList"
68
:key="item.id"
79
:class="{ active: item.id === activeConversationId, pinned: item.adminPinned }"
810
class="kefu-conversation flex items-center"
@@ -75,29 +77,26 @@ import { useEmoji } from './tools/emoji'
7577
import { formatPast } from '@/utils/formatTime'
7678
import { KeFuMessageContentTypeEnum } from './tools/constants'
7779
import { useAppStore } from '@/store/modules/app'
80+
import { useMallKefuStore } from '@/store/modules/mall/kefu'
7881
7982
defineOptions({ name: 'KeFuConversationList' })
8083
8184
const message = useMessage() // 消息弹窗
8285
const appStore = useAppStore()
86+
const kefuStore = useMallKefuStore() // 客服缓存
8387
const { replaceEmoji } = useEmoji()
84-
const conversationList = ref<KeFuConversationRespVO[]>([]) // 会话列表
8588
const activeConversationId = ref(-1) // 选中的会话
8689
const collapse = computed(() => appStore.getCollapse) // 折叠菜单
8790
88-
/** 加载会话列表 */
89-
const getConversationList = async () => {
90-
const list = await KeFuConversationApi.getConversationList()
91-
list.sort((a: KeFuConversationRespVO, _) => (a.adminPinned ? -1 : 1))
92-
conversationList.value = list
93-
}
94-
defineExpose({ getConversationList })
95-
9691
/** 打开右侧的消息列表 */
9792
const emits = defineEmits<{
9893
(e: 'change', v: KeFuConversationRespVO): void
9994
}>()
10095
const openRightMessage = (item: KeFuConversationRespVO) => {
96+
// 同一个会话则不处理
97+
if (activeConversationId.value === item.id) {
98+
return
99+
}
101100
activeConversationId.value = item.id
102101
emits('change', item)
103102
}
@@ -156,7 +155,7 @@ const updateConversationPinned = async (adminPinned: boolean) => {
156155
message.notifySuccess(adminPinned ? '置顶成功' : '取消置顶成功')
157156
// 2. 关闭右键菜单,更新会话列表
158157
closeRightMenu()
159-
await getConversationList()
158+
await kefuStore.updateConversation(rightClickConversation.value.id)
160159
}
161160
162161
/** 删除会话 */
@@ -166,7 +165,7 @@ const deleteConversation = async () => {
166165
await KeFuConversationApi.deleteConversation(rightClickConversation.value.id)
167166
// 2. 关闭右键菜单,更新会话列表
168167
closeRightMenu()
169-
await getConversationList()
168+
kefuStore.deleteConversation(rightClickConversation.value.id)
170169
}
171170
172171
/** 监听右键菜单的显示状态,添加点击事件监听器 */

src/views/mall/promotion/kefu/components/KeFuMessageList.vue

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ import dayjs from 'dayjs'
152152
import relativeTime from 'dayjs/plugin/relativeTime'
153153
import { debounce } from 'lodash-es'
154154
import { jsonParse } from '@/utils'
155+
import { useMallKefuStore } from '@/store/modules/mall/kefu'
155156
156157
dayjs.extend(relativeTime)
157158
@@ -169,6 +170,7 @@ const queryParams = reactive({
169170
})
170171
const total = ref(0) // 消息总条数
171172
const refreshContent = ref(false) // 内容刷新,主要解决会话消息页面高度不一致导致的滚动功能精度失效
173+
const kefuStore = useMallKefuStore() // 客服缓存
172174
173175
/** 获悉消息内容 */
174176
const getMessageContent = computed(() => (item: any) => jsonParse(item.content))
@@ -234,19 +236,20 @@ const refreshMessageList = async (message?: any) => {
234236
}
235237
}
236238
237-
/** 获得新会话的消息列表 */
238-
// TODO @puhui999:可优化:可以考虑本地做每个会话的消息 list 缓存;然后点击切换时,读取缓存;然后异步获取新消息,merge 下;
239+
/** 获得新会话的消息列表, 点击切换时,读取缓存;然后异步获取新消息,merge 下; */
239240
const getNewMessageList = async (val: KeFuConversationRespVO) => {
240-
// 会话切换,重置相关参数
241-
messageList.value = []
242-
total.value = 0
241+
// 1. 缓存当前会话消息列表
242+
kefuStore.saveMessageList(conversation.value.id, messageList.value)
243+
// 2.1 会话切换,重置相关参数
244+
messageList.value = kefuStore.getConversationMessageList(val.id) || []
245+
total.value = messageList.value.length || 0
243246
loadHistory.value = false
244247
refreshContent.value = false
245-
// 设置会话相关属性
248+
// 2.2 设置会话相关属性
246249
conversation.value = val
247250
queryParams.conversationId = val.id
248251
queryParams.createTime = undefined
249-
// 获取消息
252+
// 3. 获取消息
250253
await refreshMessageList()
251254
}
252255
defineExpose({ getNewMessageList, refreshMessageList })
@@ -297,6 +300,8 @@ const sendMessage = async (msg: any) => {
297300
message.value = ''
298301
// 加载消息列表
299302
await refreshMessageList()
303+
// 更新会话缓存
304+
await kefuStore.updateConversation(conversation.value.id)
300305
}
301306
302307
/** 滚动到底部 */

src/views/mall/promotion/kefu/index.vue

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<!-- 会话列表 -->
44
<KeFuConversationList ref="keFuConversationRef" @change="handleChange" />
55
<!-- 会话详情(选中会话的消息列表) -->
6-
<KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList" />
6+
<KeFuMessageList ref="keFuChatBoxRef" />
77
<!-- 会员信息(选中会话的会员信息) -->
88
<MemberInfo ref="memberInfoRef" />
99
</el-container>
@@ -15,10 +15,12 @@ import { WebSocketMessageTypeConstants } from './components/tools/constants'
1515
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
1616
import { getRefreshToken } from '@/utils/auth'
1717
import { useWebSocket } from '@vueuse/core'
18+
import { useMallKefuStore } from '@/store/modules/mall/kefu'
1819
1920
defineOptions({ name: 'KeFu' })
2021
2122
const message = useMessage() // 消息弹窗
23+
const kefuStore = useMallKefuStore() // 客服缓存
2224
2325
// ======================= WebSocket start =======================
2426
const server = ref(
@@ -53,29 +55,24 @@ watchEffect(() => {
5355
}
5456
// 2.2 消息类型:KEFU_MESSAGE_TYPE
5557
if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_TYPE) {
58+
const message = JSON.parse(jsonMessage.content)
5659
// 刷新会话列表
5760
// TODO @puhui999:不应该刷新列表,而是根据消息,本地 update 列表的数据;
58-
getConversationList()
61+
kefuStore.updateConversation(message.conversationId)
5962
// 刷新消息列表
60-
keFuChatBoxRef.value?.refreshMessageList(JSON.parse(jsonMessage.content))
63+
keFuChatBoxRef.value?.refreshMessageList(message)
6164
return
6265
}
6366
// 2.3 消息类型:KEFU_MESSAGE_ADMIN_READ
6467
if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_ADMIN_READ) {
65-
// 刷新会话列表
66-
// TODO @puhui999:不应该刷新列表,而是根据消息,本地 update 列表的数据;
67-
getConversationList()
68+
// 更新会话已读
69+
kefuStore.updateConversationStatus(JSON.parse(jsonMessage.content)?.id)
6870
}
6971
} catch (error) {
7072
console.error(error)
7173
}
7274
})
7375
// ======================= WebSocket end =======================
74-
/** 加载会话列表 */
75-
const keFuConversationRef = ref<InstanceType<typeof KeFuConversationList>>()
76-
const getConversationList = () => {
77-
keFuConversationRef.value?.getConversationList()
78-
}
7976
8077
/** 加载指定会话的消息列表 */
8178
const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>()
@@ -87,7 +84,8 @@ const handleChange = (conversation: KeFuConversationRespVO) => {
8784
8885
/** 初始化 */
8986
onMounted(() => {
90-
getConversationList()
87+
/** 加载会话列表 */
88+
kefuStore.setConversationList()
9189
// 打开 websocket 连接
9290
open()
9391
})

0 commit comments

Comments
 (0)