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

pull/468/head
puhui999 2024-07-01 16:15:27 +08:00
parent d3614cbbac
commit d3b4063b94
8 changed files with 249 additions and 88 deletions

View File

@ -16,7 +16,7 @@ export interface KeFuConversationRespVO {
/**
*
*/
nickname: string
userNickname: string
/**
*
*/

View File

@ -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 })
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

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

View File

@ -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 }

View File

@ -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()