mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-11-04 12:18:43 +08:00 
			
		
		
		
	【功能完善】商城客服: 客服布局样式调整
This commit is contained in:
		@@ -1,5 +1,6 @@
 | 
			
		||||
<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
 | 
			
		||||
      v-for="item in conversationList"
 | 
			
		||||
      :key="item.id"
 | 
			
		||||
@@ -22,7 +23,7 @@
 | 
			
		||||
        <div class="ml-10px w-100%">
 | 
			
		||||
          <div class="flex justify-between items-center w-100%">
 | 
			
		||||
            <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') }}
 | 
			
		||||
            </span>
 | 
			
		||||
          </div>
 | 
			
		||||
@@ -31,7 +32,7 @@
 | 
			
		||||
            v-dompurify-html="
 | 
			
		||||
              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>
 | 
			
		||||
@@ -65,7 +66,7 @@
 | 
			
		||||
        取消
 | 
			
		||||
      </li>
 | 
			
		||||
    </ul>
 | 
			
		||||
  </div>
 | 
			
		||||
  </el-aside>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
@@ -179,7 +180,9 @@ watch(showRightMenu, (val) => {
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.kefu {
 | 
			
		||||
.kefu-conversation-aside {
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
 | 
			
		||||
  &-conversation {
 | 
			
		||||
    height: 60px;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
@@ -206,12 +209,13 @@ watch(showRightMenu, (val) => {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .active {
 | 
			
		||||
    border-left: 5px #3271ff solid;
 | 
			
		||||
    background-color: var(--login-bg-color);
 | 
			
		||||
    //border-left: 5px #96afea solid;
 | 
			
		||||
    background-color: rgba(128, 128, 128, 0.5); // 透明色,暗黑模式下也能体现
 | 
			
		||||
    border-radius: 8px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .pinned {
 | 
			
		||||
    background-color: var(--left-menu-bg-active-color);
 | 
			
		||||
    background-color: rgba(128, 128, 128, 0.5); // 透明色,暗黑模式下也能体现
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .right-menu-ul {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <el-container v-if="showKeFuMessageList" class="kefu">
 | 
			
		||||
    <el-header>
 | 
			
		||||
    <el-header class="kefu-header">
 | 
			
		||||
      <div class="kefu-title">{{ conversation.userNickname }}</div>
 | 
			
		||||
    </el-header>
 | 
			
		||||
    <el-main class="kefu-content overflow-visible">
 | 
			
		||||
      <el-scrollbar ref="scrollbarRef" always height="calc(100vh - 495px)" @scroll="handleScroll">
 | 
			
		||||
        <div v-if="refreshContent" ref="innerRef" class="w-[100%] pb-3px">
 | 
			
		||||
      <el-scrollbar ref="scrollbarRef" always @scroll="handleScroll">
 | 
			
		||||
        <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 class="flex justify-center items-center mb-20px">
 | 
			
		||||
@@ -43,7 +43,9 @@
 | 
			
		||||
                class="w-60px h-60px"
 | 
			
		||||
              />
 | 
			
		||||
              <div
 | 
			
		||||
                :class="{ 'kefu-message': KeFuMessageContentTypeEnum.TEXT === item.contentType }"
 | 
			
		||||
                :class="{
 | 
			
		||||
                  'kefu-message': KeFuMessageContentTypeEnum.TEXT === item.contentType
 | 
			
		||||
                }"
 | 
			
		||||
                class="p-10px"
 | 
			
		||||
              >
 | 
			
		||||
                <!-- 文本消息 -->
 | 
			
		||||
@@ -71,10 +73,10 @@
 | 
			
		||||
                <MessageItem :message="item">
 | 
			
		||||
                  <ProductItem
 | 
			
		||||
                    v-if="KeFuMessageContentTypeEnum.PRODUCT === item.contentType"
 | 
			
		||||
                    :spuId="getMessageContent(item).spuId"
 | 
			
		||||
                    :picUrl="getMessageContent(item).picUrl"
 | 
			
		||||
                    :price="getMessageContent(item).price"
 | 
			
		||||
                    :skuText="getMessageContent(item).introduction"
 | 
			
		||||
                    :spuId="getMessageContent(item).spuId"
 | 
			
		||||
                    :title="getMessageContent(item).spuName"
 | 
			
		||||
                    :titleWidth="400"
 | 
			
		||||
                    class="max-w-70%"
 | 
			
		||||
@@ -108,23 +110,29 @@
 | 
			
		||||
        <Icon class="ml-5px" icon="ep:bottom" />
 | 
			
		||||
      </div>
 | 
			
		||||
    </el-main>
 | 
			
		||||
    <el-footer height="230px">
 | 
			
		||||
      <div class="h-[100%]">
 | 
			
		||||
        <div class="chat-tools flex items-center">
 | 
			
		||||
          <EmojiSelectPopover @select-emoji="handleEmojiSelect" />
 | 
			
		||||
          <PictureSelectUpload
 | 
			
		||||
            class="ml-15px mt-3px cursor-pointer"
 | 
			
		||||
            @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>
 | 
			
		||||
    <el-footer class="kefu-footer">
 | 
			
		||||
      <div class="chat-tools flex items-center">
 | 
			
		||||
        <EmojiSelectPopover @select-emoji="handleEmojiSelect" />
 | 
			
		||||
        <PictureSelectUpload
 | 
			
		||||
          class="ml-15px mt-3px cursor-pointer"
 | 
			
		||||
          @send-picture="handleSendPicture"
 | 
			
		||||
        />
 | 
			
		||||
      </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-container>
 | 
			
		||||
  <el-empty v-else description="请选择左侧的一个会话后开始" />
 | 
			
		||||
  <el-container v-else class="kefu">
 | 
			
		||||
    <el-main>
 | 
			
		||||
      <el-empty description="请选择左侧的一个会话后开始" />
 | 
			
		||||
    </el-main>
 | 
			
		||||
  </el-container>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<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. 校验消息是否为空
 | 
			
		||||
  if (isEmpty(unref(message.value))) {
 | 
			
		||||
    messageTool.notifyWarning('请输入消息后再发送哦!')
 | 
			
		||||
@@ -357,14 +369,29 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.kefu {
 | 
			
		||||
  &-title {
 | 
			
		||||
    border-bottom: #e4e0e0 solid 1px;
 | 
			
		||||
    height: 60px;
 | 
			
		||||
    line-height: 60px;
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  width: calc(100% - 300px - 260px);
 | 
			
		||||
  border-left: var(--el-border-color) solid 1px;
 | 
			
		||||
 | 
			
		||||
  .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 {
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
 | 
			
		||||
    .newMessageTip {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
@@ -447,21 +474,36 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
 | 
			
		||||
      border-radius: 12rpx;
 | 
			
		||||
      padding: 8rpx 16rpx;
 | 
			
		||||
      margin-bottom: 16rpx;
 | 
			
		||||
      //background-color: #e8e8e8;
 | 
			
		||||
      color: #999;
 | 
			
		||||
      font-size: 24rpx;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .chat-tools {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    border: var(--el-border-color) solid 1px;
 | 
			
		||||
    border-radius: 10px;
 | 
			
		||||
    height: 44px;
 | 
			
		||||
  .kefu-footer {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    height: auto;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    border-top: var(--el-border-color) solid 1px;
 | 
			
		||||
 | 
			
		||||
    .chat-tools {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 44px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ::v-deep(textarea) {
 | 
			
		||||
    resize: none;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  :deep(.el-input__wrapper) {
 | 
			
		||||
    box-shadow: none !important;
 | 
			
		||||
    border-radius: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ::v-deep(.el-textarea__inner) {
 | 
			
		||||
    box-shadow: none !important;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import KeFuConversationList from './KeFuConversationList.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 }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,24 @@
 | 
			
		||||
<!-- 目录是不是叫 member 好点。然后这个组件是 MemberInfo,里面有浏览足迹 -->
 | 
			
		||||
<template>
 | 
			
		||||
  <div v-show="!isEmpty(conversation)" class="kefu">
 | 
			
		||||
    <div class="header-title h-60px flex justify-center items-center">他的足迹</div>
 | 
			
		||||
    <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
 | 
			
		||||
      <el-tab-pane label="最近浏览" name="a" />
 | 
			
		||||
      <el-tab-pane label="订单列表" name="b" />
 | 
			
		||||
    </el-tabs>
 | 
			
		||||
    <div>
 | 
			
		||||
      <el-scrollbar ref="scrollbarRef" always height="calc(115vh - 400px)" @scroll="handleScroll">
 | 
			
		||||
        <!-- 最近浏览 -->
 | 
			
		||||
        <ProductBrowsingHistory v-if="activeName === 'a'" ref="productBrowsingHistoryRef" />
 | 
			
		||||
        <!-- 订单列表 -->
 | 
			
		||||
        <OrderBrowsingHistory v-if="activeName === 'b'" ref="orderBrowsingHistoryRef" />
 | 
			
		||||
      </el-scrollbar>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <el-empty v-show="isEmpty(conversation)" description="请选择左侧的一个会话后开始" />
 | 
			
		||||
  <el-container class="kefu">
 | 
			
		||||
    <el-header class="kefu-header">
 | 
			
		||||
      <el-tabs v-model="activeName" class="kefu-tabs" @tab-click="handleClick">
 | 
			
		||||
        <el-tab-pane label="最近浏览" name="a" />
 | 
			
		||||
        <el-tab-pane label="订单列表" name="b" />
 | 
			
		||||
      </el-tabs>
 | 
			
		||||
    </el-header>
 | 
			
		||||
    <el-main class="kefu-content">
 | 
			
		||||
      <div v-show="!isEmpty(conversation)">
 | 
			
		||||
        <el-scrollbar ref="scrollbarRef" always @scroll="handleScroll">
 | 
			
		||||
          <!-- 最近浏览 -->
 | 
			
		||||
          <ProductBrowsingHistory v-if="activeName === 'a'" ref="productBrowsingHistoryRef" />
 | 
			
		||||
          <!-- 订单列表 -->
 | 
			
		||||
          <OrderBrowsingHistory v-if="activeName === 'b'" ref="orderBrowsingHistoryRef" />
 | 
			
		||||
        </el-scrollbar>
 | 
			
		||||
      </div>
 | 
			
		||||
      <el-empty v-show="isEmpty(conversation)" description="请选择左侧的一个会话后开始" />
 | 
			
		||||
    </el-main>
 | 
			
		||||
  </el-container>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
@@ -91,6 +94,41 @@ const handleScroll = debounce(() => {
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<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 {
 | 
			
		||||
  border-bottom: #e4e0e0 solid 1px;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,28 +1,16 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <el-row :gutter="10">
 | 
			
		||||
  <el-container class="kefu-layout">
 | 
			
		||||
    <!-- 会话列表 -->
 | 
			
		||||
    <el-col :span="6">
 | 
			
		||||
      <ContentWrap>
 | 
			
		||||
        <KeFuConversationList ref="keFuConversationRef" @change="handleChange" />
 | 
			
		||||
      </ContentWrap>
 | 
			
		||||
    </el-col>
 | 
			
		||||
    <KeFuConversationList ref="keFuConversationRef" @change="handleChange" />
 | 
			
		||||
    <!-- 会话详情(选中会话的消息列表) -->
 | 
			
		||||
    <el-col :span="12">
 | 
			
		||||
      <ContentWrap>
 | 
			
		||||
        <KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList" />
 | 
			
		||||
      </ContentWrap>
 | 
			
		||||
    </el-col>
 | 
			
		||||
    <KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList" />
 | 
			
		||||
    <!-- 会员足迹(选中会话的会员足迹) -->
 | 
			
		||||
    <el-col :span="6">
 | 
			
		||||
      <ContentWrap>
 | 
			
		||||
        <MemberBrowsingHistory ref="memberBrowsingHistoryRef" />
 | 
			
		||||
      </ContentWrap>
 | 
			
		||||
    </el-col>
 | 
			
		||||
  </el-row>
 | 
			
		||||
    <MemberInfo ref="memberInfoRef" />
 | 
			
		||||
  </el-container>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory } from './components'
 | 
			
		||||
import { KeFuConversationList, KeFuMessageList, MemberInfo } from './components'
 | 
			
		||||
import { WebSocketMessageTypeConstants } from './components/tools/constants'
 | 
			
		||||
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
 | 
			
		||||
import { getRefreshToken } from '@/utils/auth'
 | 
			
		||||
@@ -91,10 +79,10 @@ const getConversationList = () => {
 | 
			
		||||
 | 
			
		||||
/** 加载指定会话的消息列表 */
 | 
			
		||||
const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>()
 | 
			
		||||
const memberBrowsingHistoryRef = ref<InstanceType<typeof MemberBrowsingHistory>>()
 | 
			
		||||
const memberInfoRef = ref<InstanceType<typeof MemberInfo>>()
 | 
			
		||||
const handleChange = (conversation: KeFuConversationRespVO) => {
 | 
			
		||||
  keFuChatBoxRef.value?.getNewMessageList(conversation)
 | 
			
		||||
  memberBrowsingHistoryRef.value?.initHistory(conversation)
 | 
			
		||||
  memberInfoRef.value?.initHistory(conversation)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 初始化 */
 | 
			
		||||
@@ -112,9 +100,13 @@ onBeforeUnmount(() => {
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
.kefu {
 | 
			
		||||
  height: calc(100vh - 165px);
 | 
			
		||||
  overflow: auto; /* 确保内容可滚动 */
 | 
			
		||||
.kefu-layout {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 定义滚动条样式 */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user