Ver código fonte

【新增】:mall 客服消息样式设计

puhui999 9 meses atrás
pai
commit
d3b4063b94

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

@@ -16,7 +16,7 @@ export interface KeFuConversationRespVO {
   /**
    * 会话所属用户昵称
    */
-  nickname: string
+  userNickname: string
   /**
    * 最后聊天时间
    */

+ 70 - 0
src/api/mall/promotion/kefu/message/index.ts

@@ -0,0 +1,70 @@
+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
+}
+
+// 客服会话 API
+export const KeFuMessageApi = {
+  // 发送客服消息
+  sendKeFuMessage: async (data: any) => {
+    return await request.put({
+      url: '/promotion/kefu-message/send',
+      data
+    })
+  },
+  // 更新客服消息已读状态
+  updateKeFuMessageReadStatus: async (data: any) => {
+    return await request.put({
+      url: '/promotion/kefu-message/update-read-status',
+      data
+    })
+  },
+  // 获得消息分页数据
+  getKeFuMessagePage: async (params: any) => {
+    return await request.get({ url: '/promotion/kefu-message/page', params })
+  }
+}

+ 167 - 0
src/views/mall/promotion/kefu/components/KeFuChatBox.vue

@@ -0,0 +1,167 @@
+<template>
+  <el-container class="kefu">
+    <el-header>
+      <div class="kefu-title">{{ keFuConversation.userNickname }}</div>
+    </el-header>
+    <el-main class="kefu-content">
+      <div
+        v-for="item in messageList"
+        :key="item.id"
+        :class="[
+          item.senderType === UserTypeEnum.MEMBER
+            ? `ss-row-left`
+            : item.senderType === UserTypeEnum.ADMIN
+              ? `ss-row-right`
+              : ''
+        ]"
+        class="flex mb-20px w-[100%]"
+      >
+        <el-avatar
+          v-show="item.senderType === UserTypeEnum.MEMBER"
+          :src="keFuConversation.userAvatar"
+          alt="avatar"
+        />
+        <div class="kefu-message flex items-center p-10px">
+          <!-- 文本消息 -->
+          <template v-if="KeFuMessageContentTypeEnum.TEXT === item.contentType">
+            <div
+              v-dompurify-html="replaceEmoji(item.content)"
+              :class="[
+                item.senderType === UserTypeEnum.MEMBER
+                  ? `ml-10px`
+                  : item.senderType === UserTypeEnum.ADMIN
+                    ? `mr-10px`
+                    : ''
+              ]"
+            ></div>
+          </template>
+          <template v-else>
+            {{ item.content }}
+          </template>
+        </div>
+        <el-avatar
+          v-show="item.senderType === UserTypeEnum.ADMIN"
+          :src="item.senderAvatar"
+          alt="avatar"
+        />
+      </div>
+    </el-main>
+    <el-footer height="230px">
+      <div class="h-[100%]">
+        <div class="chat-tools">
+          <Icon :size="30" class="ml-10px" icon="fa:frown-o" />
+        </div>
+        <el-input v-model="message" :rows="6" type="textarea" />
+        <div class="h-45px flex justify-end">
+          <el-button class="mt-10px" type="primary">发送</el-button>
+        </div>
+      </div>
+    </el-footer>
+  </el-container>
+  <!-- 没选择左侧会话时显示空界面 -->
+</template>
+
+<script lang="ts" setup>
+import { KeFuMessageApi, KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
+import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
+import { UserTypeEnum } from '@/utils/constants'
+import { replaceEmoji } from '@/views/mall/promotion/kefu/components/emoji'
+import { KeFuMessageContentTypeEnum } from '@/views/mall/promotion/kefu/components/constants'
+
+defineOptions({ name: 'KeFuMessageBox' })
+const message = ref('') // 消息
+const messageList = ref<KeFuMessageRespVO[]>([]) // 消息列表
+const keFuConversation = ref<KeFuConversationRespVO>({} as KeFuConversationRespVO) // 用户会话
+// 获得消息
+const getMessageList = async (conversation: KeFuConversationRespVO) => {
+  keFuConversation.value = conversation
+  const { list } = await KeFuMessageApi.getKeFuMessagePage({
+    pageNo: 1,
+    conversationId: conversation.id
+  })
+  messageList.value = list
+}
+defineExpose({ getMessageList })
+</script>
+
+<style lang="scss" scoped>
+.kefu {
+  &-title {
+    border-bottom: #e4e0e0 solid 1px;
+    height: 60px;
+    line-height: 60px;
+  }
+
+  &-content {
+    .ss-row-left {
+      justify-content: flex-start;
+
+      .kefu-message {
+        margin-left: 20px;
+        position: relative;
+
+        &::before {
+          content: '';
+          width: 10px;
+          height: 10px;
+          left: -19px;
+          top: calc(50% - 10px);
+          position: absolute;
+          border-left: 5px solid transparent;
+          border-bottom: 5px solid transparent;
+          border-top: 5px solid transparent;
+          border-right: 5px solid #ffffff;
+        }
+      }
+    }
+
+    .ss-row-right {
+      justify-content: flex-end;
+
+      .kefu-message::after {
+        content: '';
+        width: 10px;
+        height: 10px;
+        right: -19px;
+        top: calc(50% - 10px);
+        position: absolute;
+        border-left: 5px solid #ffffff;
+        border-bottom: 5px solid transparent;
+        border-top: 5px solid transparent;
+        border-right: 5px solid transparent;
+      }
+    }
+
+    .kefu-message {
+      color: #333;
+      border-radius: 5px;
+      box-shadow: 3px 5px 15px rgba(0, 0, 0, 0.2);
+      padding: 5px 10px;
+      width: auto;
+      max-width: 50%;
+      text-align: left;
+      display: inline-block !important;
+      position: relative;
+      word-break: break-all;
+      background-color: #ffffff;
+      transition: all 0.2s;
+
+      &:hover {
+        transform: scale(1.03);
+      }
+    }
+  }
+
+  .chat-tools {
+    width: 100%;
+    border: #e4e0e0 solid 1px;
+    height: 44px;
+    display: flex;
+    align-items: center;
+  }
+
+  ::v-deep(textarea) {
+    resize: none;
+  }
+}
+</style>

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

@@ -10,7 +10,7 @@
       <div class="kefu-conversation-left flex justify-center items-center">
         <el-avatar :src="item.userAvatar" alt="avatar" />
         <div class="ml-10px">
-          <div class="nickname">{{ item.nickname }}</div>
+          <div class="nickname">{{ item.userNickname }}</div>
           <div
             v-dompurify-html="replaceEmoji(item.lastMessageContent)"
             class="last-message flex items-center color-[#989EA6]"
@@ -41,7 +41,7 @@ const getConversationList = async () => {
       userId: 283,
       userAvatar:
         'https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKMezSxtOImrC9lbhwHiazYwck3xwrEcO7VJfG6WQo260whaeVNoByE5RreiaGsGfOMlIiaDhSaA991w/132',
-      nickname: '辉辉鸭' + i,
+      userNickname: '辉辉鸭' + i,
       lastMessageTime: getNowDateTime(),
       lastMessageContent: '[爱心][爱心]你好哇',
       lastMessageContentType: 1,
@@ -54,12 +54,12 @@ const getConversationList = async () => {
 }
 defineExpose({ getConversationList })
 const emits = defineEmits<{
-  (e: 'change', v: number): void
+  (e: 'change', v: KeFuConversationRespVO): void
 }>()
 // 打开右侧消息
 const openRightMessage = (item: KeFuConversationRespVO, index: number) => {
   activeConversationIndex.value = index
-  emits('change', item.id)
+  emits('change', item)
 }
 </script>
 

+ 0 - 77
src/views/mall/promotion/kefu/components/KefuChatBox.vue

@@ -1,77 +0,0 @@
-<template>
-  <el-container class="kefu">
-    <el-header>
-      <div class="kefu-title">芋道</div>
-    </el-header>
-    <el-main class="kefu-content">
-      <div
-        v-for="item in 100"
-        :key="item"
-        :class="[item % 2 === 0 ? `ss-row-left` : `ss-row-right`]"
-        class="flex mb-20px w-[100%]"
-      >
-        <el-avatar
-          v-if="item % 2 === 0"
-          alt="avatar"
-          src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
-        />
-        <div class="ml-10px"> Lorem Ipsum,也称乱数假文或者哑元文本</div>
-        <el-avatar
-          v-if="item % 2 !== 0"
-          alt="avatar"
-          src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
-        />
-      </div>
-    </el-main>
-    <el-footer height="230px">
-      <div class="h-[100%]">
-        <div class="chat-tools">
-          <Icon :size="30" class="ml-10px" icon="fa:frown-o" />
-        </div>
-        <el-input v-model="message" :autosize="{ minRows: 6, maxRows: 6 }" type="textarea" />
-        <div class="h-45px flex justify-end">
-          <el-button class="mt-10px" type="primary">发送</el-button>
-        </div>
-      </div>
-    </el-footer>
-  </el-container>
-  <!-- 没选择左侧会话时显示空界面 -->
-</template>
-
-<script lang="ts" setup>
-defineOptions({ name: 'KeFuMessageBox' })
-const message = ref('')
-</script>
-
-<style lang="scss" scoped>
-.kefu {
-  &-title {
-    border-bottom: #e4e0e0 solid 1px;
-    height: 60px;
-    line-height: 60px;
-  }
-
-  &-content {
-  }
-
-  .chat-tools {
-    width: 100%;
-    border: #e4e0e0 solid 1px;
-    height: 44px;
-    display: flex;
-    align-items: center;
-  }
-
-  .ss-row-left {
-    justify-content: flex-start;
-  }
-
-  .ss-row-right {
-    justify-content: flex-end;
-  }
-
-  ::v-deep(textarea) {
-    resize: none;
-  }
-}
-</style>

+ 0 - 4
src/views/mall/promotion/kefu/components/constants.ts

@@ -8,7 +8,3 @@ export const KeFuMessageContentTypeEnum = {
   PRODUCT: 10, //  商品消息
   ORDER: 11 //  订单消息"
 }
-export const UserTypeEnum = {
-  MEMBER: 1, // 会员 面向 c 端,普通用户
-  ADMIN: 2 // 管理员 面向 b 端,管理后台
-}

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

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

+ 6 - 1
src/views/mall/promotion/kefu/index.vue

@@ -15,12 +15,17 @@
 
 <script lang="ts" setup>
 import { KeFuChatBox, KeFuConversationBox } from './components'
+import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
 
 defineOptions({ name: 'KeFu' })
 
+// 加载消息
 const keFuChatBoxRef = ref<InstanceType<typeof KeFuChatBox>>()
-const handleChange = () => {}
+const handleChange = (conversation: KeFuConversationRespVO) => {
+  keFuChatBoxRef.value?.getMessageList(conversation)
+}
 
+// 加载会话
 const keFuConversationRef = ref<InstanceType<typeof KeFuConversationBox>>()
 onMounted(() => {
   keFuConversationRef.value?.getConversationList()