Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
d3f30446
authored
Nov 05, 2024
by
芋道源码
Committed by
Gitee
Nov 05, 2024
Browse files
Options
Browse Files
Download
Plain Diff
!576 【功能完善】商城客服
Merge pull request !576 from puhui999/dev-crm
parents
1d01955b
70abc5fc
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
281 additions
and
151 deletions
+281
-151
src/api/mall/promotion/kefu/message/index.ts
+3
-3
src/store/modules/mall/kefu.ts
+37
-0
src/views/mall/promotion/kefu/components/KeFuConversationList.vue
+11
-12
src/views/mall/promotion/kefu/components/KeFuMessageList.vue
+102
-82
src/views/mall/promotion/kefu/components/index.ts
+2
-2
src/views/mall/promotion/kefu/components/member/MemberInfo.vue
+111
-29
src/views/mall/promotion/kefu/components/member/OrderBrowsingHistory.vue
+0
-0
src/views/mall/promotion/kefu/components/member/ProductBrowsingHistory.vue
+0
-0
src/views/mall/promotion/kefu/index.vue
+15
-23
No files found.
src/api/mall/promotion/kefu/message/index.ts
View file @
d3f30446
...
...
@@ -29,8 +29,8 @@ export const KeFuMessageApi = {
url
:
'/promotion/kefu-message/update-read-status?conversationId='
+
conversationId
})
},
// 获得消息
分页
数据
getKeFuMessage
Page
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
'/promotion/kefu-message/
page
'
,
params
})
// 获得消息数据
getKeFuMessage
List
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
'/promotion/kefu-message/
list
'
,
params
})
}
}
src/store/modules/mall/kefu.ts
0 → 100644
View file @
d3f30446
import
{
store
}
from
'@/store'
import
{
defineStore
}
from
'pinia'
import
{
KeFuConversationApi
,
KeFuConversationRespVO
}
from
'@/api/mall/promotion/kefu/conversation'
import
{
KeFuMessageRespVO
}
from
'@/api/mall/promotion/kefu/message'
// TODO puhui999: 待优化完善
interface
MallKefuInfoVO
{
conversationList
:
KeFuConversationRespVO
[]
// 会话列表
conversationMessageList
:
Map
<
number
,
KeFuMessageRespVO
[]
>
// 会话消息
}
export
const
useMallKefuStore
=
defineStore
(
'mall-kefu'
,
{
state
:
():
MallKefuInfoVO
=>
({
conversationList
:
[],
conversationMessageList
:
new
Map
<
number
,
KeFuMessageRespVO
[]
>
()
// key 会话,value 会话消息列表
}),
getters
:
{
getConversationList
():
KeFuConversationRespVO
[]
{
return
this
.
conversationList
},
getConversationMessageList
():
Map
<
number
,
KeFuMessageRespVO
[]
>
{
return
this
.
conversationMessageList
}
},
actions
:
{
async
setConversationList
()
{
const
list
=
await
KeFuConversationApi
.
getConversationList
()
list
.
sort
((
a
:
KeFuConversationRespVO
,
_
)
=>
(
a
.
adminPinned
?
-
1
:
1
))
this
.
conversationList
=
list
}
// async setConversationMessageList(conversationId: number) {}
}
})
export
const
useUserStoreWithOut
=
()
=>
{
return
useMallKefuStore
(
store
)
}
src/views/mall/promotion/kefu/components/KeFuConversationList.vue
View file @
d3f30446
<
template
>
<div
class=
"kefu"
>
<el-aside
class=
"kefu p-5px 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
>
...
...
@@ -180,11 +181,12 @@ watch(showRightMenu, (val) => {
<
style
lang=
"scss"
scoped
>
.kefu
{
background-color
:
#fff
;
&-conversation
{
height
:
60px
;
padding
:
10px
;
//
background-color
:
#fff
;
transition
:
border-left
0.05s
ease-in-out
;
/* 设置过渡效果 */
//
transition
:
border-left
0.05s
ease-in-out
;
/* 设置过渡效果 */
.username
{
min-width
:
0
;
...
...
@@ -205,13 +207,10 @@ watch(showRightMenu, (val) => {
}
}
.active
{
border-left
:
5px
#3271ff
solid
;
background-color
:
var
(
--login-bg-color
);
}
.active
,
.pinned
{
background-color
:
var
(
--left-menu-bg-active-color
);
background-color
:
rgba
(
128
,
128
,
128
,
0.5
);
//
透明色,暗黑模式下也能体现
border-radius
:
8px
;
}
.right-menu-ul
{
...
...
src/views/mall/promotion/kefu/components/KeFuMessageList.vue
View file @
d3f30446
<
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%] p
b-3
px"
>
<el-scrollbar
ref=
"scrollbarRef"
always
@
scroll=
"handleScroll"
>
<div
v-if=
"refreshContent"
ref=
"innerRef"
class=
"w-[100%] p
x-10
px"
>
<!-- 消息列表 -->
<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
>
...
...
@@ -156,9 +164,8 @@ const messageList = ref<KeFuMessageRespVO[]>([]) // 消息列表
const
conversation
=
ref
<
KeFuConversationRespVO
>
({}
as
KeFuConversationRespVO
)
// 用户会话
const
showNewMessageTip
=
ref
(
false
)
// 显示有新消息提示
const
queryParams
=
reactive
({
pageNo
:
1
,
pageSize
:
10
,
conversationId
:
0
conversationId
:
0
,
createTime
:
undefined
})
const
total
=
ref
(
0
)
// 消息总条数
const
refreshContent
=
ref
(
false
)
// 内容刷新,主要解决会话消息页面高度不一致导致的滚动功能精度失效
...
...
@@ -167,14 +174,20 @@ const refreshContent = ref(false) // 内容刷新,主要解决会话消息页面
const
getMessageContent
=
computed
(()
=>
(
item
:
any
)
=>
jsonParse
(
item
.
content
))
/** 获得消息列表 */
const
getMessageList
=
async
()
=>
{
const
res
=
await
KeFuMessageApi
.
getKeFuMessagePage
(
queryParams
)
total
.
value
=
res
.
total
const
res
=
await
KeFuMessageApi
.
getKeFuMessageList
(
queryParams
)
if
(
isEmpty
(
res
))
{
// 当返回的是空列表说明没有消息或者已经查询完了历史消息
skipGetMessageList
.
value
=
true
return
}
queryParams
.
createTime
=
formatDate
(
res
.
at
(
-
1
).
createTime
)
as
any
// 情况一:加载最新消息
if
(
queryParams
.
pageNo
===
1
)
{
messageList
.
value
=
res
.
list
if
(
!
queryParams
.
createTime
)
{
messageList
.
value
=
res
}
else
{
// 情况二:加载历史消息
for
(
const
item
of
res
.
list
)
{
for
(
const
item
of
res
)
{
pushMessage
(
item
)
}
}
...
...
@@ -208,8 +221,7 @@ const refreshMessageList = async (message?: any) => {
}
pushMessage
(
message
)
}
else
{
// TODO @puhui999:不基于 page 做。而是流式分页;通过 createTime 排序查询;
queryParams
.
pageNo
=
1
queryParams
.
createTime
=
undefined
await
getMessageList
()
}
...
...
@@ -226,7 +238,6 @@ const refreshMessageList = async (message?: any) => {
// TODO @puhui999:可优化:可以考虑本地做每个会话的消息 list 缓存;然后点击切换时,读取缓存;然后异步获取新消息,merge 下;
const
getNewMessageList
=
async
(
val
:
KeFuConversationRespVO
)
=>
{
// 会话切换,重置相关参数
queryParams
.
pageNo
=
1
messageList
.
value
=
[]
total
.
value
=
0
loadHistory
.
value
=
false
...
...
@@ -234,16 +245,14 @@ const getNewMessageList = async (val: KeFuConversationRespVO) => {
// 设置会话相关属性
conversation
.
value
=
val
queryParams
.
conversationId
=
val
.
id
queryParams
.
createTime
=
undefined
// 获取消息
await
refreshMessageList
()
}
defineExpose
({
getNewMessageList
,
refreshMessageList
})
const
showKeFuMessageList
=
computed
(()
=>
!
isEmpty
(
conversation
.
value
))
// 是否显示聊天区域
const
skipGetMessageList
=
computed
(()
=>
{
// 已加载到最后一页的话则不触发新的消息获取
return
total
.
value
>
0
&&
Math
.
ceil
(
total
.
value
/
queryParams
.
pageSize
)
===
queryParams
.
pageNo
})
// 跳过消息获取
const
skipGetMessageList
=
ref
(
false
)
// 跳过消息获取
/** 处理表情选择 */
const
handleEmojiSelect
=
(
item
:
Emoji
)
=>
{
...
...
@@ -262,7 +271,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
(
'请输入消息后再发送哦!'
)
...
...
@@ -333,8 +346,6 @@ const handleOldMessage = async () => {
return
}
loadHistory
.
value
=
true
// 加载消息列表
queryParams
.
pageNo
+=
1
await
getMessageList
()
// 等页面加载完后,获得上一页最后一条消息的位置,控制滚动到它所在位置
scrollbarRef
.
value
!
.
setScrollTop
(
innerRef
.
value
!
.
clientHeight
-
oldPageHeight
)
...
...
@@ -357,14 +368,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
;
...
...
@@ -381,21 +407,12 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
justify-content
:
flex-start
;
.kefu-message
{
margin-left
:
20px
;
position
:
relative
;
&::before
{
content
:
''
;
width
:
10px
;
height
:
10px
;
left
:
-19px
;
top
:
calc
(
50%
-
10px
);
position
:
absolute
;
border-left
:
5px
solid
transparent
;
border-bottom
:
5px
solid
transparent
;
border-top
:
5px
solid
transparent
;
border-right
:
5px
solid
var
(
--app-content-bg-color
);
}
background-color
:
rgb
(
245
,
245
,
245
);
margin-left
:
10px
;
margin-top
:
18px
;
border-top-right-radius
:
10px
;
border-bottom-right-radius
:
10px
;
border-bottom-left-radius
:
10px
;
}
}
...
...
@@ -403,37 +420,25 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
justify-content
:
flex-end
;
.kefu-message
{
margin-right
:
20px
;
position
:
relative
;
&::after
{
content
:
''
;
width
:
10px
;
height
:
10px
;
right
:
-19px
;
top
:
calc
(
50%
-
10px
);
position
:
absolute
;
border-left
:
5px
solid
var
(
--app-content-bg-color
);
border-bottom
:
5px
solid
transparent
;
border-top
:
5px
solid
transparent
;
border-right
:
5px
solid
transparent
;
}
background-color
:
rgb
(
206
,
223
,
255
);
margin-right
:
10px
;
margin-top
:
18px
;
border-top-left-radius
:
10px
;
border-bottom-right-radius
:
10px
;
border-bottom-left-radius
:
10px
;
}
}
//
消息气泡
.kefu-message
{
color
:
#a9a9a9
;
border-radius
:
5px
;
box-shadow
:
3px
3px
5px
rgba
(
220
,
220
,
220
,
0.1
);
color
:
#414141
;
font-weight
:
500
;
padding
:
5px
10px
;
width
:
auto
;
max-width
:
50%
;
text-align
:
left
;
display
:
inline-block
!important
;
position
:
relative
;
word-break
:
break-all
;
background-color
:
var
(
--app-content-bg-color
);
transition
:
all
0.2s
;
&:hover
{
...
...
@@ -447,21 +452,36 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
border-radius
:
12
rpx
;
padding
:
8
rpx
16
rpx
;
margin-bottom
:
16
rpx
;
//
background-color
:
#e8e8e8
;
color
:
#999
;
font-size
:
24
rpx
;
}
}
.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
>
src/views/mall/promotion/kefu/components/index.ts
View file @
d3f30446
import
KeFuConversationList
from
'./KeFuConversationList.vue'
import
KeFuMessageList
from
'./KeFuMessageList.vue'
import
Member
BrowsingHistory
from
'./history/MemberBrowsingHistory
.vue'
import
Member
Info
from
'./member/MemberInfo
.vue'
export
{
KeFuConversationList
,
KeFuMessageList
,
Member
BrowsingHistory
}
export
{
KeFuConversationList
,
KeFuMessageList
,
Member
Info
}
src/views/mall/promotion/kefu/components/
history/MemberBrowsingHistory
.vue
→
src/views/mall/promotion/kefu/components/
member/MemberInfo
.vue
View file @
d3f30446
<!-- 目录是不是叫 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"
>
<div
:class=
"
{ 'kefu-header-item-activation': tabActivation('会员信息') }"
class="kefu-header-item cursor-pointer flex items-center justify-center"
@click="handleClick('会员信息')"
>
会员信息
</div>
<div
:class=
"
{ 'kefu-header-item-activation': tabActivation('最近浏览') }"
class="kefu-header-item cursor-pointer flex items-center justify-center"
@click="handleClick('最近浏览')"
>
最近浏览
</div>
<div
:class=
"
{ 'kefu-header-item-activation': tabActivation('交易订单') }"
class="kefu-header-item cursor-pointer flex items-center justify-center"
@click="handleClick('交易订单')"
>
交易订单
</div>
</el-header>
<el-main
class=
"kefu-content"
>
<div
v-show=
"!isEmpty(conversation)"
>
<el-scrollbar
ref=
"scrollbarRef"
always
@
scroll=
"handleScroll"
>
<!-- 最近浏览 -->
<ProductBrowsingHistory
v-if=
"activeTab === '最近浏览'"
ref=
"productBrowsingHistoryRef"
/>
<!-- 交易订单 -->
<OrderBrowsingHistory
v-if=
"activeTab === '交易订单'"
ref=
"orderBrowsingHistoryRef"
/>
</el-scrollbar>
</div>
<el-empty
v-show=
"isEmpty(conversation)"
description=
"请选择左侧的一个会话后开始"
/>
</el-main>
</el-container>
</
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'
...
...
@@ -29,25 +48,26 @@ import { ElScrollbar as ElScrollbarType } from 'element-plus/es/components/scrol
defineOptions
({
name
:
'MemberBrowsingHistory'
})
const
active
Name
=
ref
(
'a
'
)
const
active
Tab
=
ref
(
'会员信息
'
)
const
tabActivation
=
computed
(()
=>
(
tab
:
string
)
=>
activeTab
.
value
===
tab
)
/** tab 切换 */
const
productBrowsingHistoryRef
=
ref
<
InstanceType
<
typeof
ProductBrowsingHistory
>>
()
const
orderBrowsingHistoryRef
=
ref
<
InstanceType
<
typeof
OrderBrowsingHistory
>>
()
const
handleClick
=
async
(
tab
:
TabsPaneContext
)
=>
{
active
Name
.
value
=
tab
.
paneName
as
string
const
handleClick
=
async
(
tab
:
string
)
=>
{
active
Tab
.
value
=
tab
await
nextTick
()
await
getHistoryList
()
}
/** 获得历史数据 */
// TODO @puhui:不要用 a、b 哈。就订单列表、浏览列表这种噶
const
getHistoryList
=
async
()
=>
{
switch
(
activeName
.
value
)
{
case
'a'
:
switch
(
activeTab
.
value
)
{
case
'会员信息'
:
break
case
'最近浏览'
:
await
productBrowsingHistoryRef
.
value
?.
getHistoryList
(
conversation
.
value
)
break
case
'
b
'
:
case
'
交易订单
'
:
await
orderBrowsingHistoryRef
.
value
?.
getHistoryList
(
conversation
.
value
)
break
default
:
...
...
@@ -57,11 +77,13 @@ const getHistoryList = async () => {
/** 加载下一页数据 */
const
loadMore
=
async
()
=>
{
switch
(
activeName
.
value
)
{
case
'a'
:
switch
(
activeTab
.
value
)
{
case
'会员信息'
:
break
case
'最近浏览'
:
await
productBrowsingHistoryRef
.
value
?.
loadMore
()
break
case
'
b
'
:
case
'
交易订单
'
:
await
orderBrowsingHistoryRef
.
value
?.
loadMore
()
break
default
:
...
...
@@ -72,7 +94,7 @@ const loadMore = async () => {
/** 浏览历史初始化 */
const
conversation
=
ref
<
KeFuConversationRespVO
>
({}
as
KeFuConversationRespVO
)
// 用户会话
const
initHistory
=
async
(
val
:
KeFuConversationRespVO
)
=>
{
active
Name
.
value
=
'a
'
active
Tab
.
value
=
'会员信息
'
conversation
.
value
=
val
await
nextTick
()
await
getHistoryList
()
...
...
@@ -91,6 +113,66 @@ const handleScroll = debounce(() => {
</
script
>
<
style
lang=
"scss"
scoped
>
.kefu
{
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
:
space-around
;
&-title
{
font-size
:
18px
;
font-weight
:
bold
;
}
&
-item
{
height
:
100%
;
width
:
100%
;
position
:
relative
;
&
-activation
::
before
{
content
:
''
;
position
:
absolute
;
/* 绝对定位 */
top
:
0
;
left
:
0
;
right
:
0
;
bottom
:
0
;
/* 覆盖整个元素 */
border-bottom
:
2px
solid
rgba
(
128
,
128
,
128
,
0.5
);
/* 边框样式 */
pointer-events
:
none
;
/* 确保点击事件不会被伪元素拦截 */
}
&
:hover::before
{
content
:
''
;
position
:
absolute
;
/* 绝对定位 */
top
:
0
;
left
:
0
;
right
:
0
;
bottom
:
0
;
/* 覆盖整个元素 */
border-bottom
:
2px
solid
rgba
(
128
,
128
,
128
,
0.5
);
/* 边框样式 */
pointer-events
:
none
;
/* 确保点击事件不会被伪元素拦截 */
}
}
}
&
-content
{
margin
:
0
;
padding
:
0
;
position
:
relative
;
height
:
100%
;
width
:
100%
;
}
&
-tabs
{
height
:
100%
;
width
:
100%
;
}
}
.header-title
{
border-bottom
:
#e4e0e0
solid
1px
;
}
...
...
src/views/mall/promotion/kefu/components/
history
/OrderBrowsingHistory.vue
→
src/views/mall/promotion/kefu/components/
member
/OrderBrowsingHistory.vue
View file @
d3f30446
File moved
src/views/mall/promotion/kefu/components/
history
/ProductBrowsingHistory.vue
→
src/views/mall/promotion/kefu/components/
member
/ProductBrowsingHistory.vue
View file @
d3f30446
File moved
src/views/mall/promotion/kefu/index.vue
View file @
d3f30446
<
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
,
Member
BrowsingHistory
}
from
'./components'
import
{
KeFuConversationList
,
KeFuMessageList
,
Member
Info
}
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
member
BrowsingHistoryRef
=
ref
<
InstanceType
<
typeof
MemberBrowsingHistory
>>
()
const
member
InfoRef
=
ref
<
InstanceType
<
typeof
MemberInfo
>>
()
const
handleChange
=
(
conversation
:
KeFuConversationRespVO
)
=>
{
keFuChatBoxRef
.
value
?.
getNewMessageList
(
conversation
)
member
BrowsingHistory
Ref
.
value
?.
initHistory
(
conversation
)
member
Info
Ref
.
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%
;
}
/* 定义滚动条样式 */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment