【功能完善】商城客服: 客服布局样式调整

This commit is contained in:
puhui999 2024-11-04 15:25:43 +08:00
parent 1d01955b8b
commit 34fe09e2db
7 changed files with 155 additions and 79 deletions

View File

@ -1,5 +1,6 @@
<template> <template>
<div class="kefu"> <el-aside class="kefu-conversation-aside p-10px h-100%" width="260px">
<div class="color-[#999] font-bold my-10px">会话记录({{ conversationList.length }})</div>
<div <div
v-for="item in conversationList" v-for="item in conversationList"
:key="item.id" :key="item.id"
@ -22,7 +23,7 @@
<div class="ml-10px w-100%"> <div class="ml-10px w-100%">
<div class="flex justify-between items-center w-100%"> <div class="flex justify-between items-center w-100%">
<span class="username">{{ item.userNickname }}</span> <span class="username">{{ item.userNickname }}</span>
<span class="color-[var(--left-menu-text-color)]" style="font-size: 13px"> <span class="color-[#999]" style="font-size: 13px">
{{ formatPast(item.lastMessageTime, 'YYYY-MM-DD') }} {{ formatPast(item.lastMessageTime, 'YYYY-MM-DD') }}
</span> </span>
</div> </div>
@ -31,7 +32,7 @@
v-dompurify-html=" v-dompurify-html="
getConversationDisplayText(item.lastMessageContentType, item.lastMessageContent) getConversationDisplayText(item.lastMessageContentType, item.lastMessageContent)
" "
class="last-message flex items-center color-[var(--left-menu-text-color)]" class="last-message flex items-center color-[#999]"
> >
</div> </div>
</div> </div>
@ -65,7 +66,7 @@
取消 取消
</li> </li>
</ul> </ul>
</div> </el-aside>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -179,7 +180,9 @@ watch(showRightMenu, (val) => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.kefu { .kefu-conversation-aside {
background-color: #fff;
&-conversation { &-conversation {
height: 60px; height: 60px;
padding: 10px; padding: 10px;
@ -206,12 +209,13 @@ watch(showRightMenu, (val) => {
} }
.active { .active {
border-left: 5px #3271ff solid; //border-left: 5px #96afea solid;
background-color: var(--login-bg-color); background-color: rgba(128, 128, 128, 0.5); //
border-radius: 8px;
} }
.pinned { .pinned {
background-color: var(--left-menu-bg-active-color); background-color: rgba(128, 128, 128, 0.5); //
} }
.right-menu-ul { .right-menu-ul {

View File

@ -1,11 +1,11 @@
<template> <template>
<el-container v-if="showKeFuMessageList" class="kefu"> <el-container v-if="showKeFuMessageList" class="kefu">
<el-header> <el-header class="kefu-header">
<div class="kefu-title">{{ conversation.userNickname }}</div> <div class="kefu-title">{{ conversation.userNickname }}</div>
</el-header> </el-header>
<el-main class="kefu-content overflow-visible"> <el-main class="kefu-content overflow-visible">
<el-scrollbar ref="scrollbarRef" always height="calc(100vh - 495px)" @scroll="handleScroll"> <el-scrollbar ref="scrollbarRef" always @scroll="handleScroll">
<div v-if="refreshContent" ref="innerRef" class="w-[100%] pb-3px"> <div v-if="refreshContent" ref="innerRef" class="w-[100%] px-10px">
<!-- 消息列表 --> <!-- 消息列表 -->
<div v-for="(item, index) in getMessageList0" :key="item.id" class="w-[100%]"> <div v-for="(item, index) in getMessageList0" :key="item.id" class="w-[100%]">
<div class="flex justify-center items-center mb-20px"> <div class="flex justify-center items-center mb-20px">
@ -43,7 +43,9 @@
class="w-60px h-60px" class="w-60px h-60px"
/> />
<div <div
:class="{ 'kefu-message': KeFuMessageContentTypeEnum.TEXT === item.contentType }" :class="{
'kefu-message': KeFuMessageContentTypeEnum.TEXT === item.contentType
}"
class="p-10px" class="p-10px"
> >
<!-- 文本消息 --> <!-- 文本消息 -->
@ -71,10 +73,10 @@
<MessageItem :message="item"> <MessageItem :message="item">
<ProductItem <ProductItem
v-if="KeFuMessageContentTypeEnum.PRODUCT === item.contentType" v-if="KeFuMessageContentTypeEnum.PRODUCT === item.contentType"
:spuId="getMessageContent(item).spuId"
:picUrl="getMessageContent(item).picUrl" :picUrl="getMessageContent(item).picUrl"
:price="getMessageContent(item).price" :price="getMessageContent(item).price"
:skuText="getMessageContent(item).introduction" :skuText="getMessageContent(item).introduction"
:spuId="getMessageContent(item).spuId"
:title="getMessageContent(item).spuName" :title="getMessageContent(item).spuName"
:titleWidth="400" :titleWidth="400"
class="max-w-70%" class="max-w-70%"
@ -108,23 +110,29 @@
<Icon class="ml-5px" icon="ep:bottom" /> <Icon class="ml-5px" icon="ep:bottom" />
</div> </div>
</el-main> </el-main>
<el-footer height="230px"> <el-footer class="kefu-footer">
<div class="h-[100%]"> <div class="chat-tools flex items-center">
<div class="chat-tools flex items-center"> <EmojiSelectPopover @select-emoji="handleEmojiSelect" />
<EmojiSelectPopover @select-emoji="handleEmojiSelect" /> <PictureSelectUpload
<PictureSelectUpload class="ml-15px mt-3px cursor-pointer"
class="ml-15px mt-3px cursor-pointer" @send-picture="handleSendPicture"
@send-picture="handleSendPicture" />
/>
</div>
<el-input v-model="message" :rows="6" style="border-style: none" type="textarea" />
<div class="h-45px flex justify-end">
<el-button class="mt-10px" type="primary" @click="handleSendMessage">发送</el-button>
</div>
</div> </div>
<el-input
v-model="message"
:rows="6"
placeholder="输入消息Enter发送Shift+Enter换行"
style="border-style: none"
type="textarea"
@keyup.enter.prevent="handleSendMessage"
/>
</el-footer> </el-footer>
</el-container> </el-container>
<el-empty v-else description="请选择左侧的一个会话后开始" /> <el-container v-else class="kefu">
<el-main>
<el-empty description="请选择左侧的一个会话后开始" />
</el-main>
</el-container>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -262,7 +270,11 @@ const handleSendPicture = async (picUrl: string) => {
} }
/** 发送文本消息 */ /** 发送文本消息 */
const handleSendMessage = async () => { const handleSendMessage = async (event: any) => {
// shift
if (event.shiftKey) {
return
}
// 1. // 1.
if (isEmpty(unref(message.value))) { if (isEmpty(unref(message.value))) {
messageTool.notifyWarning('请输入消息后再发送哦!') messageTool.notifyWarning('请输入消息后再发送哦!')
@ -357,14 +369,29 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
<style lang="scss" scoped> <style lang="scss" scoped>
.kefu { .kefu {
&-title { background-color: #fff;
border-bottom: #e4e0e0 solid 1px; width: calc(100% - 300px - 260px);
height: 60px; border-left: var(--el-border-color) solid 1px;
line-height: 60px;
.kefu-header {
background: #fbfbfb;
box-shadow: 0 0 0 0 #dcdfe6;
display: flex;
align-items: center;
justify-content: space-between;
&-title {
font-size: 18px;
font-weight: bold;
}
} }
&-content { &-content {
margin: 0;
padding: 0;
position: relative; position: relative;
height: 100%;
width: 100%;
.newMessageTip { .newMessageTip {
position: absolute; position: absolute;
@ -447,21 +474,36 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
border-radius: 12rpx; border-radius: 12rpx;
padding: 8rpx 16rpx; padding: 8rpx 16rpx;
margin-bottom: 16rpx; margin-bottom: 16rpx;
//background-color: #e8e8e8;
color: #999; color: #999;
font-size: 24rpx; font-size: 24rpx;
} }
} }
.chat-tools { .kefu-footer {
width: 100%; display: flex;
border: var(--el-border-color) solid 1px; flex-direction: column;
border-radius: 10px; height: auto;
height: 44px; margin: 0;
padding: 0;
border-top: var(--el-border-color) solid 1px;
.chat-tools {
width: 100%;
height: 44px;
}
} }
::v-deep(textarea) { ::v-deep(textarea) {
resize: none; resize: none;
} }
:deep(.el-input__wrapper) {
box-shadow: none !important;
border-radius: 0;
}
::v-deep(.el-textarea__inner) {
box-shadow: none !important;
}
} }
</style> </style>

View File

@ -1,5 +1,5 @@
import KeFuConversationList from './KeFuConversationList.vue' import KeFuConversationList from './KeFuConversationList.vue'
import KeFuMessageList from './KeFuMessageList.vue' import KeFuMessageList from './KeFuMessageList.vue'
import MemberBrowsingHistory from './history/MemberBrowsingHistory.vue' import MemberInfo from './member/MemberInfo.vue'
export { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory } export { KeFuConversationList, KeFuMessageList, MemberInfo }

View File

@ -1,21 +1,24 @@
<!-- 目录是不是叫 member 好点然后这个组件是 MemberInfo里面有浏览足迹 --> <!-- 目录是不是叫 member 好点然后这个组件是 MemberInfo里面有浏览足迹 -->
<template> <template>
<div v-show="!isEmpty(conversation)" class="kefu"> <el-container class="kefu">
<div class="header-title h-60px flex justify-center items-center">他的足迹</div> <el-header class="kefu-header">
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick"> <el-tabs v-model="activeName" class="kefu-tabs" @tab-click="handleClick">
<el-tab-pane label="最近浏览" name="a" /> <el-tab-pane label="最近浏览" name="a" />
<el-tab-pane label="订单列表" name="b" /> <el-tab-pane label="订单列表" name="b" />
</el-tabs> </el-tabs>
<div> </el-header>
<el-scrollbar ref="scrollbarRef" always height="calc(115vh - 400px)" @scroll="handleScroll"> <el-main class="kefu-content">
<!-- 最近浏览 --> <div v-show="!isEmpty(conversation)">
<ProductBrowsingHistory v-if="activeName === 'a'" ref="productBrowsingHistoryRef" /> <el-scrollbar ref="scrollbarRef" always @scroll="handleScroll">
<!-- 订单列表 --> <!-- 最近浏览 -->
<OrderBrowsingHistory v-if="activeName === 'b'" ref="orderBrowsingHistoryRef" /> <ProductBrowsingHistory v-if="activeName === 'a'" ref="productBrowsingHistoryRef" />
</el-scrollbar> <!-- 订单列表 -->
</div> <OrderBrowsingHistory v-if="activeName === 'b'" ref="orderBrowsingHistoryRef" />
</div> </el-scrollbar>
<el-empty v-show="isEmpty(conversation)" description="请选择左侧的一个会话后开始" /> </div>
<el-empty v-show="isEmpty(conversation)" description="请选择左侧的一个会话后开始" />
</el-main>
</el-container>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -91,6 +94,41 @@ const handleScroll = debounce(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.kefu {
margin: 0;
padding: 0;
height: 100%;
width: 300px !important;
background-color: #fff;
border-left: var(--el-border-color) solid 1px;
&-header {
background: #fbfbfb;
box-shadow: 0 0 0 0 #dcdfe6;
display: flex;
align-items: center;
justify-content: center;
&-title {
font-size: 18px;
font-weight: bold;
}
}
&-content {
margin: 0;
padding: 0;
position: relative;
height: 100%;
width: 100%;
}
&-tabs {
height: 100%;
width: 100%;
}
}
.header-title { .header-title {
border-bottom: #e4e0e0 solid 1px; border-bottom: #e4e0e0 solid 1px;
} }

View File

@ -1,28 +1,16 @@
<template> <template>
<el-row :gutter="10"> <el-container class="kefu-layout">
<!-- 会话列表 --> <!-- 会话列表 -->
<el-col :span="6"> <KeFuConversationList ref="keFuConversationRef" @change="handleChange" />
<ContentWrap>
<KeFuConversationList ref="keFuConversationRef" @change="handleChange" />
</ContentWrap>
</el-col>
<!-- 会话详情选中会话的消息列表 --> <!-- 会话详情选中会话的消息列表 -->
<el-col :span="12"> <KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList" />
<ContentWrap>
<KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList" />
</ContentWrap>
</el-col>
<!-- 会员足迹选中会话的会员足迹 --> <!-- 会员足迹选中会话的会员足迹 -->
<el-col :span="6"> <MemberInfo ref="memberInfoRef" />
<ContentWrap> </el-container>
<MemberBrowsingHistory ref="memberBrowsingHistoryRef" />
</ContentWrap>
</el-col>
</el-row>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory } from './components' import { KeFuConversationList, KeFuMessageList, MemberInfo } from './components'
import { WebSocketMessageTypeConstants } from './components/tools/constants' import { WebSocketMessageTypeConstants } from './components/tools/constants'
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation' import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
import { getRefreshToken } from '@/utils/auth' import { getRefreshToken } from '@/utils/auth'
@ -91,10 +79,10 @@ const getConversationList = () => {
/** 加载指定会话的消息列表 */ /** 加载指定会话的消息列表 */
const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>() const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>()
const memberBrowsingHistoryRef = ref<InstanceType<typeof MemberBrowsingHistory>>() const memberInfoRef = ref<InstanceType<typeof MemberInfo>>()
const handleChange = (conversation: KeFuConversationRespVO) => { const handleChange = (conversation: KeFuConversationRespVO) => {
keFuChatBoxRef.value?.getNewMessageList(conversation) keFuChatBoxRef.value?.getNewMessageList(conversation)
memberBrowsingHistoryRef.value?.initHistory(conversation) memberInfoRef.value?.initHistory(conversation)
} }
/** 初始化 */ /** 初始化 */
@ -112,9 +100,13 @@ onBeforeUnmount(() => {
</script> </script>
<style lang="scss"> <style lang="scss">
.kefu { .kefu-layout {
height: calc(100vh - 165px); position: absolute;
overflow: auto; /* 确保内容可滚动 */ flex: 1;
top: 0;
left: 0;
height: 100%;
width: 100%;
} }
/* 定义滚动条样式 */ /* 定义滚动条样式 */