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
Commit
34fe09e2
authored
Nov 04, 2024
by
puhui999
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【功能完善】商城客服: 客服布局样式调整
parent
1d01955b
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
133 additions
and
57 deletions
+133
-57
src/views/mall/promotion/kefu/components/KeFuConversationList.vue
+12
-8
src/views/mall/promotion/kefu/components/KeFuMessageList.vue
+62
-20
src/views/mall/promotion/kefu/components/index.ts
+2
-2
src/views/mall/promotion/kefu/components/member/MemberInfo.vue
+44
-6
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
+13
-21
No files found.
src/views/mall/promotion/kefu/components/KeFuConversationList.vue
View file @
34fe09e2
<
template
>
<
template
>
<div
class=
"kefu"
>
<el-aside
class=
"kefu-conversation-aside p-10px h-100%"
width=
"260px"
>
<div
class=
"color-[#999] font-bold my-10px"
>
会话记录(
{{
conversationList
.
length
}}
)
</div>
<div
<div
v-for=
"item in conversationList"
v-for=
"item in conversationList"
:key=
"item.id"
:key=
"item.id"
...
@@ -22,7 +23,7 @@
...
@@ -22,7 +23,7 @@
<div
class=
"ml-10px w-100%"
>
<div
class=
"ml-10px w-100%"
>
<div
class=
"flex justify-between items-center w-100%"
>
<div
class=
"flex justify-between items-center w-100%"
>
<span
class=
"username"
>
{{
item
.
userNickname
}}
</span>
<span
class=
"username"
>
{{
item
.
userNickname
}}
</span>
<span
class=
"color-[
var(--left-menu-text-color)
]"
style=
"font-size: 13px"
>
<span
class=
"color-[
#999
]"
style=
"font-size: 13px"
>
{{
formatPast
(
item
.
lastMessageTime
,
'YYYY-MM-DD'
)
}}
{{
formatPast
(
item
.
lastMessageTime
,
'YYYY-MM-DD'
)
}}
</span>
</span>
</div>
</div>
...
@@ -31,7 +32,7 @@
...
@@ -31,7 +32,7 @@
v-dompurify-html=
"
v-dompurify-html=
"
getConversationDisplayText(item.lastMessageContentType, item.lastMessageContent)
getConversationDisplayText(item.lastMessageContentType, item.lastMessageContent)
"
"
class=
"last-message flex items-center color-[
var(--left-menu-text-color)
]"
class=
"last-message flex items-center color-[
#999
]"
>
>
</div>
</div>
</div>
</div>
...
@@ -65,7 +66,7 @@
...
@@ -65,7 +66,7 @@
取消
取消
</li>
</li>
</ul>
</ul>
</
div
>
</
el-aside
>
</
template
>
</
template
>
<
script
lang=
"ts"
setup
>
<
script
lang=
"ts"
setup
>
...
@@ -179,7 +180,9 @@ watch(showRightMenu, (val) => {
...
@@ -179,7 +180,9 @@ watch(showRightMenu, (val) => {
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
.kefu
{
.kefu-conversation-aside
{
background-color
:
#fff
;
&-conversation
{
&-conversation
{
height
:
60px
;
height
:
60px
;
padding
:
10px
;
padding
:
10px
;
...
@@ -206,12 +209,13 @@ watch(showRightMenu, (val) => {
...
@@ -206,12 +209,13 @@ watch(showRightMenu, (val) => {
}
}
.active
{
.active
{
border-left
:
5px
#3271ff
solid
;
//
border-left
:
5px
#96afea
solid
;
background-color
:
var
(
--login-bg-color
);
background-color
:
rgba
(
128
,
128
,
128
,
0.5
);
//
透明色,暗黑模式下也能体现
border-radius
:
8px
;
}
}
.pinned
{
.pinned
{
background-color
:
var
(
--left-menu-bg-active-color
);
background-color
:
rgba
(
128
,
128
,
128
,
0.5
);
//
透明色,暗黑模式下也能体现
}
}
.right-menu-ul
{
.right-menu-ul
{
...
...
src/views/mall/promotion/kefu/components/KeFuMessageList.vue
View file @
34fe09e2
<
template
>
<
template
>
<el-container
v-if=
"showKeFuMessageList"
class=
"kefu"
>
<el-container
v-if=
"showKeFuMessageList"
class=
"kefu"
>
<el-header>
<el-header
class=
"kefu-header"
>
<div
class=
"kefu-title"
>
{{
conversation
.
userNickname
}}
</div>
<div
class=
"kefu-title"
>
{{
conversation
.
userNickname
}}
</div>
</el-header>
</el-header>
<el-main
class=
"kefu-content overflow-visible"
>
<el-main
class=
"kefu-content overflow-visible"
>
<el-scrollbar
ref=
"scrollbarRef"
always
height=
"calc(100vh - 495px)"
@
scroll=
"handleScroll"
>
<el-scrollbar
ref=
"scrollbarRef"
always
@
scroll=
"handleScroll"
>
<div
v-if=
"refreshContent"
ref=
"innerRef"
class=
"w-[100%] p
b-3
px"
>
<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
v-for=
"(item, index) in getMessageList0"
:key=
"item.id"
class=
"w-[100%]"
>
<div
class=
"flex justify-center items-center mb-20px"
>
<div
class=
"flex justify-center items-center mb-20px"
>
...
@@ -43,7 +43,9 @@
...
@@ -43,7 +43,9 @@
class=
"w-60px h-60px"
class=
"w-60px h-60px"
/>
/>
<div
<div
:class=
"
{ 'kefu-message': KeFuMessageContentTypeEnum.TEXT === item.contentType }"
:class=
"
{
'kefu-message': KeFuMessageContentTypeEnum.TEXT === item.contentType
}"
class="p-10px"
class="p-10px"
>
>
<!-- 文本消息 -->
<!-- 文本消息 -->
...
@@ -71,10 +73,10 @@
...
@@ -71,10 +73,10 @@
<MessageItem
:message=
"item"
>
<MessageItem
:message=
"item"
>
<ProductItem
<ProductItem
v-if=
"KeFuMessageContentTypeEnum.PRODUCT === item.contentType"
v-if=
"KeFuMessageContentTypeEnum.PRODUCT === item.contentType"
:spuId=
"getMessageContent(item).spuId"
:picUrl=
"getMessageContent(item).picUrl"
:picUrl=
"getMessageContent(item).picUrl"
:price=
"getMessageContent(item).price"
:price=
"getMessageContent(item).price"
:skuText=
"getMessageContent(item).introduction"
:skuText=
"getMessageContent(item).introduction"
:spuId=
"getMessageContent(item).spuId"
:title=
"getMessageContent(item).spuName"
:title=
"getMessageContent(item).spuName"
:titleWidth=
"400"
:titleWidth=
"400"
class=
"max-w-70%"
class=
"max-w-70%"
...
@@ -108,8 +110,7 @@
...
@@ -108,8 +110,7 @@
<Icon
class=
"ml-5px"
icon=
"ep:bottom"
/>
<Icon
class=
"ml-5px"
icon=
"ep:bottom"
/>
</div>
</div>
</el-main>
</el-main>
<el-footer
height=
"230px"
>
<el-footer
class=
"kefu-footer"
>
<div
class=
"h-[100%]"
>
<div
class=
"chat-tools flex items-center"
>
<div
class=
"chat-tools flex items-center"
>
<EmojiSelectPopover
@
select-emoji=
"handleEmojiSelect"
/>
<EmojiSelectPopover
@
select-emoji=
"handleEmojiSelect"
/>
<PictureSelectUpload
<PictureSelectUpload
...
@@ -117,14 +118,21 @@
...
@@ -117,14 +118,21 @@
@
send-picture=
"handleSendPicture"
@
send-picture=
"handleSendPicture"
/>
/>
</div>
</div>
<el-input
v-model=
"message"
:rows=
"6"
style=
"border-style: none"
type=
"textarea"
/>
<el-input
<div
class=
"h-45px flex justify-end"
>
v-model=
"message"
<el-button
class=
"mt-10px"
type=
"primary"
@
click=
"handleSendMessage"
>
发送
</el-button>
:rows=
"6"
</div>
placeholder=
"输入消息,Enter发送,Shift+Enter换行"
</div>
style=
"border-style: none"
type=
"textarea"
@
keyup
.
enter
.
prevent=
"handleSendMessage"
/>
</el-footer>
</el-footer>
</el-container>
</el-container>
<el-empty
v-else
description=
"请选择左侧的一个会话后开始"
/>
<el-container
v-else
class=
"kefu"
>
<el-main>
<el-empty
description=
"请选择左侧的一个会话后开始"
/>
</el-main>
</el-container>
</template>
</template>
<
script
lang=
"ts"
setup
>
<
script
lang=
"ts"
setup
>
...
@@ -262,7 +270,11 @@ const handleSendPicture = async (picUrl: string) => {
...
@@ -262,7 +270,11 @@ const handleSendPicture = async (picUrl: string) => {
}
}
/** 发送文本消息 */
/** 发送文本消息 */
const
handleSendMessage
=
async
()
=>
{
const
handleSendMessage
=
async
(
event
:
any
)
=>
{
// shift 不发送
if
(
event
.
shiftKey
)
{
return
}
// 1. 校验消息是否为空
// 1. 校验消息是否为空
if
(
isEmpty
(
unref
(
message
.
value
)))
{
if
(
isEmpty
(
unref
(
message
.
value
)))
{
messageTool
.
notifyWarning
(
'请输入消息后再发送哦!'
)
messageTool
.
notifyWarning
(
'请输入消息后再发送哦!'
)
...
@@ -357,14 +369,29 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
...
@@ -357,14 +369,29 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
.kefu
{
.kefu
{
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
{
&-title
{
border-bottom
:
#e4e0e0
solid
1
px
;
font-size
:
18
px
;
height
:
60px
;
font-weight
:
bold
;
line-height
:
60px
;
}
}
}
&
-content
{
&
-content
{
margin
:
0
;
padding
:
0
;
position
:
relative
;
position
:
relative
;
height
:
100%
;
width
:
100%
;
.newMessageTip
{
.newMessageTip
{
position
:
absolute
;
position
:
absolute
;
...
@@ -447,21 +474,36 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
...
@@ -447,21 +474,36 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
border-radius
:
12
rpx
;
border-radius
:
12
rpx
;
padding
:
8
rpx
16
rpx
;
padding
:
8
rpx
16
rpx
;
margin-bottom
:
16
rpx
;
margin-bottom
:
16
rpx
;
//
background-color
:
#e8e8e8
;
color
:
#999
;
color
:
#999
;
font-size
:
24
rpx
;
font-size
:
24
rpx
;
}
}
}
}
.kefu-footer
{
display
:
flex
;
flex-direction
:
column
;
height
:
auto
;
margin
:
0
;
padding
:
0
;
border-top
:
var
(
--el-border-color
)
solid
1px
;
.chat-tools
{
.chat-tools
{
width
:
100%
;
width
:
100%
;
border
:
var
(
--el-border-color
)
solid
1px
;
border-radius
:
10px
;
height
:
44px
;
height
:
44px
;
}
}
}
::v-deep
(
textarea
)
{
::v-deep
(
textarea
)
{
resize
:
none
;
resize
:
none
;
}
}
:deep
(
.el-input__wrapper
)
{
box-shadow
:
none
!important
;
border-radius
:
0
;
}
::v-deep
(
.el-textarea__inner
)
{
box-shadow
:
none
!important
;
}
}
}
</
style
>
</
style
>
src/views/mall/promotion/kefu/components/index.ts
View file @
34fe09e2
import
KeFuConversationList
from
'./KeFuConversationList.vue'
import
KeFuConversationList
from
'./KeFuConversationList.vue'
import
KeFuMessageList
from
'./KeFuMessageList.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 @
34fe09e2
<!-- 目录是不是叫 member 好点。然后这个组件是 MemberInfo,里面有浏览足迹 -->
<!-- 目录是不是叫 member 好点。然后这个组件是 MemberInfo,里面有浏览足迹 -->
<
template
>
<
template
>
<
div
v-show=
"!isEmpty(conversation)"
class=
"kefu"
>
<
el-container
class=
"kefu"
>
<
div
class=
"header-title h-60px flex justify-center items-center"
>
他的足迹
</div
>
<
el-header
class=
"kefu-header"
>
<el-tabs
v-model=
"activeName"
class=
"demo
-tabs"
@
tab-click=
"handleClick"
>
<el-tabs
v-model=
"activeName"
class=
"kefu
-tabs"
@
tab-click=
"handleClick"
>
<el-tab-pane
label=
"最近浏览"
name=
"a"
/>
<el-tab-pane
label=
"最近浏览"
name=
"a"
/>
<el-tab-pane
label=
"订单列表"
name=
"b"
/>
<el-tab-pane
label=
"订单列表"
name=
"b"
/>
</el-tabs>
</el-tabs>
<div>
</el-header>
<el-scrollbar
ref=
"scrollbarRef"
always
height=
"calc(115vh - 400px)"
@
scroll=
"handleScroll"
>
<el-main
class=
"kefu-content"
>
<div
v-show=
"!isEmpty(conversation)"
>
<el-scrollbar
ref=
"scrollbarRef"
always
@
scroll=
"handleScroll"
>
<!-- 最近浏览 -->
<!-- 最近浏览 -->
<ProductBrowsingHistory
v-if=
"activeName === 'a'"
ref=
"productBrowsingHistoryRef"
/>
<ProductBrowsingHistory
v-if=
"activeName === 'a'"
ref=
"productBrowsingHistoryRef"
/>
<!-- 订单列表 -->
<!-- 订单列表 -->
<OrderBrowsingHistory
v-if=
"activeName === 'b'"
ref=
"orderBrowsingHistoryRef"
/>
<OrderBrowsingHistory
v-if=
"activeName === 'b'"
ref=
"orderBrowsingHistoryRef"
/>
</el-scrollbar>
</el-scrollbar>
</div>
</div>
</div>
<el-empty
v-show=
"isEmpty(conversation)"
description=
"请选择左侧的一个会话后开始"
/>
<el-empty
v-show=
"isEmpty(conversation)"
description=
"请选择左侧的一个会话后开始"
/>
</el-main>
</el-container>
</
template
>
</
template
>
<
script
lang=
"ts"
setup
>
<
script
lang=
"ts"
setup
>
...
@@ -91,6 +94,41 @@ const handleScroll = debounce(() => {
...
@@ -91,6 +94,41 @@ const handleScroll = debounce(() => {
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
.kefu
{
margin
:
0
;
padding
:
0
;
height
:
100%
;
width
:
300px
!important
;
background-color
:
#fff
;
border-left
:
var
(
--el-border-color
)
solid
1px
;
&-header
{
background
:
#fbfbfb
;
box-shadow
:
0
0
0
0
#dcdfe6
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
&-title
{
font-size
:
18px
;
font-weight
:
bold
;
}
}
&
-content
{
margin
:
0
;
padding
:
0
;
position
:
relative
;
height
:
100%
;
width
:
100%
;
}
&
-tabs
{
height
:
100%
;
width
:
100%
;
}
}
.header-title
{
.header-title
{
border-bottom
:
#e4e0e0
solid
1px
;
border-bottom
:
#e4e0e0
solid
1px
;
}
}
...
...
src/views/mall/promotion/kefu/components/
history
/OrderBrowsingHistory.vue
→
src/views/mall/promotion/kefu/components/
member
/OrderBrowsingHistory.vue
View file @
34fe09e2
File moved
src/views/mall/promotion/kefu/components/
history
/ProductBrowsingHistory.vue
→
src/views/mall/promotion/kefu/components/
member
/ProductBrowsingHistory.vue
View file @
34fe09e2
File moved
src/views/mall/promotion/kefu/index.vue
View file @
34fe09e2
<
template
>
<
template
>
<el-
row
:gutter=
"10
"
>
<el-
container
class=
"kefu-layout
"
>
<!-- 会话列表 -->
<!-- 会话列表 -->
<el-col
:span=
"6"
>
<ContentWrap>
<KeFuConversationList
ref=
"keFuConversationRef"
@
change=
"handleChange"
/>
<KeFuConversationList
ref=
"keFuConversationRef"
@
change=
"handleChange"
/>
</ContentWrap>
</el-col>
<!-- 会话详情(选中会话的消息列表) -->
<!-- 会话详情(选中会话的消息列表) -->
<el-col
:span=
"12"
>
<ContentWrap>
<KeFuMessageList
ref=
"keFuChatBoxRef"
@
change=
"getConversationList"
/>
<KeFuMessageList
ref=
"keFuChatBoxRef"
@
change=
"getConversationList"
/>
</ContentWrap>
</el-col>
<!-- 会员足迹(选中会话的会员足迹) -->
<!-- 会员足迹(选中会话的会员足迹) -->
<el-col
:span=
"6"
>
<MemberInfo
ref=
"memberInfoRef"
/>
<ContentWrap>
</el-container>
<MemberBrowsingHistory
ref=
"memberBrowsingHistoryRef"
/>
</ContentWrap>
</el-col>
</el-row>
</
template
>
</
template
>
<
script
lang=
"ts"
setup
>
<
script
lang=
"ts"
setup
>
import
{
KeFuConversationList
,
KeFuMessageList
,
Member
BrowsingHistory
}
from
'./components'
import
{
KeFuConversationList
,
KeFuMessageList
,
Member
Info
}
from
'./components'
import
{
WebSocketMessageTypeConstants
}
from
'./components/tools/constants'
import
{
WebSocketMessageTypeConstants
}
from
'./components/tools/constants'
import
{
KeFuConversationRespVO
}
from
'@/api/mall/promotion/kefu/conversation'
import
{
KeFuConversationRespVO
}
from
'@/api/mall/promotion/kefu/conversation'
import
{
getRefreshToken
}
from
'@/utils/auth'
import
{
getRefreshToken
}
from
'@/utils/auth'
...
@@ -91,10 +79,10 @@ const getConversationList = () => {
...
@@ -91,10 +79,10 @@ const getConversationList = () => {
/** 加载指定会话的消息列表 */
/** 加载指定会话的消息列表 */
const
keFuChatBoxRef
=
ref
<
InstanceType
<
typeof
KeFuMessageList
>>
()
const
keFuChatBoxRef
=
ref
<
InstanceType
<
typeof
KeFuMessageList
>>
()
const
member
BrowsingHistoryRef
=
ref
<
InstanceType
<
typeof
MemberBrowsingHistory
>>
()
const
member
InfoRef
=
ref
<
InstanceType
<
typeof
MemberInfo
>>
()
const
handleChange
=
(
conversation
:
KeFuConversationRespVO
)
=>
{
const
handleChange
=
(
conversation
:
KeFuConversationRespVO
)
=>
{
keFuChatBoxRef
.
value
?.
getNewMessageList
(
conversation
)
keFuChatBoxRef
.
value
?.
getNewMessageList
(
conversation
)
member
BrowsingHistory
Ref
.
value
?.
initHistory
(
conversation
)
member
Info
Ref
.
value
?.
initHistory
(
conversation
)
}
}
/** 初始化 */
/** 初始化 */
...
@@ -112,9 +100,13 @@ onBeforeUnmount(() => {
...
@@ -112,9 +100,13 @@ onBeforeUnmount(() => {
</
script
>
</
script
>
<
style
lang=
"scss"
>
<
style
lang=
"scss"
>
.kefu
{
.kefu-layout
{
height
:
calc
(
100vh
-
165px
);
position
:
absolute
;
overflow
:
auto
;
/* 确保内容可滚动 */
flex
:
1
;
top
:
0
;
left
:
0
;
height
:
100%
;
width
:
100%
;
}
}
/* 定义滚动条样式 */
/* 定义滚动条样式 */
...
...
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