commit
4abd246d96
|
@ -0,0 +1,9 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得商品浏览记录分页
|
||||||
|
* @param params 请求参数
|
||||||
|
*/
|
||||||
|
export const getBrowseHistoryPage = (params: any) => {
|
||||||
|
return request.get({ url: '/product/browse-history/page', params })
|
||||||
|
}
|
|
@ -21,9 +21,9 @@
|
||||||
</div>
|
</div>
|
||||||
<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>{{ item.userNickname }}</span>
|
<span class="username">{{ item.userNickname }}</span>
|
||||||
<span class="color-[#989EA6]">
|
<span class="color-[#989EA6]">
|
||||||
{{ formatDate(item.lastMessageTime) }}
|
{{ formatPast(item.lastMessageTime, 'YYYY-mm-dd') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 最后聊天内容 -->
|
<!-- 最后聊天内容 -->
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { KeFuConversationApi, KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
import { KeFuConversationApi, KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
||||||
import { useEmoji } from './tools/emoji'
|
import { useEmoji } from './tools/emoji'
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import { formatPast } from '@/utils/formatTime'
|
||||||
import { KeFuMessageContentTypeEnum } from './tools/constants'
|
import { KeFuMessageContentTypeEnum } from './tools/constants'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
|
|
||||||
|
@ -185,6 +185,16 @@ watch(showRightMenu, (val) => {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
transition: border-left 0.05s ease-in-out; /* 设置过渡效果 */
|
transition: border-left 0.05s ease-in-out; /* 设置过渡效果 */
|
||||||
|
|
||||||
|
.username {
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 60%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.last-message {
|
.last-message {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
overflow: hidden; // 隐藏超出的文本
|
overflow: hidden; // 隐藏超出的文本
|
||||||
|
|
|
@ -40,19 +40,54 @@
|
||||||
v-if="item.senderType === UserTypeEnum.MEMBER"
|
v-if="item.senderType === UserTypeEnum.MEMBER"
|
||||||
:src="conversation.userAvatar"
|
:src="conversation.userAvatar"
|
||||||
alt="avatar"
|
alt="avatar"
|
||||||
|
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"
|
||||||
>
|
>
|
||||||
<!-- 文本消息 -->
|
<!-- 文本消息 -->
|
||||||
<TextMessageItem :message="item" />
|
<MessageItem :message="item">
|
||||||
|
<template v-if="KeFuMessageContentTypeEnum.TEXT === item.contentType">
|
||||||
|
<div
|
||||||
|
v-dompurify-html="replaceEmoji(item.content)"
|
||||||
|
class="flex items-center"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
|
</MessageItem>
|
||||||
<!-- 图片消息 -->
|
<!-- 图片消息 -->
|
||||||
<ImageMessageItem :message="item" />
|
<MessageItem :message="item">
|
||||||
|
<el-image
|
||||||
|
v-if="KeFuMessageContentTypeEnum.IMAGE === item.contentType"
|
||||||
|
:initial-index="0"
|
||||||
|
:preview-src-list="[item.content]"
|
||||||
|
:src="item.content"
|
||||||
|
class="w-200px"
|
||||||
|
fit="contain"
|
||||||
|
preview-teleported
|
||||||
|
/>
|
||||||
|
</MessageItem>
|
||||||
<!-- 商品消息 -->
|
<!-- 商品消息 -->
|
||||||
<ProductMessageItem :message="item" />
|
<MessageItem :message="item">
|
||||||
|
<ProductItem
|
||||||
|
v-if="KeFuMessageContentTypeEnum.PRODUCT === item.contentType"
|
||||||
|
:picUrl="getMessageContent(item).picUrl"
|
||||||
|
:price="getMessageContent(item).price"
|
||||||
|
:skuText="getMessageContent(item).introduction"
|
||||||
|
:title="getMessageContent(item).spuName"
|
||||||
|
:titleWidth="400"
|
||||||
|
class="max-w-70%"
|
||||||
|
priceColor="#FF3000"
|
||||||
|
/>
|
||||||
|
</MessageItem>
|
||||||
<!-- 订单消息 -->
|
<!-- 订单消息 -->
|
||||||
<OrderMessageItem :message="item" />
|
<MessageItem :message="item">
|
||||||
|
<OrderItem
|
||||||
|
v-if="KeFuMessageContentTypeEnum.ORDER === item.contentType"
|
||||||
|
:message="item"
|
||||||
|
class="max-w-70%"
|
||||||
|
/>
|
||||||
|
</MessageItem>
|
||||||
</div>
|
</div>
|
||||||
<el-avatar
|
<el-avatar
|
||||||
v-if="item.senderType === UserTypeEnum.ADMIN"
|
v-if="item.senderType === UserTypeEnum.ADMIN"
|
||||||
|
@ -97,24 +132,24 @@ import { KeFuMessageApi, KeFuMessageRespVO } from '@/api/mall/promotion/kefu/mes
|
||||||
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
||||||
import EmojiSelectPopover from './tools/EmojiSelectPopover.vue'
|
import EmojiSelectPopover from './tools/EmojiSelectPopover.vue'
|
||||||
import PictureSelectUpload from './tools/PictureSelectUpload.vue'
|
import PictureSelectUpload from './tools/PictureSelectUpload.vue'
|
||||||
import TextMessageItem from './message/TextMessageItem.vue'
|
import ProductItem from './message/ProductItem.vue'
|
||||||
import ImageMessageItem from './message/ImageMessageItem.vue'
|
import OrderItem from './message/OrderItem.vue'
|
||||||
import ProductMessageItem from './message/ProductMessageItem.vue'
|
import { Emoji, useEmoji } from './tools/emoji'
|
||||||
import OrderMessageItem from './message/OrderMessageItem.vue'
|
|
||||||
import { Emoji } from './tools/emoji'
|
|
||||||
import { KeFuMessageContentTypeEnum } from './tools/constants'
|
import { KeFuMessageContentTypeEnum } from './tools/constants'
|
||||||
import { isEmpty } from '@/utils/is'
|
import { isEmpty } from '@/utils/is'
|
||||||
import { UserTypeEnum } from '@/utils/constants'
|
import { UserTypeEnum } from '@/utils/constants'
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import { formatDate } from '@/utils/formatTime'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
|
import { debounce } from 'lodash-es'
|
||||||
|
import { jsonParse } from '@/utils'
|
||||||
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
defineOptions({ name: 'KeFuMessageList' })
|
defineOptions({ name: 'KeFuMessageList' })
|
||||||
|
|
||||||
const message = ref('') // 消息弹窗
|
const message = ref('') // 消息弹窗
|
||||||
|
const { replaceEmoji } = useEmoji()
|
||||||
const messageTool = useMessage()
|
const messageTool = useMessage()
|
||||||
const messageList = ref<KeFuMessageRespVO[]>([]) // 消息列表
|
const messageList = ref<KeFuMessageRespVO[]>([]) // 消息列表
|
||||||
const conversation = ref<KeFuConversationRespVO>({} as KeFuConversationRespVO) // 用户会话
|
const conversation = ref<KeFuConversationRespVO>({} as KeFuConversationRespVO) // 用户会话
|
||||||
|
@ -126,18 +161,10 @@ const queryParams = reactive({
|
||||||
})
|
})
|
||||||
const total = ref(0) // 消息总条数
|
const total = ref(0) // 消息总条数
|
||||||
const refreshContent = ref(false) // 内容刷新,主要解决会话消息页面高度不一致导致的滚动功能精度失效
|
const refreshContent = ref(false) // 内容刷新,主要解决会话消息页面高度不一致导致的滚动功能精度失效
|
||||||
|
/** 获悉消息内容 */
|
||||||
|
const getMessageContent = computed(() => (item: any) => jsonParse(item.content))
|
||||||
/** 获得消息列表 */
|
/** 获得消息列表 */
|
||||||
const getMessageList = async (val: KeFuConversationRespVO, conversationChange: boolean) => {
|
const getMessageList = async () => {
|
||||||
// 会话切换,重置相关参数
|
|
||||||
if (conversationChange) {
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
messageList.value = []
|
|
||||||
total.value = 0
|
|
||||||
loadHistory.value = false
|
|
||||||
refreshContent.value = false
|
|
||||||
}
|
|
||||||
conversation.value = val
|
|
||||||
queryParams.conversationId = val.id
|
|
||||||
const res = await KeFuMessageApi.getKeFuMessagePage(queryParams)
|
const res = await KeFuMessageApi.getKeFuMessagePage(queryParams)
|
||||||
total.value = res.total
|
total.value = res.total
|
||||||
// 情况一:加载最新消息
|
// 情况一:加载最新消息
|
||||||
|
@ -146,14 +173,17 @@ const getMessageList = async (val: KeFuConversationRespVO, conversationChange: b
|
||||||
} else {
|
} else {
|
||||||
// 情况二:加载历史消息
|
// 情况二:加载历史消息
|
||||||
for (const item of res.list) {
|
for (const item of res.list) {
|
||||||
if (messageList.value.some((val) => val.id === item.id)) {
|
pushMessage(item)
|
||||||
continue
|
|
||||||
}
|
|
||||||
messageList.value.push(item)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refreshContent.value = true
|
refreshContent.value = true
|
||||||
await scrollToBottom()
|
}
|
||||||
|
/** 添加消息 */
|
||||||
|
const pushMessage = (message: any) => {
|
||||||
|
if (messageList.value.some((val) => val.id === message.id)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
messageList.value.push(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 按照时间倒序,获取消息列表 */
|
/** 按照时间倒序,获取消息列表 */
|
||||||
|
@ -163,20 +193,44 @@ const getMessageList0 = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 刷新消息列表 */
|
/** 刷新消息列表 */
|
||||||
const refreshMessageList = async () => {
|
const refreshMessageList = async (message?: any) => {
|
||||||
if (!conversation.value) {
|
if (!conversation.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof message !== 'undefined') {
|
||||||
|
// 当前查询会话与消息所属会话不一致则不做处理
|
||||||
|
if (message.conversationId !== conversation.value.id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pushMessage(message)
|
||||||
|
} else {
|
||||||
queryParams.pageNo = 1
|
queryParams.pageNo = 1
|
||||||
await getMessageList(conversation.value, false)
|
await getMessageList()
|
||||||
|
}
|
||||||
|
|
||||||
if (loadHistory.value) {
|
if (loadHistory.value) {
|
||||||
// 右下角显示有新消息提示
|
// 右下角显示有新消息提示
|
||||||
showNewMessageTip.value = true
|
showNewMessageTip.value = true
|
||||||
|
} else {
|
||||||
|
// 滚动到最新消息处
|
||||||
|
await handleToNewMessage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const getNewMessageList = async (val: KeFuConversationRespVO) => {
|
||||||
defineExpose({ getMessageList, refreshMessageList })
|
// 会话切换,重置相关参数
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
messageList.value = []
|
||||||
|
total.value = 0
|
||||||
|
loadHistory.value = false
|
||||||
|
refreshContent.value = false
|
||||||
|
// 设置会话相关属性
|
||||||
|
conversation.value = val
|
||||||
|
queryParams.conversationId = val.id
|
||||||
|
// 获取消息
|
||||||
|
await refreshMessageList()
|
||||||
|
}
|
||||||
|
defineExpose({ getNewMessageList, refreshMessageList })
|
||||||
const showKeFuMessageList = computed(() => !isEmpty(conversation.value)) // 是否显示聊天区域
|
const showKeFuMessageList = computed(() => !isEmpty(conversation.value)) // 是否显示聊天区域
|
||||||
const skipGetMessageList = computed(() => {
|
const skipGetMessageList = computed(() => {
|
||||||
// 已加载到最后一页的话则不触发新的消息获取
|
// 已加载到最后一页的话则不触发新的消息获取
|
||||||
|
@ -221,9 +275,7 @@ const sendMessage = async (msg: any) => {
|
||||||
await KeFuMessageApi.sendKeFuMessage(msg)
|
await KeFuMessageApi.sendKeFuMessage(msg)
|
||||||
message.value = ''
|
message.value = ''
|
||||||
// 加载消息列表
|
// 加载消息列表
|
||||||
await getMessageList(conversation.value, false)
|
await refreshMessageList()
|
||||||
// 滚动到最新消息处
|
|
||||||
await scrollToBottom()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 滚动到底部 */
|
/** 滚动到底部 */
|
||||||
|
@ -248,17 +300,24 @@ const handleToNewMessage = async () => {
|
||||||
await scrollToBottom()
|
await scrollToBottom()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 加载历史消息 */
|
|
||||||
const loadHistory = ref(false) // 加载历史消息
|
const loadHistory = ref(false) // 加载历史消息
|
||||||
const handleScroll = async ({ scrollTop }) => {
|
/** 处理消息列表滚动事件(debounce 限流) */
|
||||||
|
const handleScroll = debounce(({ scrollTop }) => {
|
||||||
if (skipGetMessageList.value) {
|
if (skipGetMessageList.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 触顶自动加载下一页数据
|
// 触顶自动加载下一页数据
|
||||||
if (scrollTop === 0) {
|
if (Math.floor(scrollTop) === 0) {
|
||||||
await handleOldMessage()
|
handleOldMessage()
|
||||||
}
|
}
|
||||||
}
|
const wrap = scrollbarRef.value?.wrapRef
|
||||||
|
// 触底重置
|
||||||
|
if (Math.abs(wrap!.scrollHeight - wrap!.clientHeight - wrap!.scrollTop) < 1) {
|
||||||
|
loadHistory.value = false
|
||||||
|
refreshMessageList()
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
/** 加载历史消息 */
|
||||||
const handleOldMessage = async () => {
|
const handleOldMessage = async () => {
|
||||||
// 记录已有页面高度
|
// 记录已有页面高度
|
||||||
const oldPageHeight = innerRef.value?.clientHeight
|
const oldPageHeight = innerRef.value?.clientHeight
|
||||||
|
@ -268,7 +327,7 @@ const handleOldMessage = async () => {
|
||||||
loadHistory.value = true
|
loadHistory.value = true
|
||||||
// 加载消息列表
|
// 加载消息列表
|
||||||
queryParams.pageNo += 1
|
queryParams.pageNo += 1
|
||||||
await getMessageList(conversation.value, false)
|
await getMessageList()
|
||||||
// 等页面加载完后,获得上一页最后一条消息的位置,控制滚动到它所在位置
|
// 等页面加载完后,获得上一页最后一条消息的位置,控制滚动到它所在位置
|
||||||
scrollbarRef.value!.setScrollTop(innerRef.value!.clientHeight - oldPageHeight)
|
scrollbarRef.value!.setScrollTop(innerRef.value!.clientHeight - oldPageHeight)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
<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(100vh - 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="请选择左侧的一个会话后开始" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { TabsPaneContext } from 'element-plus'
|
||||||
|
import ProductBrowsingHistory from './ProductBrowsingHistory.vue'
|
||||||
|
import OrderBrowsingHistory from './OrderBrowsingHistory.vue'
|
||||||
|
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
||||||
|
import { isEmpty } from '@/utils/is'
|
||||||
|
import { debounce } from 'lodash-es'
|
||||||
|
import { ElScrollbar as ElScrollbarType } from 'element-plus/es/components/scrollbar'
|
||||||
|
|
||||||
|
defineOptions({ name: 'MemberBrowsingHistory' })
|
||||||
|
|
||||||
|
const activeName = ref('a')
|
||||||
|
/** tab 切换 */
|
||||||
|
const productBrowsingHistoryRef = ref<InstanceType<typeof ProductBrowsingHistory>>()
|
||||||
|
const orderBrowsingHistoryRef = ref<InstanceType<typeof OrderBrowsingHistory>>()
|
||||||
|
const handleClick = async (tab: TabsPaneContext) => {
|
||||||
|
activeName.value = tab.paneName as string
|
||||||
|
await nextTick()
|
||||||
|
await getHistoryList()
|
||||||
|
}
|
||||||
|
/** 获得历史数据 */
|
||||||
|
const getHistoryList = async () => {
|
||||||
|
switch (activeName.value) {
|
||||||
|
case 'a':
|
||||||
|
await productBrowsingHistoryRef.value?.getHistoryList(conversation.value)
|
||||||
|
break
|
||||||
|
case 'b':
|
||||||
|
await orderBrowsingHistoryRef.value?.getHistoryList(conversation.value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** 加载下一页数据 */
|
||||||
|
const loadMore = async () => {
|
||||||
|
switch (activeName.value) {
|
||||||
|
case 'a':
|
||||||
|
await productBrowsingHistoryRef.value?.loadMore()
|
||||||
|
break
|
||||||
|
case 'b':
|
||||||
|
await orderBrowsingHistoryRef.value?.loadMore()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** 浏览历史初始化 */
|
||||||
|
const conversation = ref<KeFuConversationRespVO>({} as KeFuConversationRespVO) // 用户会话
|
||||||
|
const initHistory = async (val: KeFuConversationRespVO) => {
|
||||||
|
activeName.value = 'a'
|
||||||
|
conversation.value = val
|
||||||
|
await nextTick()
|
||||||
|
await getHistoryList()
|
||||||
|
}
|
||||||
|
defineExpose({ initHistory })
|
||||||
|
|
||||||
|
/** 处理消息列表滚动事件(debounce 限流) */
|
||||||
|
const scrollbarRef = ref<InstanceType<typeof ElScrollbarType>>()
|
||||||
|
const handleScroll = debounce(() => {
|
||||||
|
const wrap = scrollbarRef.value?.wrapRef
|
||||||
|
// 触底重置
|
||||||
|
if (Math.abs(wrap!.scrollHeight - wrap!.clientHeight - wrap!.scrollTop) < 1) {
|
||||||
|
loadMore()
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.header-title {
|
||||||
|
border-bottom: #e4e0e0 solid 1px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,42 @@
|
||||||
|
<template>
|
||||||
|
<OrderItem v-for="item in list" :key="item.id" :order="item" class="mb-10px" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import OrderItem from '@/views/mall/promotion/kefu/components/message/OrderItem.vue'
|
||||||
|
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
||||||
|
import { getOrderPage } from '@/api/mall/trade/order'
|
||||||
|
import { concat } from 'lodash-es'
|
||||||
|
|
||||||
|
defineOptions({ name: 'OrderBrowsingHistory' })
|
||||||
|
|
||||||
|
const list = ref<any>([]) // 列表
|
||||||
|
const total = ref(0) // 总数
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
userId: 0
|
||||||
|
})
|
||||||
|
const skipGetMessageList = computed(() => {
|
||||||
|
// 已加载到最后一页的话则不触发新的消息获取
|
||||||
|
return total.value > 0 && Math.ceil(total.value / queryParams.pageSize) === queryParams.pageNo
|
||||||
|
}) // 跳过消息获取
|
||||||
|
/** 获得浏览记录 */
|
||||||
|
const getHistoryList = async (val: KeFuConversationRespVO) => {
|
||||||
|
queryParams.userId = val.userId
|
||||||
|
const res = await getOrderPage(queryParams)
|
||||||
|
total.value = res.total
|
||||||
|
list.value = res.list
|
||||||
|
}
|
||||||
|
/** 加载下一页数据 */
|
||||||
|
const loadMore = async () => {
|
||||||
|
if (skipGetMessageList.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
queryParams.pageNo += 1
|
||||||
|
const res = await getOrderPage(queryParams)
|
||||||
|
total.value = res.total
|
||||||
|
concat(list.value, res.list)
|
||||||
|
}
|
||||||
|
defineExpose({ getHistoryList, loadMore })
|
||||||
|
</script>
|
|
@ -0,0 +1,55 @@
|
||||||
|
<template>
|
||||||
|
<ProductItem
|
||||||
|
v-for="item in list"
|
||||||
|
:key="item.id"
|
||||||
|
:picUrl="item.picUrl"
|
||||||
|
:price="item.price"
|
||||||
|
:skuText="item.introduction"
|
||||||
|
:title="item.spuName"
|
||||||
|
:titleWidth="400"
|
||||||
|
class="mb-10px"
|
||||||
|
priceColor="#FF3000"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getBrowseHistoryPage } from '@/api/mall/product/history'
|
||||||
|
import ProductItem from '@/views/mall/promotion/kefu/components/message/ProductItem.vue'
|
||||||
|
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
||||||
|
import { concat } from 'lodash-es'
|
||||||
|
|
||||||
|
defineOptions({ name: 'ProductBrowsingHistory' })
|
||||||
|
|
||||||
|
const list = ref<any>([]) // 列表
|
||||||
|
const total = ref(0) // 总数
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
userId: 0,
|
||||||
|
userDeleted: false
|
||||||
|
})
|
||||||
|
const skipGetMessageList = computed(() => {
|
||||||
|
// 已加载到最后一页的话则不触发新的消息获取
|
||||||
|
return total.value > 0 && Math.ceil(total.value / queryParams.pageSize) === queryParams.pageNo
|
||||||
|
}) // 跳过消息获取
|
||||||
|
/** 获得浏览记录 */
|
||||||
|
const getHistoryList = async (val: KeFuConversationRespVO) => {
|
||||||
|
queryParams.userId = val.userId
|
||||||
|
const res = await getBrowseHistoryPage(queryParams)
|
||||||
|
total.value = res.total
|
||||||
|
list.value = res.list
|
||||||
|
}
|
||||||
|
/** 加载下一页数据 */
|
||||||
|
const loadMore = async () => {
|
||||||
|
if (skipGetMessageList.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
queryParams.pageNo += 1
|
||||||
|
const res = await getBrowseHistoryPage(queryParams)
|
||||||
|
total.value = res.total
|
||||||
|
concat(list.value, res.list)
|
||||||
|
}
|
||||||
|
defineExpose({ getHistoryList, loadMore })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
|
@ -1,4 +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'
|
||||||
|
|
||||||
export { KeFuConversationList, KeFuMessageList }
|
export { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory }
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- 图片消息 -->
|
|
||||||
<template v-if="KeFuMessageContentTypeEnum.IMAGE === message.contentType">
|
|
||||||
<div
|
|
||||||
:class="[
|
|
||||||
message.senderType === UserTypeEnum.MEMBER
|
|
||||||
? `ml-10px`
|
|
||||||
: message.senderType === UserTypeEnum.ADMIN
|
|
||||||
? `mr-10px`
|
|
||||||
: ''
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<el-image
|
|
||||||
:initial-index="0"
|
|
||||||
:preview-src-list="[message.content]"
|
|
||||||
:src="message.content"
|
|
||||||
class="w-200px"
|
|
||||||
fit="contain"
|
|
||||||
preview-teleported
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { KeFuMessageContentTypeEnum } from '../tools/constants'
|
|
||||||
import { UserTypeEnum } from '@/utils/constants'
|
|
||||||
import { KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
|
|
||||||
|
|
||||||
defineOptions({ name: 'ImageMessageItem' })
|
|
||||||
defineProps<{
|
|
||||||
message: KeFuMessageRespVO
|
|
||||||
}>()
|
|
||||||
</script>
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<template>
|
||||||
|
<!-- 消息组件 -->
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
message.senderType === UserTypeEnum.MEMBER
|
||||||
|
? `ml-10px`
|
||||||
|
: message.senderType === UserTypeEnum.ADMIN
|
||||||
|
? `mr-10px`
|
||||||
|
: ''
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { UserTypeEnum } from '@/utils/constants'
|
||||||
|
import { KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
|
||||||
|
|
||||||
|
defineOptions({ name: 'MessageItem' })
|
||||||
|
defineProps<{
|
||||||
|
message: KeFuMessageRespVO
|
||||||
|
}>()
|
||||||
|
</script>
|
|
@ -0,0 +1,146 @@
|
||||||
|
<template>
|
||||||
|
<div v-if="isObject(getMessageContent)">
|
||||||
|
<div :key="getMessageContent.id" class="order-list-card-box mt-14px">
|
||||||
|
<div class="order-card-header flex items-center justify-between p-x-20px">
|
||||||
|
<div class="order-no">订单号:{{ getMessageContent.no }}</div>
|
||||||
|
<div :class="formatOrderColor(getMessageContent)" class="order-state font-16">
|
||||||
|
{{ formatOrderStatus(getMessageContent) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-for="item in getMessageContent.items" :key="item.id" class="border-bottom">
|
||||||
|
<ProductItem
|
||||||
|
:num="item.count"
|
||||||
|
:picUrl="item.picUrl"
|
||||||
|
:price="item.price"
|
||||||
|
:skuText="item.properties.map((property: any) => property.valueName).join(' ')"
|
||||||
|
:title="item.spuName"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="pay-box flex justify-end pr-20px">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="discounts-title pay-color"
|
||||||
|
>共 {{ getMessageContent?.productCount }} 件商品,总金额:
|
||||||
|
</div>
|
||||||
|
<div class="discounts-money pay-color">
|
||||||
|
¥{{ fenToYuan(getMessageContent?.payPrice) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { fenToYuan, jsonParse } from '@/utils'
|
||||||
|
import { KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
|
||||||
|
import { isObject } from '@/utils/is'
|
||||||
|
import ProductItem from '@/views/mall/promotion/kefu/components/message/ProductItem.vue'
|
||||||
|
|
||||||
|
defineOptions({ name: 'OrderItem' })
|
||||||
|
const props = defineProps<{
|
||||||
|
message?: KeFuMessageRespVO
|
||||||
|
order?: any
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const getMessageContent = computed(() =>
|
||||||
|
typeof props.message !== 'undefined' ? jsonParse(props!.message!.content) : props.order
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化订单状态的颜色
|
||||||
|
*
|
||||||
|
* @param order 订单
|
||||||
|
* @return {string} 颜色的 class 名称
|
||||||
|
*/
|
||||||
|
function formatOrderColor(order: any) {
|
||||||
|
if (order.status === 0) {
|
||||||
|
return 'info-color'
|
||||||
|
}
|
||||||
|
if (order.status === 10 || order.status === 20 || (order.status === 30 && !order.commentStatus)) {
|
||||||
|
return 'warning-color'
|
||||||
|
}
|
||||||
|
if (order.status === 30 && order.commentStatus) {
|
||||||
|
return 'success-color'
|
||||||
|
}
|
||||||
|
return 'danger-color'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化订单状态
|
||||||
|
*
|
||||||
|
* @param order 订单
|
||||||
|
*/
|
||||||
|
function formatOrderStatus(order: any) {
|
||||||
|
if (order.status === 0) {
|
||||||
|
return '待付款'
|
||||||
|
}
|
||||||
|
if (order.status === 10 && order.deliveryType === 1) {
|
||||||
|
return '待发货'
|
||||||
|
}
|
||||||
|
if (order.status === 10 && order.deliveryType === 2) {
|
||||||
|
return '待核销'
|
||||||
|
}
|
||||||
|
if (order.status === 20) {
|
||||||
|
return '待收货'
|
||||||
|
}
|
||||||
|
if (order.status === 30 && !order.commentStatus) {
|
||||||
|
return '待评价'
|
||||||
|
}
|
||||||
|
if (order.status === 30 && order.commentStatus) {
|
||||||
|
return '已完成'
|
||||||
|
}
|
||||||
|
return '已关闭'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.order-list-card-box {
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #e2e2e2;
|
||||||
|
|
||||||
|
.order-card-header {
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
|
.order-no {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-box {
|
||||||
|
.discounts-title {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: normal;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discounts-money {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: normal;
|
||||||
|
color: #999;
|
||||||
|
font-family: OPPOSANS;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-color {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-color {
|
||||||
|
color: #faad14;
|
||||||
|
}
|
||||||
|
|
||||||
|
.danger-color {
|
||||||
|
color: #ff3000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-color {
|
||||||
|
color: #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-color {
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,182 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- 图片消息 -->
|
|
||||||
<template v-if="KeFuMessageContentTypeEnum.ORDER === message.contentType">
|
|
||||||
<div
|
|
||||||
:class="[
|
|
||||||
message.senderType === UserTypeEnum.MEMBER
|
|
||||||
? `ml-10px`
|
|
||||||
: message.senderType === UserTypeEnum.ADMIN
|
|
||||||
? `mr-10px`
|
|
||||||
: ''
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<div :key="getMessageContent.id" class="order-list-card-box mt-14px">
|
|
||||||
<div class="order-card-header flex items-center justify-between p-x-20px">
|
|
||||||
<div class="order-no">订单号:{{ getMessageContent.no }}</div>
|
|
||||||
<div :class="formatOrderColor(getMessageContent)" class="order-state font-26">
|
|
||||||
{{ formatOrderStatus(getMessageContent) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-for="item in getMessageContent.items" :key="item.id" class="border-bottom">
|
|
||||||
<ProductItem
|
|
||||||
:num="item.count"
|
|
||||||
:picUrl="item.picUrl"
|
|
||||||
:price="item.price"
|
|
||||||
:skuText="item.properties.map((property: any) => property.valueName).join(' ')"
|
|
||||||
:title="item.spuName"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="pay-box mt-30px flex justify-end pr-20px">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<div class="discounts-title pay-color"
|
|
||||||
>共 {{ getMessageContent?.productCount }} 件商品,总金额:
|
|
||||||
</div>
|
|
||||||
<div class="discounts-money pay-color">
|
|
||||||
¥{{ fenToYuan(getMessageContent?.payPrice) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { KeFuMessageContentTypeEnum } from '../tools/constants'
|
|
||||||
import ProductItem from './ProductItem.vue'
|
|
||||||
import { UserTypeEnum } from '@/utils/constants'
|
|
||||||
import { KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
|
|
||||||
import { fenToYuan } from '@/utils'
|
|
||||||
|
|
||||||
defineOptions({ name: 'OrderMessageItem' })
|
|
||||||
const props = defineProps<{
|
|
||||||
message: KeFuMessageRespVO
|
|
||||||
}>()
|
|
||||||
const getMessageContent = computed(() => JSON.parse(props.message.content))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 格式化订单状态的颜色
|
|
||||||
*
|
|
||||||
* @param order 订单
|
|
||||||
* @return {string} 颜色的 class 名称
|
|
||||||
*/
|
|
||||||
function formatOrderColor(order: any) {
|
|
||||||
if (order.status === 0) {
|
|
||||||
return 'info-color'
|
|
||||||
}
|
|
||||||
if (order.status === 10 || order.status === 20 || (order.status === 30 && !order.commentStatus)) {
|
|
||||||
return 'warning-color'
|
|
||||||
}
|
|
||||||
if (order.status === 30 && order.commentStatus) {
|
|
||||||
return 'success-color'
|
|
||||||
}
|
|
||||||
return 'danger-color'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 格式化订单状态
|
|
||||||
*
|
|
||||||
* @param order 订单
|
|
||||||
*/
|
|
||||||
function formatOrderStatus(order: any) {
|
|
||||||
if (order.status === 0) {
|
|
||||||
return '待付款'
|
|
||||||
}
|
|
||||||
if (order.status === 10 && order.deliveryType === 1) {
|
|
||||||
return '待发货'
|
|
||||||
}
|
|
||||||
if (order.status === 10 && order.deliveryType === 2) {
|
|
||||||
return '待核销'
|
|
||||||
}
|
|
||||||
if (order.status === 20) {
|
|
||||||
return '待收货'
|
|
||||||
}
|
|
||||||
if (order.status === 30 && !order.commentStatus) {
|
|
||||||
return '待评价'
|
|
||||||
}
|
|
||||||
if (order.status === 30 && order.commentStatus) {
|
|
||||||
return '已完成'
|
|
||||||
}
|
|
||||||
return '已关闭'
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.order-list-card-box {
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: #e2e2e2;
|
|
||||||
|
|
||||||
.order-card-header {
|
|
||||||
height: 80px;
|
|
||||||
|
|
||||||
.order-no {
|
|
||||||
font-size: 26px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.pay-box {
|
|
||||||
.discounts-title {
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: normal;
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.discounts-money {
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: normal;
|
|
||||||
color: #999;
|
|
||||||
font-family: OPPOSANS;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pay-color {
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.order-card-footer {
|
|
||||||
height: 100px;
|
|
||||||
|
|
||||||
.more-item-box {
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.more-item {
|
|
||||||
height: 60px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 26px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.more-btn {
|
|
||||||
color: #999999;
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
width: 154px;
|
|
||||||
color: #333333;
|
|
||||||
font-size: 26px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-color {
|
|
||||||
color: #faad14;
|
|
||||||
}
|
|
||||||
|
|
||||||
.danger-color {
|
|
||||||
color: #ff3000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success-color {
|
|
||||||
color: #52c41a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-color {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -110,33 +110,25 @@ const skuString = computed(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.score-img {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
margin: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ss-order-card-warp {
|
.ss-order-card-warp {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: #e2e2e2;
|
background-color: #e2e2e2;
|
||||||
|
|
||||||
.img-box {
|
.img-box {
|
||||||
width: 164px;
|
width: 80px;
|
||||||
height: 164px;
|
height: 80px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.order-img {
|
.order-img {
|
||||||
width: 164px;
|
width: 80px;
|
||||||
height: 164px;
|
height: 80px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.box-right {
|
.box-right {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
// width: 500px;
|
|
||||||
// height: 164px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.tool-box {
|
.tool-box {
|
||||||
|
@ -147,13 +139,13 @@ const skuString = computed(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-text {
|
.title-text {
|
||||||
font-size: 28px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 40px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spec-text {
|
.spec-text {
|
||||||
font-size: 24px;
|
font-size: 16px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: #999999;
|
color: #999999;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
@ -165,15 +157,15 @@ const skuString = computed(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-text {
|
.price-text {
|
||||||
font-size: 24px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-family: OPPOSANS;
|
font-family: OPPOSANS;
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-text {
|
.total-text {
|
||||||
font-size: 24px;
|
font-size: 16px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 24px;
|
line-height: 16px;
|
||||||
color: #999999;
|
color: #999999;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- 图片消息 -->
|
|
||||||
<template v-if="KeFuMessageContentTypeEnum.PRODUCT === message.contentType">
|
|
||||||
<div
|
|
||||||
:class="[
|
|
||||||
message.senderType === UserTypeEnum.MEMBER
|
|
||||||
? `ml-10px`
|
|
||||||
: message.senderType === UserTypeEnum.ADMIN
|
|
||||||
? `mr-10px`
|
|
||||||
: ''
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<ProductItem
|
|
||||||
:picUrl="getMessageContent.picUrl"
|
|
||||||
:price="getMessageContent.price"
|
|
||||||
:skuText="getMessageContent.introduction"
|
|
||||||
:title="getMessageContent.spuName"
|
|
||||||
:titleWidth="400"
|
|
||||||
priceColor="#FF3000"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { KeFuMessageContentTypeEnum } from '../tools/constants'
|
|
||||||
import ProductItem from './ProductItem.vue'
|
|
||||||
import { UserTypeEnum } from '@/utils/constants'
|
|
||||||
import { KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
|
|
||||||
|
|
||||||
defineOptions({ name: 'ProductMessageItem' })
|
|
||||||
const props = defineProps<{
|
|
||||||
message: KeFuMessageRespVO
|
|
||||||
}>()
|
|
||||||
|
|
||||||
/** 获悉消息内容 */
|
|
||||||
const getMessageContent = computed(() => JSON.parse(props.message.content))
|
|
||||||
</script>
|
|
|
@ -1,29 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- 文本消息 -->
|
|
||||||
<template v-if="KeFuMessageContentTypeEnum.TEXT === message.contentType">
|
|
||||||
<div
|
|
||||||
v-dompurify-html="replaceEmoji(message.content)"
|
|
||||||
:class="[
|
|
||||||
message.senderType === UserTypeEnum.MEMBER
|
|
||||||
? `ml-10px`
|
|
||||||
: message.senderType === UserTypeEnum.ADMIN
|
|
||||||
? `mr-10px`
|
|
||||||
: ''
|
|
||||||
]"
|
|
||||||
class="flex items-center"
|
|
||||||
></div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { KeFuMessageContentTypeEnum } from '../tools/constants'
|
|
||||||
import { UserTypeEnum } from '@/utils/constants'
|
|
||||||
import { useEmoji } from '../tools/emoji'
|
|
||||||
import { KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
|
|
||||||
|
|
||||||
defineOptions({ name: 'TextMessageItem' })
|
|
||||||
defineProps<{
|
|
||||||
message: KeFuMessageRespVO
|
|
||||||
}>()
|
|
||||||
const { replaceEmoji } = useEmoji()
|
|
||||||
</script>
|
|
|
@ -1,22 +1,28 @@
|
||||||
<template>
|
<template>
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<!-- 会话列表 -->
|
<!-- 会话列表 -->
|
||||||
<el-col :span="8">
|
<el-col :span="6">
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<KeFuConversationList ref="keFuConversationRef" @change="handleChange" />
|
<KeFuConversationList ref="keFuConversationRef" @change="handleChange" />
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
</el-col>
|
</el-col>
|
||||||
<!-- 会话详情(选中会话的消息列表) -->
|
<!-- 会话详情(选中会话的消息列表) -->
|
||||||
<el-col :span="16">
|
<el-col :span="12">
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList" />
|
<KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList" />
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<!-- 会员足迹(选中会话的会员足迹) -->
|
||||||
|
<el-col :span="6">
|
||||||
|
<ContentWrap>
|
||||||
|
<MemberBrowsingHistory ref="memberBrowsingHistoryRef" />
|
||||||
|
</ContentWrap>
|
||||||
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { KeFuConversationList, KeFuMessageList } from './components'
|
import { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory } 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 { getAccessToken } from '@/utils/auth'
|
import { getAccessToken } from '@/utils/auth'
|
||||||
|
@ -60,7 +66,7 @@ watchEffect(() => {
|
||||||
// 刷新会话列表
|
// 刷新会话列表
|
||||||
getConversationList()
|
getConversationList()
|
||||||
// 刷新消息列表
|
// 刷新消息列表
|
||||||
keFuChatBoxRef.value?.refreshMessageList()
|
keFuChatBoxRef.value?.refreshMessageList(JSON.parse(jsonMessage.content))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 2.3 消息类型:KEFU_MESSAGE_ADMIN_READ
|
// 2.3 消息类型:KEFU_MESSAGE_ADMIN_READ
|
||||||
|
@ -81,8 +87,10 @@ const getConversationList = () => {
|
||||||
|
|
||||||
/** 加载指定会话的消息列表 */
|
/** 加载指定会话的消息列表 */
|
||||||
const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>()
|
const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>()
|
||||||
|
const memberBrowsingHistoryRef = ref<InstanceType<typeof MemberBrowsingHistory>>()
|
||||||
const handleChange = (conversation: KeFuConversationRespVO) => {
|
const handleChange = (conversation: KeFuConversationRespVO) => {
|
||||||
keFuChatBoxRef.value?.getMessageList(conversation, true)
|
keFuChatBoxRef.value?.getNewMessageList(conversation)
|
||||||
|
memberBrowsingHistoryRef.value?.initHistory(conversation)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 初始化 */
|
/** 初始化 */
|
||||||
|
|
Loading…
Reference in New Issue