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
65509834
authored
Jul 10, 2024
by
YunaiV
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【代码评审】Mall:客服的会话列表
parent
00512641
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
52 additions
and
24 deletions
+52
-24
src/views/mall/promotion/kefu/components/KeFuConversationBox.vue
+26
-10
src/views/mall/promotion/kefu/components/tools/constants.ts
+1
-0
src/views/mall/promotion/kefu/index.vue
+25
-14
No files found.
src/views/mall/promotion/kefu/components/KeFuConversationBox.vue
View file @
65509834
<
template
>
<
template
>
<div
class=
"kefu"
>
<div
class=
"kefu"
>
<!-- TODO @puhui999:item => conversation 会不会更容易理解 -->
<div
<div
v-for=
"(item, index) in conversationList"
v-for=
"(item, index) in conversationList"
:key=
"item.id"
:key=
"item.id"
...
@@ -9,7 +10,9 @@
...
@@ -9,7 +10,9 @@
@contextmenu.prevent="rightClick($event as PointerEvent, item)"
@contextmenu.prevent="rightClick($event as PointerEvent, item)"
>
>
<div
class=
"flex justify-center items-center w-100%"
>
<div
class=
"flex justify-center items-center w-100%"
>
<!-- TODO style 换成 unocss -->
<div
class=
"flex justify-center items-center"
style=
"width: 50px; height: 50px"
>
<div
class=
"flex justify-center items-center"
style=
"width: 50px; height: 50px"
>
<!-- 头像 + 未读 -->
<el-badge
<el-badge
:hidden=
"item.adminUnreadMessageCount === 0"
:hidden=
"item.adminUnreadMessageCount === 0"
:max=
"99"
:max=
"99"
...
@@ -41,7 +44,8 @@
...
@@ -41,7 +44,8 @@
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 通过右击获取到的坐标定位 -->
<!-- 右键,进行操作(类似微信) -->
<ul
v-show=
"showRightMenu"
:style=
"rightMenuStyle"
class=
"right-menu-ul"
>
<ul
v-show=
"showRightMenu"
:style=
"rightMenuStyle"
class=
"right-menu-ul"
>
<li
<li
v-show=
"!selectedConversation.adminPinned"
v-show=
"!selectedConversation.adminPinned"
...
@@ -78,23 +82,30 @@ import { formatDate } from '@/utils/formatTime'
...
@@ -78,23 +82,30 @@ import { formatDate } from '@/utils/formatTime'
import
{
KeFuMessageContentTypeEnum
}
from
'./tools/constants'
import
{
KeFuMessageContentTypeEnum
}
from
'./tools/constants'
defineOptions
({
name
:
'KeFuConversationBox'
})
defineOptions
({
name
:
'KeFuConversationBox'
})
const
message
=
useMessage
()
const
message
=
useMessage
()
// 消息弹窗
const
{
replaceEmoji
}
=
useEmoji
()
const
{
replaceEmoji
}
=
useEmoji
()
const
activeConversationIndex
=
ref
(
-
1
)
// 选中的会话
const
conversationList
=
ref
<
KeFuConversationRespVO
[]
>
([])
// 会话列表
const
conversationList
=
ref
<
KeFuConversationRespVO
[]
>
([])
// 会话列表
const
activeConversationIndex
=
ref
(
-
1
)
// 选中的会话 index 位置 TODO @puhui999:这个可以改成 activeConversationId 么?因为一般是选中的对话编号
/** 加载会话列表 */
const
getConversationList
=
async
()
=>
{
const
getConversationList
=
async
()
=>
{
conversationList
.
value
=
await
KeFuConversationApi
.
getConversationList
()
conversationList
.
value
=
await
KeFuConversationApi
.
getConversationList
()
}
}
defineExpose
({
getConversationList
})
defineExpose
({
getConversationList
})
/** 打开右侧的消息列表 */
const
emits
=
defineEmits
<
{
const
emits
=
defineEmits
<
{
(
e
:
'change'
,
v
:
KeFuConversationRespVO
):
void
(
e
:
'change'
,
v
:
KeFuConversationRespVO
):
void
}
>
()
}
>
()
// 打开右侧消息
const
openRightMessage
=
(
item
:
KeFuConversationRespVO
,
index
:
number
)
=>
{
const
openRightMessage
=
(
item
:
KeFuConversationRespVO
,
index
:
number
)
=>
{
activeConversationIndex
.
value
=
index
activeConversationIndex
.
value
=
index
emits
(
'change'
,
item
)
emits
(
'change'
,
item
)
}
}
// 获得消息类型
// TODO @puhui999:这个,是不是改成 getConversationDisplayText,获取会话的展示文本。然后,把文本消息类型,也统一处理(包括上面的 replaceEmoji)。这样,更统一。
/** 获得消息类型 */
const
getContentType
=
computed
(()
=>
(
lastMessageContentType
:
number
)
=>
{
const
getContentType
=
computed
(()
=>
(
lastMessageContentType
:
number
)
=>
{
switch
(
lastMessageContentType
)
{
switch
(
lastMessageContentType
)
{
case
KeFuMessageContentTypeEnum
.
SYSTEM
:
case
KeFuMessageContentTypeEnum
.
SYSTEM
:
...
@@ -117,8 +128,9 @@ const getContentType = computed(() => (lastMessageContentType: number) => {
...
@@ -117,8 +128,9 @@ const getContentType = computed(() => (lastMessageContentType: number) => {
//======================= 右键菜单 =======================
//======================= 右键菜单 =======================
const
showRightMenu
=
ref
(
false
)
// 显示右键菜单
const
showRightMenu
=
ref
(
false
)
// 显示右键菜单
const
rightMenuStyle
=
ref
<
any
>
({})
// 右键菜单 Style
const
rightMenuStyle
=
ref
<
any
>
({})
// 右键菜单 Style
const
selectedConversation
=
ref
<
KeFuConversationRespVO
>
({}
as
KeFuConversationRespVO
)
// 右键选中的会话对象
const
selectedConversation
=
ref
<
KeFuConversationRespVO
>
({}
as
KeFuConversationRespVO
)
// 右键选中的会话对象 TODO puhui999:这个是不是叫 rightClickConversation 会好点。因为 selected 容易和选中的对话,定义上有点重叠
// 右键菜单
/** 打开右键菜单 */
const
rightClick
=
(
mouseEvent
:
PointerEvent
,
item
:
KeFuConversationRespVO
)
=>
{
const
rightClick
=
(
mouseEvent
:
PointerEvent
,
item
:
KeFuConversationRespVO
)
=>
{
selectedConversation
.
value
=
item
selectedConversation
.
value
=
item
// 显示右键菜单
// 显示右键菜单
...
@@ -128,11 +140,12 @@ const rightClick = (mouseEvent: PointerEvent, item: KeFuConversationRespVO) => {
...
@@ -128,11 +140,12 @@ const rightClick = (mouseEvent: PointerEvent, item: KeFuConversationRespVO) => {
left
:
mouseEvent
.
clientX
-
80
+
'px'
left
:
mouseEvent
.
clientX
-
80
+
'px'
}
}
}
}
/
/ 关闭菜单
/
** 关闭右键菜单 */
const
closeRightMenu
=
()
=>
{
const
closeRightMenu
=
()
=>
{
showRightMenu
.
value
=
false
showRightMenu
.
value
=
false
}
}
// 置顶会话
/** 置顶会话 */
const
updateConversationPinned
=
async
(
adminPinned
:
boolean
)
=>
{
const
updateConversationPinned
=
async
(
adminPinned
:
boolean
)
=>
{
// 1. 会话置顶/取消置顶
// 1. 会话置顶/取消置顶
await
KeFuConversationApi
.
updateConversationPinned
({
await
KeFuConversationApi
.
updateConversationPinned
({
...
@@ -144,7 +157,8 @@ const updateConversationPinned = async (adminPinned: boolean) => {
...
@@ -144,7 +157,8 @@ const updateConversationPinned = async (adminPinned: boolean) => {
closeRightMenu
()
closeRightMenu
()
await
getConversationList
()
await
getConversationList
()
}
}
// 删除会话
/** 删除会话 */
const
deleteConversation
=
async
()
=>
{
const
deleteConversation
=
async
()
=>
{
// 1. 删除会话
// 1. 删除会话
await
message
.
confirm
(
'您确定要删除该会话吗?'
)
await
message
.
confirm
(
'您确定要删除该会话吗?'
)
...
@@ -153,6 +167,8 @@ const deleteConversation = async () => {
...
@@ -153,6 +167,8 @@ const deleteConversation = async () => {
closeRightMenu
()
closeRightMenu
()
await
getConversationList
()
await
getConversationList
()
}
}
/** 监听右键菜单的显示状态,添加点击事件监听器 */
watch
(
showRightMenu
,
(
val
)
=>
{
watch
(
showRightMenu
,
(
val
)
=>
{
if
(
val
)
{
if
(
val
)
{
document
.
body
.
addEventListener
(
'click'
,
closeRightMenu
)
document
.
body
.
addEventListener
(
'click'
,
closeRightMenu
)
...
...
src/views/mall/promotion/kefu/components/tools/constants.ts
View file @
65509834
...
@@ -9,6 +9,7 @@ export const KeFuMessageContentTypeEnum = {
...
@@ -9,6 +9,7 @@ export const KeFuMessageContentTypeEnum = {
PRODUCT
:
10
,
// 商品消息
PRODUCT
:
10
,
// 商品消息
ORDER
:
11
// 订单消息"
ORDER
:
11
// 订单消息"
}
}
// Promotion 的 WebSocket 消息类型枚举类
// Promotion 的 WebSocket 消息类型枚举类
export
const
WebSocketMessageTypeConstants
=
{
export
const
WebSocketMessageTypeConstants
=
{
KEFU_MESSAGE_TYPE
:
'kefu_message_type'
,
// 客服消息类型
KEFU_MESSAGE_TYPE
:
'kefu_message_type'
,
// 客服消息类型
...
...
src/views/mall/promotion/kefu/index.vue
View file @
65509834
<
template
>
<
template
>
<el-row
:gutter=
"10"
>
<el-row
:gutter=
"10"
>
<!-- TODO @puhui999:KeFuConversationBox => KeFuConversationList ;KeFuChatBox => KeFuMessageList -->
<!-- 会话列表 -->
<el-col
:span=
"8"
>
<el-col
:span=
"8"
>
<ContentWrap>
<ContentWrap>
<KeFuConversationBox
ref=
"keFuConversationRef"
@
change=
"handleChange"
/>
<KeFuConversationBox
ref=
"keFuConversationRef"
@
change=
"handleChange"
/>
</ContentWrap>
</ContentWrap>
</el-col>
</el-col>
<!-- 会话详情(选中会话的消息列表) -->
<el-col
:span=
"16"
>
<el-col
:span=
"16"
>
<ContentWrap>
<ContentWrap>
<KeFuChatBox
ref=
"keFuChatBoxRef"
@
change=
"getConversationList"
/>
<KeFuChatBox
ref=
"keFuChatBoxRef"
@
change=
"getConversationList"
/>
...
@@ -21,15 +24,10 @@ import { getAccessToken } from '@/utils/auth'
...
@@ -21,15 +24,10 @@ import { getAccessToken } from '@/utils/auth'
import
{
useWebSocket
}
from
'@vueuse/core'
import
{
useWebSocket
}
from
'@vueuse/core'
defineOptions
({
name
:
'KeFu'
})
defineOptions
({
name
:
'KeFu'
})
const
message
=
useMessage
()
// 加载消息
const
message
=
useMessage
()
// 消息弹窗
const
keFuChatBoxRef
=
ref
<
InstanceType
<
typeof
KeFuChatBox
>>
()
const
handleChange
=
(
conversation
:
KeFuConversationRespVO
)
=>
{
keFuChatBoxRef
.
value
?.
getMessageList
(
conversation
)
}
//
======================= 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'
)
+
'?token='
+
'?token='
+
...
@@ -38,9 +36,11 @@ const server = ref(
...
@@ -38,9 +36,11 @@ const server = ref(
/** 发起 WebSocket 连接 */
/** 发起 WebSocket 连接 */
const
{
data
,
close
,
open
}
=
useWebSocket
(
server
.
value
,
{
const
{
data
,
close
,
open
}
=
useWebSocket
(
server
.
value
,
{
autoReconnect
:
false
,
autoReconnect
:
false
,
// TODO @puhui999:重连要加下
heartbeat
:
true
heartbeat
:
true
})
})
/** 监听 WebSocket 数据 */
watchEffect
(()
=>
{
watchEffect
(()
=>
{
if
(
!
data
.
value
)
{
if
(
!
data
.
value
)
{
return
return
...
@@ -75,17 +75,28 @@ watchEffect(() => {
...
@@ -75,17 +75,28 @@ watchEffect(() => {
console
.
error
(
error
)
console
.
error
(
error
)
}
}
})
})
//======================= websocket end=======================
// ======================= WebSocket end =======================
// 加载会话列表
/** 加载会话列表 */
const
keFuConversationRef
=
ref
<
InstanceType
<
typeof
KeFuConversationBox
>>
()
const
keFuConversationRef
=
ref
<
InstanceType
<
typeof
KeFuConversationBox
>>
()
const
getConversationList
=
()
=>
{
const
getConversationList
=
()
=>
{
keFuConversationRef
.
value
?.
getConversationList
()
keFuConversationRef
.
value
?.
getConversationList
()
}
}
/** 加载指定会话的消息列表 */
const
keFuChatBoxRef
=
ref
<
InstanceType
<
typeof
KeFuChatBox
>>
()
const
handleChange
=
(
conversation
:
KeFuConversationRespVO
)
=>
{
keFuChatBoxRef
.
value
?.
getMessageList
(
conversation
)
}
/** 初始化 */
onMounted
(()
=>
{
onMounted
(()
=>
{
getConversationList
()
getConversationList
()
// 打开 websocket 连接
// 打开 websocket 连接
open
()
open
()
})
})
/** 销毁 */
onBeforeUnmount
(()
=>
{
onBeforeUnmount
(()
=>
{
// 关闭 websocket 连接
// 关闭 websocket 连接
close
()
close
()
...
@@ -104,17 +115,17 @@ onBeforeUnmount(() => {
...
@@ -104,17 +115,17 @@ onBeforeUnmount(() => {
height
:
6px
;
height
:
6px
;
}
}
/*
定义滚动条轨道 内阴影+圆角
*/
/*
定义滚动条轨道 内阴影+圆角
*/
::-webkit-scrollbar-track
{
::-webkit-scrollbar-track
{
box-shadow
:
inset
0
0
0
px
rgba
(
240
,
240
,
240
,
0.5
);
box-shadow
:
inset
0
0
0
rgba
(
240
,
240
,
240
,
0.5
);
border-radius
:
10px
;
border-radius
:
10px
;
background-color
:
#fff
;
background-color
:
#fff
;
}
}
/*
定义滑块 内阴影+圆角
*/
/*
定义滑块 内阴影+圆角
*/
::-webkit-scrollbar-thumb
{
::-webkit-scrollbar-thumb
{
border-radius
:
10px
;
border-radius
:
10px
;
box-shadow
:
inset
0
0
0
px
rgba
(
240
,
240
,
240
,
0.5
);
box-shadow
:
inset
0
0
0
rgba
(
240
,
240
,
240
,
0.5
);
background-color
:
rgba
(
240
,
240
,
240
,
0.5
);
background-color
:
rgba
(
240
,
240
,
240
,
0.5
);
}
}
</
style
>
</
style
>
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