Browse Source

【功能完善】商城: 客服会话缓存

puhui999 9 months ago
parent
commit
e342e91f79

+ 5 - 1
src/api/mall/promotion/kefu/conversation/index.ts

@@ -21,6 +21,10 @@ export const KeFuConversationApi = {
   getConversationList: async () => {
     return await request.get({ url: '/promotion/kefu-conversation/list' })
   },
+  // 获得客服会话
+  getConversation: async (id: number) => {
+    return await request.get({ url: `/promotion/kefu-conversation/get?id=` + id })
+  },
   // 客服会话置顶
   updateConversationPinned: async (data: any) => {
     return await request.put({
@@ -30,6 +34,6 @@ export const KeFuConversationApi = {
   },
   // 删除客服会话
   deleteConversation: async (id: number) => {
-    return await request.delete({ url: `/promotion/kefu-conversation/delete?id=${id}`})
+    return await request.delete({ url: `/promotion/kefu-conversation/delete?id=${id}` })
   }
 }

+ 72 - 5
src/store/modules/mall/kefu.ts

@@ -2,6 +2,7 @@ import { store } from '@/store'
 import { defineStore } from 'pinia'
 import { KeFuConversationApi, KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
 import { KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
+import { isEmpty } from '@/utils/is'
 
 // TODO puhui999: 待优化完善
 interface MallKefuInfoVO {
@@ -23,15 +24,81 @@ export const useMallKefuStore = defineStore('mall-kefu', {
     }
   },
   actions: {
+    /** 加载会话缓存列表 */
     async setConversationList() {
-      const list = await KeFuConversationApi.getConversationList()
-      list.sort((a: KeFuConversationRespVO, _) => (a.adminPinned ? -1 : 1))
-      this.conversationList = list
+      this.conversationList = await KeFuConversationApi.getConversationList()
+      this.conversationSort()
+    },
+    /** 更新会话缓存已读 */
+    async updateConversationStatus(conversationId: number) {
+      if (isEmpty(this.conversationList)) {
+        return
+      }
+      const conversation = this.conversationList.find((item) => item.id === conversationId)
+      conversation && (conversation.adminUnreadMessageCount = 0)
+    },
+    /** 更新会话缓存 */
+    async updateConversation(conversationId: number) {
+      if (isEmpty(this.conversationList)) {
+        return
+      }
+
+      const conversation = await KeFuConversationApi.getConversation(conversationId)
+      this.deleteConversation(conversationId)
+      conversation && this.conversationList.push(conversation)
+      this.conversationSort()
+    },
+    /** 删除会话缓存 */
+    deleteConversation(conversationId: number) {
+      const index = this.conversationList.findIndex((item) => item.id === conversationId)
+      // 存在则删除
+      if (index > -1) {
+        this.conversationList.splice(index, 1)
+      }
+    },
+    conversationSort() {
+      this.conversationList.sort((obj1, obj2) => {
+        // 如果 obj1.adminPinned 为 true,obj2.adminPinned 为 false,obj1 应该排在前面
+        if (obj1.adminPinned && !obj2.adminPinned) return -1
+        // 如果 obj1.adminPinned 为 false,obj2.adminPinned 为 true,obj2 应该排在前面
+        if (!obj1.adminPinned && obj2.adminPinned) return 1
+
+        // 如果 obj1.adminPinned 和 obj2.adminPinned 都为 true,比较 adminUnreadMessageCount 的值
+        if (obj1.adminPinned && obj2.adminPinned) {
+          return obj1.adminUnreadMessageCount - obj2.adminUnreadMessageCount
+        }
+
+        // 如果 obj1.adminPinned 和 obj2.adminPinned 都为 false,比较 adminUnreadMessageCount 的值
+        if (!obj1.adminPinned && !obj2.adminPinned) {
+          return obj1.adminUnreadMessageCount - obj2.adminUnreadMessageCount
+        }
+
+        // 如果 obj1.adminPinned 为 true,obj2.adminPinned 为 true,且 b 都大于 0,比较 adminUnreadMessageCount 的值
+        if (
+          obj1.adminPinned &&
+          obj2.adminPinned &&
+          obj1.adminUnreadMessageCount > 0 &&
+          obj2.adminUnreadMessageCount > 0
+        ) {
+          return obj1.adminUnreadMessageCount - obj2.adminUnreadMessageCount
+        }
+
+        // 如果 obj1.adminPinned 为 false,obj2.adminPinned 为 false,且 b 都大于 0,比较 adminUnreadMessageCount 的值
+        if (
+          !obj1.adminPinned &&
+          !obj2.adminPinned &&
+          obj1.adminUnreadMessageCount > 0 &&
+          obj2.adminUnreadMessageCount > 0
+        ) {
+          return obj1.adminUnreadMessageCount - obj2.adminUnreadMessageCount
+        }
+
+        return 0
+      })
     }
-    // async setConversationMessageList(conversationId: number) {}
   }
 })
 
-export const useUserStoreWithOut = () => {
+export const useMallKefuStoreWithOut = () => {
   return useMallKefuStore(store)
 }

+ 8 - 13
src/views/mall/promotion/kefu/components/KeFuConversationList.vue

@@ -1,8 +1,10 @@
 <template>
   <el-aside class="kefu p-5px h-100%" width="260px">
-    <div class="color-[#999] font-bold my-10px">会话记录({{ conversationList.length }})</div>
+    <div class="color-[#999] font-bold my-10px"
+      >会话记录({{ kefuStore.getConversationList.length }})
+    </div>
     <div
-      v-for="item in conversationList"
+      v-for="item in kefuStore.getConversationList"
       :key="item.id"
       :class="{ active: item.id === activeConversationId, pinned: item.adminPinned }"
       class="kefu-conversation flex items-center"
@@ -75,24 +77,17 @@ import { useEmoji } from './tools/emoji'
 import { formatPast } from '@/utils/formatTime'
 import { KeFuMessageContentTypeEnum } from './tools/constants'
 import { useAppStore } from '@/store/modules/app'
+import { useMallKefuStore } from '@/store/modules/mall/kefu'
 
 defineOptions({ name: 'KeFuConversationList' })
 
 const message = useMessage() // 消息弹窗
 const appStore = useAppStore()
+const kefuStore = useMallKefuStore() // 客服缓存
 const { replaceEmoji } = useEmoji()
-const conversationList = ref<KeFuConversationRespVO[]>([]) // 会话列表
 const activeConversationId = ref(-1) // 选中的会话
 const collapse = computed(() => appStore.getCollapse) // 折叠菜单
 
-/** 加载会话列表 */
-const getConversationList = async () => {
-  const list = await KeFuConversationApi.getConversationList()
-  list.sort((a: KeFuConversationRespVO, _) => (a.adminPinned ? -1 : 1))
-  conversationList.value = list
-}
-defineExpose({ getConversationList })
-
 /** 打开右侧的消息列表 */
 const emits = defineEmits<{
   (e: 'change', v: KeFuConversationRespVO): void
@@ -156,7 +151,7 @@ const updateConversationPinned = async (adminPinned: boolean) => {
   message.notifySuccess(adminPinned ? '置顶成功' : '取消置顶成功')
   // 2. 关闭右键菜单,更新会话列表
   closeRightMenu()
-  await getConversationList()
+  await kefuStore.updateConversation(rightClickConversation.value.id)
 }
 
 /** 删除会话 */
@@ -166,7 +161,7 @@ const deleteConversation = async () => {
   await KeFuConversationApi.deleteConversation(rightClickConversation.value.id)
   // 2. 关闭右键菜单,更新会话列表
   closeRightMenu()
-  await getConversationList()
+  kefuStore.deleteConversation(rightClickConversation.value.id)
 }
 
 /** 监听右键菜单的显示状态,添加点击事件监听器 */

+ 4 - 0
src/views/mall/promotion/kefu/components/KeFuMessageList.vue

@@ -152,6 +152,7 @@ import dayjs from 'dayjs'
 import relativeTime from 'dayjs/plugin/relativeTime'
 import { debounce } from 'lodash-es'
 import { jsonParse } from '@/utils'
+import { useMallKefuStore } from '@/store/modules/mall/kefu'
 
 dayjs.extend(relativeTime)
 
@@ -169,6 +170,7 @@ const queryParams = reactive({
 })
 const total = ref(0) // 消息总条数
 const refreshContent = ref(false) // 内容刷新,主要解决会话消息页面高度不一致导致的滚动功能精度失效
+const kefuStore = useMallKefuStore() // 客服缓存
 
 /** 获悉消息内容 */
 const getMessageContent = computed(() => (item: any) => jsonParse(item.content))
@@ -297,6 +299,8 @@ const sendMessage = async (msg: any) => {
   message.value = ''
   // 加载消息列表
   await refreshMessageList()
+  // 异步刷新
+  kefuStore.updateConversation(conversation.value.id)
 }
 
 /** 滚动到底部 */

+ 10 - 12
src/views/mall/promotion/kefu/index.vue

@@ -3,7 +3,7 @@
     <!-- 会话列表 -->
     <KeFuConversationList ref="keFuConversationRef" @change="handleChange" />
     <!-- 会话详情(选中会话的消息列表) -->
-    <KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList" />
+    <KeFuMessageList ref="keFuChatBoxRef" />
     <!-- 会员信息(选中会话的会员信息) -->
     <MemberInfo ref="memberInfoRef" />
   </el-container>
@@ -15,10 +15,12 @@ import { WebSocketMessageTypeConstants } from './components/tools/constants'
 import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
 import { getRefreshToken } from '@/utils/auth'
 import { useWebSocket } from '@vueuse/core'
+import { useMallKefuStore } from '@/store/modules/mall/kefu'
 
 defineOptions({ name: 'KeFu' })
 
 const message = useMessage() // 消息弹窗
+const kefuStore = useMallKefuStore() // 客服缓存
 
 // ======================= WebSocket start =======================
 const server = ref(
@@ -53,29 +55,24 @@ watchEffect(() => {
     }
     // 2.2 消息类型:KEFU_MESSAGE_TYPE
     if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_TYPE) {
+      const message = JSON.parse(jsonMessage.content)
       // 刷新会话列表
       // TODO @puhui999:不应该刷新列表,而是根据消息,本地 update 列表的数据;
-      getConversationList()
+      kefuStore.updateConversation(message.conversationId)
       // 刷新消息列表
-      keFuChatBoxRef.value?.refreshMessageList(JSON.parse(jsonMessage.content))
+      keFuChatBoxRef.value?.refreshMessageList(message)
       return
     }
     // 2.3 消息类型:KEFU_MESSAGE_ADMIN_READ
     if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_ADMIN_READ) {
-      // 刷新会话列表
-      // TODO @puhui999:不应该刷新列表,而是根据消息,本地 update 列表的数据;
-      getConversationList()
+      // 更新会话已读
+      kefuStore.updateConversationStatus(JSON.parse(jsonMessage.content)?.id)
     }
   } catch (error) {
     console.error(error)
   }
 })
 // ======================= WebSocket end =======================
-/** 加载会话列表 */
-const keFuConversationRef = ref<InstanceType<typeof KeFuConversationList>>()
-const getConversationList = () => {
-  keFuConversationRef.value?.getConversationList()
-}
 
 /** 加载指定会话的消息列表 */
 const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>()
@@ -87,7 +84,8 @@ const handleChange = (conversation: KeFuConversationRespVO) => {
 
 /** 初始化 */
 onMounted(() => {
-  getConversationList()
+  /** 加载会话列表 */
+  kefuStore.setConversationList()
   // 打开 websocket 连接
   open()
 })