优化客服功能 #106
@ -20,6 +20,7 @@ export interface UserVO {
|
|||||||
point: number | undefined | null
|
point: number | undefined | null
|
||||||
totalPoint: number | undefined | null
|
totalPoint: number | undefined | null
|
||||||
experience: number | null | undefined
|
experience: number | null | undefined
|
||||||
|
groupName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询会员用户列表
|
// 查询会员用户列表
|
||||||
@ -51,3 +52,8 @@ export const updateUserPoint = async (data: any) => {
|
|||||||
export const updateUserBalance = async (data: any) => {
|
export const updateUserBalance = async (data: any) => {
|
||||||
return await request.put({ url: `/member/user/update-balance`, data })
|
return await request.put({ url: `/member/user/update-balance`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 客服查询用户详情
|
||||||
|
export const getUserInfo = async (id: number) => {
|
||||||
|
return await request.get({ url: `/member/user/getUserInfo?id=` + id })
|
||||||
|
}
|
||||||
|
@ -35,88 +35,120 @@ const mobile = computed(() => appStore.getMobile)
|
|||||||
// 固定菜单
|
// 固定菜单
|
||||||
const fixedMenu = computed(() => appStore.getFixedMenu)
|
const fixedMenu = computed(() => appStore.getFixedMenu)
|
||||||
|
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
export const useRenderLayout = () => {
|
export const useRenderLayout = () => {
|
||||||
const renderClassic = () => {
|
const route = useRoute()
|
||||||
return (
|
let renderClassic = null;
|
||||||
<>
|
if(route.path == "/kefu/kefu"){
|
||||||
<div
|
renderClassic = () => {
|
||||||
class={[
|
return (
|
||||||
'absolute top-0 left-0 h-full layout-border__right',
|
<>
|
||||||
{ '!fixed z-3000': mobile.value }
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{logo.value ? (
|
|
||||||
<Logo
|
|
||||||
class={[
|
|
||||||
'bg-[var(--left-menu-bg-color)] relative',
|
|
||||||
{
|
|
||||||
'!pl-0': mobile.value && collapse.value,
|
|
||||||
'w-[var(--left-menu-min-width)]': appStore.getCollapse,
|
|
||||||
'w-[var(--left-menu-max-width)]': !appStore.getCollapse
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
style="transition: all var(--transition-time-02);"
|
|
||||||
></Logo>
|
|
||||||
) : undefined}
|
|
||||||
<Menu class={[{ '!h-[calc(100%-var(--logo-height))]': logo.value }]}></Menu>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class={[
|
|
||||||
`${prefixCls}-content`,
|
|
||||||
'absolute top-0 h-[100%]',
|
|
||||||
{
|
|
||||||
'w-[calc(100%-var(--left-menu-min-width))] left-[var(--left-menu-min-width)]':
|
|
||||||
collapse.value && !mobile.value && !mobile.value,
|
|
||||||
'w-[calc(100%-var(--left-menu-max-width))] left-[var(--left-menu-max-width)]':
|
|
||||||
!collapse.value && !mobile.value && !mobile.value,
|
|
||||||
'fixed !w-full !left-0': mobile.value
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
style="transition: all var(--transition-time-02);"
|
|
||||||
>
|
|
||||||
<ElScrollbar
|
|
||||||
v-loading={pageLoading.value}
|
|
||||||
class={[
|
|
||||||
`${prefixCls}-content-scrollbar`,
|
|
||||||
{
|
|
||||||
'!h-[calc(100%-var(--top-tool-height)-var(--tags-view-height))] mt-[calc(var(--top-tool-height)+var(--tags-view-height))]':
|
|
||||||
fixedHeader.value
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class={[
|
|
||||||
{
|
|
||||||
'fixed top-0 left-0 z-10': fixedHeader.value,
|
|
||||||
'w-[calc(100%-var(--left-menu-min-width))] !left-[var(--left-menu-min-width)]':
|
|
||||||
collapse.value && fixedHeader.value && !mobile.value,
|
|
||||||
'w-[calc(100%-var(--left-menu-max-width))] !left-[var(--left-menu-max-width)]':
|
|
||||||
!collapse.value && fixedHeader.value && !mobile.value,
|
|
||||||
'!w-full !left-0': mobile.value
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
style="transition: all var(--transition-time-02);"
|
|
||||||
>
|
|
||||||
<ToolHeader
|
|
||||||
class={[
|
|
||||||
'bg-[var(--top-header-bg-color)]',
|
|
||||||
{
|
|
||||||
'layout-border__bottom': !tagsView.value
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
></ToolHeader>
|
|
||||||
|
|
||||||
{tagsView.value ? (
|
<div
|
||||||
<TagsView class="layout-border__top layout-border__bottom"></TagsView>
|
|
||||||
) : undefined}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AppView></AppView>
|
style="transition: all var(--transition-time-02);width:85%;margin:0 auto;"
|
||||||
</ElScrollbar>
|
>
|
||||||
</div>
|
<ElScrollbar
|
||||||
</>
|
v-loading={pageLoading.value}
|
||||||
)
|
class={[
|
||||||
}
|
`${prefixCls}-content-scrollbar`,
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<AppView></AppView>
|
||||||
|
</ElScrollbar>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
renderClassic = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
class={[
|
||||||
|
'absolute top-0 left-0 h-full layout-border__right',
|
||||||
|
{ '!fixed z-3000': mobile.value }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{logo.value ? (
|
||||||
|
<Logo
|
||||||
|
class={[
|
||||||
|
'bg-[var(--left-menu-bg-color)] relative',
|
||||||
|
{
|
||||||
|
'!pl-0': mobile.value && collapse.value,
|
||||||
|
'w-[var(--left-menu-min-width)]': appStore.getCollapse,
|
||||||
|
'w-[var(--left-menu-max-width)]': !appStore.getCollapse
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
style="transition: all var(--transition-time-02);"
|
||||||
|
></Logo>
|
||||||
|
) : undefined}
|
||||||
|
<Menu class={[{ '!h-[calc(100%-var(--logo-height))]': logo.value }]}></Menu>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class={[
|
||||||
|
`${prefixCls}-content`,
|
||||||
|
'absolute top-0 h-[100%]',
|
||||||
|
{
|
||||||
|
'w-[calc(100%-var(--left-menu-min-width))] left-[var(--left-menu-min-width)]':
|
||||||
|
collapse.value && !mobile.value && !mobile.value,
|
||||||
|
'w-[calc(100%-var(--left-menu-max-width))] left-[var(--left-menu-max-width)]':
|
||||||
|
!collapse.value && !mobile.value && !mobile.value,
|
||||||
|
'fixed !w-full !left-0': mobile.value
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
style="transition: all var(--transition-time-02);"
|
||||||
|
>
|
||||||
|
<ElScrollbar
|
||||||
|
v-loading={pageLoading.value}
|
||||||
|
class={[
|
||||||
|
`${prefixCls}-content-scrollbar`,
|
||||||
|
{
|
||||||
|
'!h-[calc(100%-var(--top-tool-height)-var(--tags-view-height))] mt-[calc(var(--top-tool-height)+var(--tags-view-height))]':
|
||||||
|
fixedHeader.value
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class={[
|
||||||
|
{
|
||||||
|
'fixed top-0 left-0 z-10': fixedHeader.value,
|
||||||
|
'w-[calc(100%-var(--left-menu-min-width))] !left-[var(--left-menu-min-width)]':
|
||||||
|
collapse.value && fixedHeader.value && !mobile.value,
|
||||||
|
'w-[calc(100%-var(--left-menu-max-width))] !left-[var(--left-menu-max-width)]':
|
||||||
|
!collapse.value && fixedHeader.value && !mobile.value,
|
||||||
|
'!w-full !left-0': mobile.value
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
style="transition: all var(--transition-time-02);"
|
||||||
|
>
|
||||||
|
<ToolHeader
|
||||||
|
class={[
|
||||||
|
'bg-[var(--top-header-bg-color)]',
|
||||||
|
{
|
||||||
|
'layout-border__bottom': !tagsView.value
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
></ToolHeader>
|
||||||
|
|
||||||
|
{tagsView.value ? (
|
||||||
|
<TagsView class="layout-border__top layout-border__bottom"></TagsView>
|
||||||
|
) : undefined}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AppView></AppView>
|
||||||
|
</ElScrollbar>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const renderTopLeft = () => {
|
const renderTopLeft = () => {
|
||||||
return (
|
return (
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-container v-if="showKeFuMessageList" class="kefu">
|
<el-container v-if="showKeFuMessageList" class="kefu">
|
||||||
<el-header>
|
<!-- <el-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 height="calc(100vh - 420px)" @scroll="handleScroll">
|
||||||
<div v-if="refreshContent" ref="innerRef" class="w-[100%] pb-3px">
|
<div v-if="refreshContent" ref="innerRef" class="w-[100%] pb-3px">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 消息列表 -->
|
<!-- 消息列表 -->
|
||||||
<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">
|
||||||
@ -20,6 +23,8 @@
|
|||||||
{{ item.content }}
|
{{ item.content }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div :class="[
|
<div :class="[
|
||||||
item.senderType === UserTypeEnum.MEMBER
|
item.senderType === UserTypeEnum.MEMBER
|
||||||
? `ss-row-left`
|
? `ss-row-left`
|
||||||
@ -63,6 +68,10 @@
|
|||||||
alt="avatar" />
|
alt="avatar" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
<div v-show="showNewMessageTip" class="newMessageTip flex items-center cursor-pointer"
|
<div v-show="showNewMessageTip" class="newMessageTip flex items-center cursor-pointer"
|
||||||
@ -78,15 +87,15 @@
|
|||||||
<PictureSelectUpload class="ml-15px mt-3px cursor-pointer" @send-picture="handleSendPicture" />
|
<PictureSelectUpload class="ml-15px mt-3px cursor-pointer" @send-picture="handleSendPicture" />
|
||||||
<!-- <VerbalTrick class="ml-11px mt-5px cursor-pointer" /> -->
|
<!-- <VerbalTrick class="ml-11px mt-5px cursor-pointer" /> -->
|
||||||
<!-- 话术库 -->
|
<!-- 话术库 -->
|
||||||
<div style="margin-left: 9px; margin-top:5px;cursor: pointer;">
|
<div style="margin-left: 15px; margin-top:4px;cursor: pointer;">
|
||||||
<img :src="Picture" class="w-32px h-32px" @click="huashu" />
|
<img :src="xiaoxi" class="w-22px h-22px" @click="huashu" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 转接按钮 -->
|
<!-- 转接按钮 -->
|
||||||
<el-dropdown placement="top"
|
<el-dropdown placement="top"
|
||||||
style="margin-left: auto;margin-right: 15px; margin-top:5px;margin-top:5px;cursor: pointer;"
|
style="margin-left: auto;margin-right: 15px;cursor: pointer;"
|
||||||
ref="dropdown1" trigger="contextmenu">
|
ref="dropdown1" trigger="contextmenu">
|
||||||
<div>
|
<div>
|
||||||
<img :src="Picture2" class="w-27px h-27px" @click="getOnlineStaffList" title="转接" />
|
<img :src="Picture2" class="w-62px h-20px" @click="getOnlineStaffList" title="转接" />
|
||||||
</div>
|
</div>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-item v-for="staff in onlineStaffList" :key="staff.id"
|
<el-dropdown-item v-for="staff in onlineStaffList" :key="staff.id"
|
||||||
@ -140,7 +149,8 @@
|
|||||||
import PictureSelectUpload from './tools/PictureSelectUpload.vue'
|
import PictureSelectUpload from './tools/PictureSelectUpload.vue'
|
||||||
// import VerbalTrick from './tools/VerbalTrick.vue'
|
// import VerbalTrick from './tools/VerbalTrick.vue'
|
||||||
import Picture from '@/views/mall/promotion/kefu/components/asserts/huashu.png'
|
import Picture from '@/views/mall/promotion/kefu/components/asserts/huashu.png'
|
||||||
import Picture2 from '@/views/mall/promotion/kefu/components/asserts/zhuanjie.png'
|
import xiaoxi from '@/views/mall/promotion/kefu/components/asserts/xiaoxi.png'
|
||||||
|
import Picture2 from '@/views/mall/promotion/kefu/components/asserts/zj.png'
|
||||||
import ProductItem from './message/ProductItem.vue'
|
import ProductItem from './message/ProductItem.vue'
|
||||||
import OrderItem from './message/OrderItem.vue'
|
import OrderItem from './message/OrderItem.vue'
|
||||||
import { Emoji, useEmoji } from './tools/emoji'
|
import { Emoji, useEmoji } from './tools/emoji'
|
||||||
@ -178,6 +188,8 @@
|
|||||||
import { KeFuConversationApi } from '@/api/mall/promotion/kefu/conversation'
|
import { KeFuConversationApi } from '@/api/mall/promotion/kefu/conversation'
|
||||||
import { number } from 'vue-types'
|
import { number } from 'vue-types'
|
||||||
const onlineStaffList = ref<SupportStaffVO[]>([]) // 在线客服列表的数据
|
const onlineStaffList = ref<SupportStaffVO[]>([]) // 在线客服列表的数据
|
||||||
|
const messages = useMessage() // 消息弹窗
|
||||||
|
const kefuName = ref('')
|
||||||
|
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
@ -250,6 +262,7 @@
|
|||||||
/** 获得新会话的消息列表 */
|
/** 获得新会话的消息列表 */
|
||||||
// TODO @puhui999:可优化:可以考虑本地做每个会话的消息 list 缓存;然后点击切换时,读取缓存;然后异步获取新消息,merge 下;
|
// TODO @puhui999:可优化:可以考虑本地做每个会话的消息 list 缓存;然后点击切换时,读取缓存;然后异步获取新消息,merge 下;
|
||||||
const getNewMessageList = async (val : KeFuConversationRespVO) => {
|
const getNewMessageList = async (val : KeFuConversationRespVO) => {
|
||||||
|
// console.log('22222222',val)
|
||||||
// 会话切换,重置相关参数
|
// 会话切换,重置相关参数
|
||||||
queryParams.pageNo = 1
|
queryParams.pageNo = 1
|
||||||
messageList.value = []
|
messageList.value = []
|
||||||
@ -309,7 +322,6 @@
|
|||||||
dialogVisible.value = false;
|
dialogVisible.value = false;
|
||||||
}
|
}
|
||||||
const clickMenu = (id : string) => {
|
const clickMenu = (id : string) => {
|
||||||
console.log('1111111111', id)
|
|
||||||
getVerbalTrickList(id)
|
getVerbalTrickList(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,6 +429,7 @@
|
|||||||
status: 1,
|
status: 1,
|
||||||
})
|
})
|
||||||
onlineStaffList.value = data.list
|
onlineStaffList.value = data.list
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -424,7 +437,16 @@
|
|||||||
/** 转接客服人员列表 id:会话id kefuId:客服人员id */
|
/** 转接客服人员列表 id:会话id kefuId:客服人员id */
|
||||||
const transferConversion = async (kefuId : number) => {
|
const transferConversion = async (kefuId : number) => {
|
||||||
try {
|
try {
|
||||||
await KeFuConversationApi.transferConversion(queryParams.conversationId, kefuId)
|
const re = await KeFuConversationApi.transferConversion(queryParams.conversationId, kefuId)
|
||||||
|
kefuName.value = re
|
||||||
|
// 2. 组织发送消息
|
||||||
|
const msg = {
|
||||||
|
conversationId: conversation.value.id,
|
||||||
|
contentType: KeFuMessageContentTypeEnum.SYSTEM,
|
||||||
|
content: '已为您转接至' + re,
|
||||||
|
}
|
||||||
|
await sendMessage(msg)
|
||||||
|
messages.success('转接成功')
|
||||||
} finally {
|
} finally {
|
||||||
// todo 刷新会话列表
|
// todo 刷新会话列表
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<div style="height: 522px ;" ref="userInfoRef" >
|
||||||
|
<div>
|
||||||
|
<span style="display: flex;">
|
||||||
|
<el-avatar
|
||||||
|
style="border: 1px solid #f8f9ee;"
|
||||||
|
src="https://zysc.fjptzykj.com:3000/shangcheng/29e07b7973621a1a9679744e18f2b4efd1fd50d857fe594879e813d67be3e2db.png"
|
||||||
|
/>
|
||||||
|
<span style="margin-left:5px;margin-top: 9px;">Marvin</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;" />
|
||||||
|
<div>
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">手机号</span><span style="margin-left: 47px;font-size: 14px ;">暂无</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">分组</span><span style="margin-left: 60px;font-size: 14px ;">D类客户</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">用户标签</span><span style="margin-left: 33px;font-size: 14px ;">小客户</span>
|
||||||
|
</div>
|
||||||
|
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;" />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">用户等级</span><span style="margin-left: 35px;font-size: 14px ;">暂无</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">推荐人</span><span style="margin-left: 47px;font-size: 14px ;">客户</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">用户类型</span><span style="margin-left: 33px;font-size: 14px ;">小客户</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">余额</span><span style="margin-left: 60px;font-size: 14px ;">暂无</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">推广员</span><span style="margin-left: 47px;font-size: 14px ;">客户</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">生日</span><span style="margin-left: 60px;font-size: 14px ;">小客户</span>
|
||||||
|
</div>
|
||||||
|
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script >
|
||||||
|
import * as UserApi from '@/api/member/user'
|
||||||
|
|
||||||
|
|
||||||
|
// const user = ref<UserApi.UserVO>({} as UserApi.UserVO)
|
||||||
|
|
||||||
|
|
||||||
|
// const getUserId = async (id: string) => {
|
||||||
|
// user.value = await UserApi.getUserInfo(id)
|
||||||
|
// }
|
||||||
|
// defineExpose({ getUserId })
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
@ -1,5 +1,6 @@
|
|||||||
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 MemberBrowsingHistory from './history/MemberBrowsingHistory.vue'
|
||||||
|
import UserInfo from './UserInfo.vue'
|
||||||
|
|
||||||
export { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory }
|
export { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory ,UserInfo}
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-popover :width="500" placement="top" trigger="click">
|
<el-popover :width="500" placement="top" trigger="click">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<Icon :size="30" class="ml-10px cursor-pointer" icon="twemoji:grinning-face" />
|
<!-- <Icon :size="30" class="ml-10px cursor-pointer" icon="twemoji:grinning-face" /> -->
|
||||||
|
<img :src="biaoqing" style="margin-left: 10px;" class="w-23px h-23px" />
|
||||||
</template>
|
</template>
|
||||||
<ElScrollbar height="300px">
|
<ElScrollbar height="300px">
|
||||||
<ul class="ml-2 flex flex-wrap px-2">
|
<ul class="ml-2 flex flex-wrap px-2">
|
||||||
@ -26,8 +27,10 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineOptions({ name: 'EmojiSelectPopover' })
|
defineOptions({ name: 'EmojiSelectPopover' })
|
||||||
|
import biaoqing from '@/views/mall/promotion/kefu/components/asserts/biaoqian.png'
|
||||||
import { Emoji, useEmoji } from './emoji'
|
import { Emoji, useEmoji } from './emoji'
|
||||||
|
|
||||||
|
|
||||||
const { getEmojiList } = useEmoji()
|
const { getEmojiList } = useEmoji()
|
||||||
const emojiList = computed(() => getEmojiList())
|
const emojiList = computed(() => getEmojiList())
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<!-- 图片选择 -->
|
<!-- 图片选择 -->
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<img :src="Picture" class="w-35px h-35px" @click="selectAndUpload" />
|
<img :src="tupian" class="w-23px h-23px" @click="selectAndUpload" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Picture from '@/views/mall/promotion/kefu/components/asserts/picture.svg'
|
import tupian from '@/views/mall/promotion/kefu/components/asserts/tupian.png'
|
||||||
import * as FileApi from '@/api/infra/file'
|
import * as FileApi from '@/api/infra/file'
|
||||||
|
|
||||||
defineOptions({ name: 'PictureSelectUpload' })
|
defineOptions({ name: 'PictureSelectUpload' })
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span style="display: flex; margin-top: 15px;">
|
<span style="display: flex; margin-top: 15px;">
|
||||||
<el-avatar
|
<el-avatar
|
||||||
src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
|
:src="pic"
|
||||||
/>
|
/>
|
||||||
<span style="margin-left:5px;margin-top: 9px;">客服桃子</span>
|
<span style="margin-left:5px;margin-top: 9px;">{{name}}</span>
|
||||||
<el-switch
|
<!-- <el-switch
|
||||||
style="margin-top: 4px;--el-switch-on-color: #13ce66; --el-switch-off-color: #b6bac1;"
|
style="margin-top: 4px;--el-switch-on-color: #13ce66; --el-switch-off-color: #b6bac1;"
|
||||||
v-model="value6"
|
v-model="value6"
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
@ -27,11 +27,11 @@
|
|||||||
inline-prompt
|
inline-prompt
|
||||||
active-text="在线"
|
active-text="在线"
|
||||||
inactive-text="下线"
|
inactive-text="下线"
|
||||||
/>
|
/> -->
|
||||||
</span>
|
</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-button size="small" round style="margin-top:23px;margin-left:75%">退出登录</el-button>
|
<el-button @click="out" size="small" round style="margin-top:23px;margin-left:75%">退出</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
@ -41,14 +41,11 @@
|
|||||||
active-text-color="white"
|
active-text-color="white"
|
||||||
style="width:100%;display: flex;"
|
style="width:100%;display: flex;"
|
||||||
>
|
>
|
||||||
<el-menu-item style="width:33%;height:70px" index="1">客户信息</el-menu-item>
|
<el-menu-item @click="userInfo" style="width:33%;height:70px" index="1">客户信息</el-menu-item>
|
||||||
<el-menu-item style="width:33%;height:70px" index="2">交易订单</el-menu-item>
|
<el-menu-item @click="zuoji" style="width:33%;height:70px" index="2">他的足迹</el-menu-item>
|
||||||
<el-menu-item style="width:34%;height:70px" index="3">商品信息</el-menu-item>
|
<!-- <el-menu-item style="width:34%;height:70px" index="3">商品信息</el-menu-item> -->
|
||||||
</el-menu>
|
</el-menu>
|
||||||
|
|
||||||
|
|
||||||
<!-- <div style="color: #ffffff;" class="header-title h-60px flex justify-center items-center">他的足迹</div> -->
|
|
||||||
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
@ -74,9 +71,55 @@
|
|||||||
|
|
||||||
<!-- 会员足迹(选中会话的会员足迹) -->
|
<!-- 会员足迹(选中会话的会员足迹) -->
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<ContentWrap>
|
<ContentWrap v-show="chick == '2'">
|
||||||
<MemberBrowsingHistory ref="memberBrowsingHistoryRef"/>
|
<MemberBrowsingHistory ref="memberBrowsingHistoryRef"/>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
|
<ContentWrap v-show = "chick == '1'">
|
||||||
|
<div style="height: 522px ;" >
|
||||||
|
<div>
|
||||||
|
<span style="display: flex;">
|
||||||
|
<el-avatar
|
||||||
|
style="border: 1px solid #f8f9ee;"
|
||||||
|
:src="user.avatar"
|
||||||
|
/>
|
||||||
|
<span style="margin-left:5px;margin-top: 9px;">{{user.nickname}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;" />
|
||||||
|
<div>
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">手机号</span><span style="margin-left: 47px;font-size: 14px ;">{{user.mobile}}</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">分组</span><span style="margin-left: 60px;font-size: 14px ;">{{user.groupName}}</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">用户标签</span><span style="margin-left: 33px;font-size: 14px ;">小客户</span>
|
||||||
|
</div>
|
||||||
|
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;" />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">用户等级</span><span style="margin-left: 35px;font-size: 14px ;">{{user.levelName}}</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">推荐人</span><span style="margin-left: 47px;font-size: 14px ;">客户</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">用户类型</span><span style="margin-left: 33px;font-size: 14px ;">小客户</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">积分</span><span style="margin-left: 60px;font-size: 14px ;">{{user.point}}</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">推广员</span><span style="margin-left: 47px;font-size: 14px ;">客户</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<span style="color: #5d5d59;font-size: 13px ;">生日</span><span style="margin-left: 60px;font-size: 14px ;">{{user.birthday}}</span>
|
||||||
|
</div>
|
||||||
|
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;" />
|
||||||
|
</div>
|
||||||
|
</ContentWrap>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
@ -84,12 +127,15 @@
|
|||||||
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {KeFuConversationList, KeFuMessageList, MemberBrowsingHistory} from './components'
|
import { KeFuConversationList, KeFuMessageList,MemberBrowsingHistory,UserInfo } 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, getAccessToken} from '@/utils/auth'
|
import {getRefreshToken, getAccessToken} from '@/utils/auth'
|
||||||
import {useWebSocket} from '@vueuse/core'
|
import {useWebSocket} from '@vueuse/core'
|
||||||
import {Search} from '@element-plus/icons-vue'
|
import {Search} from '@element-plus/icons-vue'
|
||||||
|
import * as UserApi from '@/api/member/user'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
defineOptions({name: 'KeFu'})
|
defineOptions({name: 'KeFu'})
|
||||||
|
|
||||||
@ -97,6 +143,15 @@
|
|||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const name = params.get('name');
|
||||||
|
const pic = params.get('pic');
|
||||||
|
const conversations = ref<KeFuConversationRespVO[]>([])
|
||||||
|
// const userInfoRef = ref<InstanceType<typeof UserInfo>>()
|
||||||
|
const user = ref<UserApi.UserVO>({} as UserApi.UserVO)
|
||||||
|
const userId = ref(0)
|
||||||
|
|
||||||
|
|
||||||
// ======================= WebSocket start =======================
|
// ======================= WebSocket start =======================
|
||||||
const server = ref(
|
const server = ref(
|
||||||
(import.meta.env.VITE_BASE_URL + '/infra/ws').replace('http', 'ws') +
|
(import.meta.env.VITE_BASE_URL + '/infra/ws').replace('http', 'ws') +
|
||||||
@ -104,6 +159,19 @@
|
|||||||
getRefreshToken() // 使用 getRefreshToken() 方法,而不使用 getAccessToken() 方法的原因:WebSocket 无法方便的刷新访问令牌
|
getRefreshToken() // 使用 getRefreshToken() 方法,而不使用 getAccessToken() 方法的原因:WebSocket 无法方便的刷新访问令牌
|
||||||
) // WebSocket 服务地址
|
) // WebSocket 服务地址
|
||||||
|
|
||||||
|
const chick = ref('2')
|
||||||
|
const userInfo = async () =>{
|
||||||
|
chick.value = '1'
|
||||||
|
user.value = await UserApi.getUserInfo(userId.value)
|
||||||
|
}
|
||||||
|
const zuoji = () =>{
|
||||||
|
chick.value = '2'
|
||||||
|
// keFuChatBoxRef.value?.getNewMessageList(conversations.value)
|
||||||
|
memberBrowsingHistoryRef.value?.initHistory(conversations.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** 发起 WebSocket 连接 */
|
/** 发起 WebSocket 连接 */
|
||||||
const {data, close, open} = useWebSocket(server.value, {
|
const {data, close, open} = useWebSocket(server.value, {
|
||||||
autoReconnect: false,
|
autoReconnect: false,
|
||||||
@ -158,10 +226,18 @@
|
|||||||
const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>()
|
const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>()
|
||||||
const memberBrowsingHistoryRef = ref<InstanceType<typeof MemberBrowsingHistory>>()
|
const memberBrowsingHistoryRef = ref<InstanceType<typeof MemberBrowsingHistory>>()
|
||||||
const handleChange = (conversation: KeFuConversationRespVO) => {
|
const handleChange = (conversation: KeFuConversationRespVO) => {
|
||||||
|
conversations.value = conversation
|
||||||
|
chick.value = '2'
|
||||||
|
userId.value = conversation.userId
|
||||||
keFuChatBoxRef.value?.getNewMessageList(conversation)
|
keFuChatBoxRef.value?.getNewMessageList(conversation)
|
||||||
memberBrowsingHistoryRef.value?.initHistory(conversation)
|
memberBrowsingHistoryRef.value?.initHistory(conversation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const out = () =>{
|
||||||
|
window.close();
|
||||||
|
// window.location.href = '/kefu/support-staff';
|
||||||
|
}
|
||||||
|
|
||||||
/** 初始化 */
|
/** 初始化 */
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getConversationList()
|
getConversationList()
|
||||||
|
@ -35,15 +35,6 @@
|
|||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item label="登录密码" prop="password">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.password"
|
|
||||||
placeholder="请输入登录密码"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
class="!w-240px"
|
|
||||||
/>
|
|
||||||
</el-form-item> -->
|
|
||||||
<el-form-item label="客服状态" prop="status">
|
<el-form-item label="客服状态" prop="status">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.status"
|
v-model="queryParams.status"
|
||||||
@ -59,36 +50,6 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item label="手机订单管理" prop="orderManage">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.orderManage"
|
|
||||||
placeholder="请选择手机订单管理"
|
|
||||||
clearable
|
|
||||||
class="!w-240px"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.KEFU_SUPPORT_STAFF_ORDER_MANAGE)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item> -->
|
|
||||||
<!-- <el-form-item label="订单通知" prop="orderInform">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.orderInform"
|
|
||||||
placeholder="请选择订单通知"
|
|
||||||
clearable
|
|
||||||
class="!w-240px"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.KEFU_SUPPORT_STAFF_ORDER_INFORM)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item> -->
|
|
||||||
<el-form-item label="创建时间" prop="createTime">
|
<el-form-item label="创建时间" prop="createTime">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="queryParams.createTime"
|
v-model="queryParams.createTime"
|
||||||
@ -143,22 +104,11 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="手机号码" align="center" prop="phone" />
|
<el-table-column label="手机号码" align="center" prop="phone" />
|
||||||
<el-table-column label="登录账号" align="center" prop="account" />
|
<el-table-column label="登录账号" align="center" prop="account" />
|
||||||
<!-- <el-table-column label="登录密码" align="center" prop="password" /> -->
|
|
||||||
<el-table-column label="客服状态" align="center" prop="status">
|
<el-table-column label="客服状态" align="center" prop="status">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<dict-tag :type="DICT_TYPE.KEFU_SUPPORT_STAFF_STATUS" :value="scope.row.status" />
|
<dict-tag :type="DICT_TYPE.KEFU_SUPPORT_STAFF_STATUS" :value="scope.row.status" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- <el-table-column label="手机订单管理" align="center" prop="orderManage">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.KEFU_SUPPORT_STAFF_ORDER_MANAGE" :value="scope.row.orderManage" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="订单通知" align="center" prop="orderInform">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.KEFU_SUPPORT_STAFF_ORDER_INFORM" :value="scope.row.orderInform" />
|
|
||||||
</template>
|
|
||||||
</el-table-column> -->
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="创建时间"
|
label="创建时间"
|
||||||
align="center"
|
align="center"
|
||||||
@ -188,7 +138,7 @@
|
|||||||
v-if="scope.row.status == 1"
|
v-if="scope.row.status == 1"
|
||||||
link
|
link
|
||||||
type="success"
|
type="success"
|
||||||
@click="handleEnterConsole(scope.row.id)"
|
@click="handleEnterConsole(scope.row.id,scope.row.name,scope.row.pic)"
|
||||||
>
|
>
|
||||||
进入工作台
|
进入工作台
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -206,6 +156,9 @@
|
|||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
<SupportStaffForm ref="formRef" @success="getList" />
|
<SupportStaffForm ref="formRef" @success="getList" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -215,6 +168,7 @@ import download from '@/utils/download'
|
|||||||
import { SupportStaffApi, SupportStaffVO } from '@/api/mall/promotion/supportstaff'
|
import { SupportStaffApi, SupportStaffVO } from '@/api/mall/promotion/supportstaff'
|
||||||
import SupportStaffForm from './SupportStaffForm.vue'
|
import SupportStaffForm from './SupportStaffForm.vue'
|
||||||
import { setStaffToken} from '@/utils/auth'
|
import { setStaffToken} from '@/utils/auth'
|
||||||
|
import { createImageViewer } from '@/components/ImageViewer'
|
||||||
/** 客服人员 列表 */
|
/** 客服人员 列表 */
|
||||||
defineOptions({ name: 'SupportStaff' })
|
defineOptions({ name: 'SupportStaff' })
|
||||||
|
|
||||||
@ -271,6 +225,13 @@ const openForm = (type: string, id?: number) => {
|
|||||||
formRef.value.open(type, id)
|
formRef.value.open(type, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const imagePreview = (imgUrl: string) => {
|
||||||
|
createImageViewer({
|
||||||
|
zIndex: 9999999,
|
||||||
|
urlList: [imgUrl]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
const handleDelete = async (id: number) => {
|
const handleDelete = async (id: number) => {
|
||||||
try {
|
try {
|
||||||
@ -284,9 +245,10 @@ const handleDelete = async (id: number) => {
|
|||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
/** 客服进入工作台 */
|
/** 客服进入工作台 */
|
||||||
const handleEnterConsole = async (id: number) => {
|
const handleEnterConsole = async (id: number,name: string,pic: string) => {
|
||||||
setStaffToken(id);
|
setStaffToken(id);
|
||||||
window.open(`${window.location.origin}/kefu/kefu`, '_blank');
|
const url = `${window.location.origin}/kefu/kefu?name=${encodeURIComponent(name)}&pic=${encodeURIComponent(pic)}`;
|
||||||
|
window.open(url, '_blank');
|
||||||
}
|
}
|
||||||
/** 导出按钮操作 */
|
/** 导出按钮操作 */
|
||||||
const handleExport = async () => {
|
const handleExport = async () => {
|
||||||
|
@ -84,9 +84,9 @@ public class KeFuConversationController {
|
|||||||
}
|
}
|
||||||
@Operation(summary = "转接会话给指定客服")
|
@Operation(summary = "转接会话给指定客服")
|
||||||
@GetMapping("/transfer/{id}/{kefuId}")
|
@GetMapping("/transfer/{id}/{kefuId}")
|
||||||
public CommonResult<Boolean> transferConversation(@PathVariable("id") Long id, @PathVariable("kefuId") Long kefuId) {
|
public CommonResult<String> transferConversation(@PathVariable("id") Long id, @PathVariable("kefuId") Long kefuId) {
|
||||||
conversationService.transferConversation(id, kefuId);
|
String name = conversationService.transferConversation(id, kefuId);
|
||||||
// 处理逻辑
|
// 处理逻辑
|
||||||
return success(true);
|
return success(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,13 +19,13 @@ public interface KeFuConversationMapper extends BaseMapperX<KeFuConversationDO>
|
|||||||
default List<KeFuConversationDO> selectConversationList() {
|
default List<KeFuConversationDO> selectConversationList() {
|
||||||
return selectList(new LambdaQueryWrapperX<KeFuConversationDO>()
|
return selectList(new LambdaQueryWrapperX<KeFuConversationDO>()
|
||||||
.eq(KeFuConversationDO::getAdminDeleted, Boolean.FALSE)
|
.eq(KeFuConversationDO::getAdminDeleted, Boolean.FALSE)
|
||||||
.orderByDesc(KeFuConversationDO::getCreateTime));
|
.orderByDesc(KeFuConversationDO::getLastMessageTime));
|
||||||
}
|
}
|
||||||
default List<KeFuConversationDO> selectConversationList(Long kefuId) {
|
default List<KeFuConversationDO> selectConversationList(Long kefuId) {
|
||||||
return selectList(new LambdaQueryWrapperX<KeFuConversationDO>()
|
return selectList(new LambdaQueryWrapperX<KeFuConversationDO>()
|
||||||
.eq(KeFuConversationDO::getAdminDeleted, Boolean.FALSE)
|
.eq(KeFuConversationDO::getAdminDeleted, Boolean.FALSE)
|
||||||
.eqIfPresent(KeFuConversationDO::getKefuId, kefuId)
|
.eqIfPresent(KeFuConversationDO::getKefuId, kefuId)
|
||||||
.orderByDesc(KeFuConversationDO::getCreateTime));
|
.orderByDesc(KeFuConversationDO::getLastMessageTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
default void updateAdminUnreadMessageCountIncrement(Long id) {
|
default void updateAdminUnreadMessageCountIncrement(Long id) {
|
||||||
|
@ -102,5 +102,5 @@ public interface KeFuConversationService {
|
|||||||
* @param kefuId 客服id
|
* @param kefuId 客服id
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
void transferConversation(Long id, Long kefuId);
|
String transferConversation(Long id, Long kefuId);
|
||||||
}
|
}
|
@ -5,7 +5,9 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
|||||||
import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;
|
import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;
|
||||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
|
import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
|
||||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
|
import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
|
||||||
|
import cn.iocoder.yudao.module.promotion.dal.dataobject.supportstaff.SupportStaffDO;
|
||||||
import cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuConversationMapper;
|
import cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuConversationMapper;
|
||||||
|
import cn.iocoder.yudao.module.promotion.dal.mysql.supportstaff.SupportStaffMapper;
|
||||||
import cn.iocoder.yudao.module.promotion.enums.kefu.KeFuMessageContentTypeEnum;
|
import cn.iocoder.yudao.module.promotion.enums.kefu.KeFuMessageContentTypeEnum;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@ -30,6 +32,9 @@ public class KeFuConversationServiceImpl implements KeFuConversationService {
|
|||||||
@Resource
|
@Resource
|
||||||
private KeFuConversationMapper conversationMapper;
|
private KeFuConversationMapper conversationMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SupportStaffMapper supportStaffMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteKefuConversation(Long id) {
|
public void deleteKefuConversation(Long id) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
@ -126,11 +131,13 @@ public class KeFuConversationServiceImpl implements KeFuConversationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transferConversation(Long id, Long kefuId) {
|
public String transferConversation(Long id, Long kefuId) {
|
||||||
KeFuConversationDO keFuConversationDO = new KeFuConversationDO();
|
KeFuConversationDO keFuConversationDO = new KeFuConversationDO();
|
||||||
keFuConversationDO.setId(id);
|
keFuConversationDO.setId(id);
|
||||||
keFuConversationDO.setKefuId(kefuId);
|
keFuConversationDO.setKefuId(kefuId);
|
||||||
conversationMapper.updateById(keFuConversationDO);
|
conversationMapper.updateById(keFuConversationDO);
|
||||||
|
SupportStaffDO supportStaffDO = supportStaffMapper.selectById(kefuId);
|
||||||
|
return supportStaffDO.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -17,7 +17,10 @@ import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
|
|||||||
import cn.iocoder.yudao.module.member.dal.dataobject.memberCode.MemberCodeDo;
|
import cn.iocoder.yudao.module.member.dal.dataobject.memberCode.MemberCodeDo;
|
||||||
import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO;
|
import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO;
|
||||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||||
|
import cn.iocoder.yudao.module.member.dal.mysql.group.MemberGroupMapper;
|
||||||
|
import cn.iocoder.yudao.module.member.dal.mysql.level.MemberLevelMapper;
|
||||||
import cn.iocoder.yudao.module.member.dal.mysql.memberCode.MemberCodeMapper;
|
import cn.iocoder.yudao.module.member.dal.mysql.memberCode.MemberCodeMapper;
|
||||||
|
import cn.iocoder.yudao.module.member.dal.mysql.tag.MemberTagMapper;
|
||||||
import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
|
import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
|
||||||
import cn.iocoder.yudao.module.member.service.clubCard.ClubCardService;
|
import cn.iocoder.yudao.module.member.service.clubCard.ClubCardService;
|
||||||
import cn.iocoder.yudao.module.member.service.group.MemberGroupService;
|
import cn.iocoder.yudao.module.member.service.group.MemberGroupService;
|
||||||
@ -52,6 +55,15 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLogi
|
|||||||
@Validated
|
@Validated
|
||||||
public class MemberUserController {
|
public class MemberUserController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MemberLevelMapper memberLevelMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MemberGroupMapper memberGroupMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MemberTagMapper memberTagMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private MemberUserService memberUserService;
|
private MemberUserService memberUserService;
|
||||||
@Resource
|
@Resource
|
||||||
@ -169,4 +181,23 @@ public class MemberUserController {
|
|||||||
memberCodeMapper.insert(memberCodeDo);
|
memberCodeMapper.insert(memberCodeDo);
|
||||||
return success(uuid);
|
return success(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/getUserInfo")
|
||||||
|
public CommonResult<MemberUserDO> getUserInfo(Long id){
|
||||||
|
MemberUserDO user = memberUserService.getUser(id);
|
||||||
|
|
||||||
|
if (user.getGroupId() != null){
|
||||||
|
MemberGroupDO groupDO = memberGroupMapper.selectOne("id", user.getGroupId());
|
||||||
|
user.setGroupName(groupDO.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.getLevelId() != null && user.getLevelId() != 0){
|
||||||
|
MemberLevelDO levelDO = memberLevelMapper.selectOne("id", user.getLevelId());
|
||||||
|
user.setLevelName(levelDO.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return success(user);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -131,6 +131,9 @@ public class MemberUserDO extends TenantBaseDO {
|
|||||||
* 关联 {@link MemberLevelDO#getId()} 字段
|
* 关联 {@link MemberLevelDO#getId()} 字段
|
||||||
*/
|
*/
|
||||||
private Long levelId;
|
private Long levelId;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String levelName;
|
||||||
/**
|
/**
|
||||||
* 会员经验
|
* 会员经验
|
||||||
*/
|
*/
|
||||||
@ -142,6 +145,9 @@ public class MemberUserDO extends TenantBaseDO {
|
|||||||
*/
|
*/
|
||||||
private Long groupId;
|
private Long groupId;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String groupName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否绑过卡,是否开通过会员(0:未开通,1:试用,2:有效期,3:永久,4:过期)
|
* 是否绑过卡,是否开通过会员(0:未开通,1:试用,2:有效期,3:永久,4:过期)
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user