فهرست منبع

【新增】:mall 客服会话未读消息展示和消息已读处理

puhui999 11 ماه پیش
والد
کامیت
9ab367e4f4

+ 12 - 49
src/api/mall/promotion/kefu/conversation/index.ts

@@ -1,55 +1,18 @@
 import request from '@/config/axios'
 
-// TODO @puhui999:注释要不放在属性后面,避免太长哈
 export interface KeFuConversationRespVO {
-  /**
-   * 编号
-   */
-  id: number
-  /**
-   * 会话所属用户
-   */
-  userId: number
-  /**
-   * 会话所属用户头像
-   */
-  userAvatar: string
-  /**
-   * 会话所属用户昵称
-   */
-  userNickname: string
-  /**
-   * 最后聊天时间
-   */
-  lastMessageTime: Date
-  /**
-   * 最后聊天内容
-   */
-  lastMessageContent: string
-  /**
-   * 最后发送的消息类型
-   */
-  lastMessageContentType: number
-  /**
-   * 管理端置顶
-   */
-  adminPinned: boolean
-  /**
-   * 用户是否可见
-   */
-  userDeleted: boolean
-  /**
-   * 管理员是否可见
-   */
-  adminDeleted: boolean
-  /**
-   * 管理员未读消息数
-   */
-  adminUnreadMessageCount: number
-  /**
-   * 创建时间
-   */
-  createTime?: string
+  id: number // 编号
+  userId: number // 会话所属用户
+  userAvatar: string // 会话所属用户头像
+  userNickname: string // 会话所属用户昵称
+  lastMessageTime: Date // 最后聊天时间
+  lastMessageContent: string // 最后聊天内容
+  lastMessageContentType: number // 最后发送的消息类型
+  adminPinned: boolean // 管理端置顶
+  userDeleted: boolean // 用户是否可见
+  adminDeleted: boolean // 管理员是否可见
+  adminUnreadMessageCount: number // 管理员未读消息数
+  createTime?: string // 创建时间
 }
 
 // 客服会话 API

+ 13 - 47
src/api/mall/promotion/kefu/message/index.ts

@@ -1,50 +1,17 @@
 import request from '@/config/axios'
 
 export interface KeFuMessageRespVO {
-  /**
-   * 编号
-   */
-  id: number
-  /**
-   * 会话编号
-   */
-  conversationId: number
-  /**
-   * 发送人编号
-   */
-  senderId: number
-  /**
-   * 发送人头像
-   */
-  senderAvatar: string
-  /**
-   * 发送人类型
-   */
-  senderType: number
-  /**
-   * 接收人编号
-   */
-  receiverId: number
-  /**
-   * 接收人类型
-   */
-  receiverType: number
-  /**
-   * 消息类型
-   */
-  contentType: number
-  /**
-   * 消息
-   */
-  content: string
-  /**
-   * 是否已读
-   */
-  readStatus: boolean
-  /**
-   * 创建时间
-   */
-  createTime: Date
+  id: number // 编号
+  conversationId: number // 会话编号
+  senderId: number // 发送人编号
+  senderAvatar: string // 发送人头像
+  senderType: number // 发送人类型
+  receiverId: number // 接收人编号
+  receiverType: number // 接收人类型
+  contentType: number // 消息类型
+  content: string // 消息
+  readStatus: boolean // 是否已读
+  createTime: Date // 创建时间
 }
 
 // 客服会话 API
@@ -57,10 +24,9 @@ export const KeFuMessageApi = {
     })
   },
   // 更新客服消息已读状态
-  updateKeFuMessageReadStatus: async (data: any) => {
+  updateKeFuMessageReadStatus: async (conversationId: number) => {
     return await request.put({
-      url: '/promotion/kefu-message/update-read-status',
-      data
+      url: '/promotion/kefu-message/update-read-status?conversationId=' + conversationId
     })
   },
   // 获得消息分页数据

+ 7 - 3
src/views/mall/promotion/kefu/components/KeFuChatBox.vue

@@ -18,12 +18,12 @@
                 {{ formatDate(item.createTime) }}
               </div>
               <!-- 系统消息 -->
-              <view
+              <div
                 v-if="item.contentType === KeFuMessageContentTypeEnum.SYSTEM"
                 class="system-message"
               >
                 {{ item.content }}
-              </view>
+              </div>
             </div>
             <div
               :class="[
@@ -154,9 +154,10 @@ const handleSendMessage = async () => {
 
 // 发送消息 【共用】
 const sendMessage = async (msg: any) => {
+  // 发送消息
   await KeFuMessageApi.sendKeFuMessage(msg)
   message.value = ''
-  // 3. 加载消息列表
+  // 加载消息列表
   await getMessageList(keFuConversation.value)
   // 滚动到最新消息处
   await scrollToBottom()
@@ -166,8 +167,11 @@ const innerRef = ref<HTMLDivElement>()
 const scrollbarRef = ref<InstanceType<typeof ElScrollbarType>>()
 // 滚动到底部
 const scrollToBottom = async () => {
+  // 1. 滚动到最新消息
   await nextTick()
   scrollbarRef.value!.setScrollTop(innerRef.value!.clientHeight)
+  // 2. 消息已读
+  await KeFuMessageApi.updateKeFuMessageReadStatus(keFuConversation.value.id)
 }
 /**
  * 是否显示时间

+ 32 - 3
src/views/mall/promotion/kefu/components/KeFuConversationBox.vue

@@ -8,7 +8,15 @@
       @click="openRightMessage(item, index)"
     >
       <div class="flex justify-center items-center w-100%">
-        <el-avatar :src="item.userAvatar" alt="avatar" />
+        <div class="flex justify-center items-center" style="width: 50px; height: 50px">
+          <el-badge
+            :hidden="item.adminUnreadMessageCount === 0"
+            :max="99"
+            :value="item.adminUnreadMessageCount"
+          >
+            <el-avatar :src="item.userAvatar" alt="avatar" />
+          </el-badge>
+        </div>
         <div class="ml-10px w-100%">
           <div class="flex justify-between items-center w-100%">
             <span>{{ item.userNickname }}</span>
@@ -24,8 +32,10 @@
             ></div>
           </template>
           <!-- 图片消息 -->
-          <template v-if="KeFuMessageContentTypeEnum.IMAGE === item.lastMessageContentType">
-            <div class="last-message flex items-center color-[#989EA6]">【图片消息】</div>
+          <template v-else>
+            <div class="last-message flex items-center color-[#989EA6]">
+              {{ getContentType(item.lastMessageContentType) }}
+            </div>
           </template>
         </div>
       </div>
@@ -55,6 +65,25 @@ const openRightMessage = (item: KeFuConversationRespVO, index: number) => {
   activeConversationIndex.value = index
   emits('change', item)
 }
+// 获得消息类型
+const getContentType = computed(() => (lastMessageContentType: number) => {
+  switch (lastMessageContentType) {
+    case KeFuMessageContentTypeEnum.SYSTEM:
+      return '[系统消息]'
+    case KeFuMessageContentTypeEnum.VIDEO:
+      return '[视频消息]'
+    case KeFuMessageContentTypeEnum.IMAGE:
+      return '[图片消息]'
+    case KeFuMessageContentTypeEnum.PRODUCT:
+      return '[商品消息]'
+    case KeFuMessageContentTypeEnum.ORDER:
+      return '[订单消息]'
+    case KeFuMessageContentTypeEnum.VOICE:
+      return '[语音消息]'
+    default:
+      return ''
+  }
+})
 </script>
 
 <style lang="scss" scoped>

+ 1 - 2
src/views/mall/promotion/kefu/components/index.ts

@@ -1,5 +1,4 @@
 import KeFuConversationBox from './KeFuConversationBox.vue'
 import KeFuChatBox from './KeFuChatBox.vue'
-import * as Constants from './tools/constants'
 
-export { KeFuConversationBox, KeFuChatBox, Constants }
+export { KeFuConversationBox, KeFuChatBox }

+ 9 - 7
src/views/mall/promotion/kefu/index.vue

@@ -7,7 +7,7 @@
     </el-col>
     <el-col :span="16">
       <ContentWrap>
-        <KeFuChatBox ref="keFuChatBoxRef" />
+        <KeFuChatBox ref="keFuChatBoxRef" @change="getConversationList" />
       </ContentWrap>
     </el-col>
   </el-row>
@@ -15,13 +15,14 @@
 
 <script lang="ts" setup>
 import { KeFuChatBox, KeFuConversationBox } from './components'
+import { WebSocketMessageTypeConstants } from './components/tools/constants'
 import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
 import { getAccessToken } from '@/utils/auth'
 import { useWebSocket } from '@vueuse/core'
-import { WebSocketMessageTypeConstants } from '@/views/mall/promotion/kefu/components/tools/constants'
 
 defineOptions({ name: 'KeFu' })
 const message = useMessage()
+
 // 加载消息
 const keFuChatBoxRef = ref<InstanceType<typeof KeFuChatBox>>()
 const handleChange = (conversation: KeFuConversationRespVO) => {
@@ -47,10 +48,6 @@ watchEffect(() => {
   try {
     // 1. 收到心跳
     if (data.value === 'pong') {
-      // state.recordList.push({
-      //   text: '【心跳】',
-      //   time: new Date().getTime()
-      // })
       return
     }
 
@@ -63,12 +60,17 @@ watchEffect(() => {
     }
     // 2.2 消息类型:KEFU_MESSAGE_TYPE
     if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_TYPE) {
-      // 刷新列表
+      // 刷新会话列表
       getConversationList()
       // 刷新消息列表
       keFuChatBoxRef.value?.refreshMessageList()
       return
     }
+    // 2.3 消息类型:KEFU_MESSAGE_ADMIN_READ
+    if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_ADMIN_READ) {
+      // 刷新会话列表
+      getConversationList()
+    }
   } catch (error) {
     console.error(error)
   }