Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Q
qk_admin
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
quanku
qk_admin
Commits
18cb1194
Commit
18cb1194
authored
Nov 07, 2023
by
yaya
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
添加消息发送功能
parent
618cc34e
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
219 additions
and
34 deletions
+219
-34
.idea/misc.xml
.idea/misc.xml
+6
-0
src/api/business/chat.js
src/api/business/chat.js
+8
-0
src/views/customerService/chatDetail.vue
src/views/customerService/chatDetail.vue
+189
-33
src/views/customerService/components/chatMessageItem.vue
src/views/customerService/components/chatMessageItem.vue
+16
-1
No files found.
.idea/misc.xml
0 → 100644
View file @
18cb1194
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<component
name=
"WebPackConfiguration"
>
<option
name=
"mode"
value=
"DISABLED"
/>
</component>
</project>
\ No newline at end of file
src/api/business/chat.js
View file @
18cb1194
...
...
@@ -15,3 +15,11 @@ export function getUserList(params) {
})
}
export
function
sendChatMsg
(
params
)
{
return
request
({
url
:
'
/sysIM/sendChatMsg
'
,
method
:
'
post
'
,
data
:
params
})
}
src/views/customerService/chatDetail.vue
View file @
18cb1194
...
...
@@ -8,13 +8,13 @@
<el-container
v-loading=
"loading"
style=
"width: 100%;height: 100%; border: 1px solid #eee"
>
<el-aside
style=
"width: 25%;background-color: white;padding: 0"
>
<el-tabs
style=
"width: 100%; padding: 10px 10px 0"
@
tab-click=
"handleClick"
>
<el-tab-pane
v-for=
"item in c
hat
List"
:key=
"item.code"
:label=
"item.name"
/>
<el-tab-pane
v-for=
"item in c
ustomer
List"
:key=
"item.code"
:label=
"item.name"
/>
</el-tabs>
<div
class=
"conversation"
>
<div
v-for=
"item in conversationList"
:key=
"item.conversationID"
:class=
"
{'conversation-item-active': currentC
hat
Item.conversationID === item.conversationID}"
:class=
"
{'conversation-item-active': currentC
onversation
Item.conversationID === item.conversationID}"
class="conversation-item flex f-ai-c"
@click="handleSelectChat(item)">
<div
class=
"conversation-item-image"
>
...
...
@@ -34,23 +34,33 @@
<el-main
style=
"background-color: rgb(238, 241, 246); padding: 0; border: 1px solid #eee"
>
<div
class=
"chat-message"
>
<div
class=
"chat-message-top flex f-jc-sb"
>
<div
class=
"chat-message-top__title"
>
账号昵称
</div>
<el-button
type=
"text"
>
人工介入
</el-button>
<div
class=
"chat-message-top__title"
>
{{
currentConversationItem
&&
currentConversationItem
.
nick
}}
</div>
<el-button
type=
"text"
@
click=
"handleService"
>
{{
serviceTip
}}
</el-button>
</div>
<div
class=
"chat-message-list"
style=
"overflow:auto"
>
<div
v-if=
"!isCompleted && currentConversationItem.conversationID"
class=
"chat-message-list__more"
@
click=
"handleGetHistory(false)"
>
点击获取更多历史消息
</div>
<ul
class=
"chat-message-list__content"
infinite-scroll-disabled=
"disabled"
style=
"margin: 0; padding: 0"
>
<div
v-for=
"messageItem in messageList"
:key=
"messageItem.id"
class=
"chat-message-list__content__item"
>
<div
v-for=
"(messageItem, index) in messageList"
:key=
"messageItem.id"
:class=
"`chat-message-list__content__item_$
{index}`"
class="chat-message-list__content__item">
<ChatMessageItem
:chat-message-item=
"messageItem"
/>
</div>
</ul>
<div
class=
"hash"
/>
</div>
<div
class=
"chat-message-input"
>
<el-input
v-model=
"messageContent"
:rows=
"4"
type=
"textarea"
placeholder=
"请输入聊天内容"
style=
"border: none"
/>
<el-input
v-model=
"messageContent"
:rows=
"4"
type=
"textarea"
placeholder=
"请输入聊天内容"
style=
"border: none"
/>
<div
class=
"chat-message-input__send"
>
<el-button
:disabled=
"sendDisabled"
type=
"primary"
>
发送
</el-button>
<el-button
:disabled=
"sendDisabled"
type=
"primary"
@
click=
"sendMessage"
>
发送
</el-button>
</div>
</div>
</div>
...
...
@@ -67,54 +77,98 @@
<
script
>
import
{
getUserSig
,
getUserList
}
from
'
@/api/business/chat
'
import
{
getUserSig
,
getUserList
,
sendChatMsg
}
from
'
@/api/business/chat
'
import
chat
from
'
@/utils/chat
'
import
{
formatTime
}
from
'
@/utils/index
'
import
TencentCloudChat
from
'
@tencentcloud/chat
'
import
ChatMessageItem
from
'
./components/chatMessageItem
'
import
store
from
'
../../store
'
export
default
{
name
:
'
ChatDetail
'
,
components
:
{
ChatMessageItem
},
data
()
{
return
{
cu
rrentChatItem
:
{},
// 当前聊天
c
hatList
:
[],
cu
stomerList
:
[],
// 客服列表
c
urrentCustomerIndex
:
-
1
,
// 当前客服
loading
:
true
,
conversationList
:
[],
messageContent
:
''
,
count
:
10
,
currentChatIndex
:
-
1
,
// 当前聊天
nextReqMessageID
:
''
,
// 历史消息下一页
isCompleted
:
false
,
envText
:
'
cjxc-applet-dev_
'
,
messageList
:
[]
// 历史消息
conversationList
:
[],
// 会话列表
currentConversationItem
:
{},
// 当前聊天
messageList
:
[],
// 历史消息
isRealPersonal
:
false
,
// 是否人工介入
userId
:
''
}
},
computed
:
{
sendDisabled
()
{
return
!
this
.
messageContent
||
this
.
messageContent
.
length
===
0
return
!
this
.
messageContent
||
this
.
messageContent
.
length
===
0
||
!
this
.
isRealPersonal
||
!
this
.
currentConversationItem
.
conversationID
},
serviceTip
()
{
return
this
.
isRealPersonal
?
'
结束人工
'
:
'
人工介入
'
}
},
mounted
()
{
},
created
()
{
chat
.
on
(
TencentCloudChat
.
EVENT
.
SDK_READY
,
this
.
onSdkReady
)
this
.
handleGetUserInfo
()
this
.
handleGetChatList
()
},
methods
:
{
handleGetUserInfo
()
{
store
.
dispatch
(
'
GetUserInfo
'
).
then
(
res
=>
{
// 拉取user_info
this
.
userId
=
res
.
data
.
data
.
id
// note: perms must be a array! such as: ['GET /aaa','POST /bbb']
}).
catch
((
err
)
=>
{
console
.
log
(
'
====userInfo=
'
,
err
)
})
},
handleSelect
(
key
,
keyPath
)
{
console
.
log
(
key
,
keyPath
)
},
/**
* 是否人工介入
* */
handleService
()
{
if
(
this
.
isRealPersonal
)
{
this
.
isRealPersonal
=
false
}
else
{
this
.
isRealPersonal
=
true
}
const
object
=
{
messageContent
:
this
.
isRealPersonal
?
'
人工接管聊天
'
:
'
退出人工聊天
'
,
msgType
:
this
.
isRealPersonal
?
3
:
4
}
this
.
messageList
.
push
(
object
)
this
.
scrollToBottom
(
true
)
},
/**
* 选择客服
**/
handleClick
(
tab
,
event
)
{
console
.
log
(
'
====tab=
'
,
tab
.
index
,
event
)
if
(
this
.
currentC
hat
Index
===
tab
.
index
)
{
if
(
this
.
currentC
ustomer
Index
===
tab
.
index
)
{
return
}
this
.
currentChatIndex
=
tab
.
index
this
.
handleClear
()
this
.
currentCustomerIndex
=
tab
.
index
this
.
handleBeforeLogin
()
},
/**
* 切换客服清空数据
* */
handleClear
()
{
this
.
currentConversationItem
=
{}
this
.
conversationList
=
[]
this
.
isRealPersonal
=
false
this
.
nextReqMessageID
=
''
this
.
messageList
=
[]
this
.
messageContent
=
''
},
/**
* 获取客服列表
**/
...
...
@@ -122,9 +176,9 @@ export default {
const
params
=
{}
getUserList
(
params
).
then
(
response
=>
{
if
(
response
&&
response
.
data
&&
response
.
data
.
data
)
{
this
.
c
hat
List
=
response
.
data
.
data
this
.
c
ustomer
List
=
response
.
data
.
data
}
if
(
this
.
c
hat
List
.
length
>
0
)
{
if
(
this
.
c
ustomer
List
.
length
>
0
)
{
this
.
handleBeforeLogin
()
}
console
.
log
(
'
===list ===
'
,
response
.
data
.
data
)
...
...
@@ -135,19 +189,23 @@ export default {
})
})
},
/**
* 切换客服
* */
handleBeforeLogin
()
{
this
.
showLoading
()
if
(
this
.
currentC
hat
Index
===
-
1
)
{
const
chatItem
=
this
.
c
hat
List
[
0
]
this
.
currentC
hat
Index
=
0
if
(
this
.
currentC
ustomer
Index
===
-
1
)
{
const
chatItem
=
this
.
c
ustomer
List
[
0
]
this
.
currentC
ustomer
Index
=
0
this
.
handleGetUserSign
(
chatItem
.
code
)
}
else
{
const
promise
=
chat
.
logout
()
promise
.
then
((
imResponse
)
=>
{
// console.log('==登出成功=', imResponse.data, this.c
hat
List) // 登出成功
const
chatItem
=
this
.
c
hatList
[
this
.
currentChat
Index
]
// console.log('==登出成功=', imResponse.data, this.c
onversation
List) // 登出成功
const
chatItem
=
this
.
c
ustomerList
[
this
.
currentCustomer
Index
]
this
.
handleGetUserSign
(
chatItem
.
code
)
}).
catch
(
function
(
imError
)
{
this
.
closeLoading
()
console
.
warn
(
'
====登出失败:
'
,
imError
)
// 登录失败的相关信息
})
}
...
...
@@ -175,12 +233,11 @@ export default {
*/
handleImLoin
(
userId
,
userSign
)
{
console
.
log
(
'
====用户信息===
'
,
userId
,
userSign
)
// 登录成功
this
.
closeLoading
()
const
promise
=
chat
.
login
({
userID
:
this
.
envText
+
userId
,
userSig
:
userSign
})
promise
.
then
((
imResponse
)
=>
{
this
.
closeLoading
()
console
.
log
(
'
====登录成功===
'
,
imResponse
.
data
)
// 登录成功
chat
.
on
(
TencentCloudChat
.
EVENT
.
CONVERSATION_LIST_UPDATED
,
this
.
onConversationListUpdated
)
chat
.
on
(
TencentCloudChat
.
EVENT
.
MESSAGE_RECEIVED
,
this
.
onMessageReceived
)
this
.
handleGetConversationList
()
...
...
@@ -195,14 +252,36 @@ export default {
},
onSdkReady
(
event
)
{
console
.
log
(
'
====SDK ready===
'
,
event
)
// 登录成功
this
.
handleGetConversationList
()
},
/**
* 接受消息
* */
onMessageReceived
(
event
)
{
const
receivedList
=
event
.
data
receivedList
.
forEach
((
message
)
=>
{
if
(
message
.
type
===
TencentCloudChat
.
TYPES
.
MSG_TEXT
)
{
console
.
log
(
'
=====新消息=
'
,
message
)
if
(
message
.
conversationID
!==
this
.
currentConversationItem
.
conversationID
)
{
return
}
const
userID
=
this
.
customerList
[
this
.
currentCustomerIndex
].
code
if
(
this
.
isRealPersonal
&&
message
.
from
===
this
.
envText
+
userID
)
{
return
}
const
object
=
{
id
:
message
.
ID
,
messageContent
:
message
.
payload
.
text
,
fromUserId
:
message
.
from
,
toAccount
:
message
.
to
,
avatar
:
message
.
avatar
,
msgType
:
this
.
isRealPersonal
&&
message
.
from
===
this
.
envText
+
userID
?
1
:
2
}
this
.
messageList
.
push
(
object
)
this
.
scrollToBottom
(
true
)
// 文本消息 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.TextPayload
}
/* else if (message.type === TencentCloudChat.TYPES.MSG_IMAGE) {
// 图片消息 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.ImagePayload
...
...
@@ -226,6 +305,9 @@ export default {
})
},
/**
* 获取会话列表
* */
handleGetConversationList
()
{
const
promise
=
chat
.
getConversationList
()
promise
.
then
((
imResponse
)
=>
{
...
...
@@ -270,18 +352,22 @@ export default {
* */
handleSelectChat
(
item
)
{
this
.
messageList
=
[]
if
(
this
.
currentC
hatItem
!==
item
)
{
this
.
currentC
hat
Item
=
item
if
(
this
.
currentC
onversationItem
&&
this
.
currentConversationItem
.
conversationID
!==
item
.
conversationID
)
{
this
.
currentC
onversation
Item
=
item
this
.
nextReqMessageID
=
''
this
.
isRealPersonal
=
false
this
.
handleGetHistory
()
}
},
handleGetHistory
()
{
const
userID
=
this
.
chatList
[
this
.
currentChatIndex
].
code
if
(
this
.
currentChatItem
)
{
/**
* 获取历史消息
* */
handleGetHistory
(
scrollEnd
=
true
)
{
const
userID
=
this
.
customerList
[
this
.
currentCustomerIndex
].
code
if
(
this
.
currentConversationItem
)
{
const
promise
=
chat
.
getMessageList
({
conversationID
:
this
.
currentC
hat
Item
.
conversationID
,
conversationID
:
this
.
currentC
onversation
Item
.
conversationID
,
nextReqMessageID
:
this
.
nextReqMessageID
})
promise
.
then
((
imResponse
)
=>
{
...
...
@@ -308,12 +394,61 @@ export default {
}
console
.
log
(
'
====messageList
'
,
this
.
messageList
,
topList
)
this
.
messageList
=
topList
.
concat
(
this
.
messageList
)
this
.
scrollToBottom
(
scrollEnd
,
topList
.
length
)
console
.
log
(
'
====历史消息
'
,
this
.
messageList
)
}).
catch
((
err
)
=>
{
console
.
log
(
'
====err--历史消息
'
,
err
)
})
}
},
scrollToBottom
(
scrollToEnd
,
listLength
)
{
if
(
scrollToEnd
)
{
setTimeout
(()
=>
{
const
hashElements
=
document
.
getElementsByClassName
(
'
hash
'
)
if
(
hashElements
&&
hashElements
.
length
>
0
)
{
hashElements
[
0
].
scrollIntoView
(
true
)
}
},
250
)
// 延长触发时间,避免在App.vue的afterEnter之前执行
}
else
{
setTimeout
(()
=>
{
const
hashElements
=
document
.
getElementsByClassName
(
`chat-message-list__content__item_
${
listLength
-
1
}
`
)
if
(
hashElements
&&
hashElements
.
length
>
0
)
{
hashElements
[
0
].
scrollIntoView
(
true
)
}
},
250
)
}
},
/**
* 发送消息
* */
sendMessage
()
{
const
params
=
{
fromAccount
:
this
.
customerList
[
this
.
currentCustomerIndex
].
code
,
toAccount
:
this
.
currentConversationItem
.
fromAccount
,
isRealPersonAnswer
:
1
,
msgType
:
1
,
realPersonId
:
this
.
userId
,
msgContent
:
this
.
messageContent
}
sendChatMsg
(
params
).
then
(
response
=>
{
const
object
=
{
messageContent
:
this
.
messageContent
,
fromUserId
:
this
.
customerList
[
this
.
currentCustomerIndex
].
code
,
toAccount
:
this
.
currentConversationItem
.
conversationID
,
avatar
:
this
.
customerList
[
this
.
currentCustomerIndex
].
avatar
,
msgType
:
1
}
this
.
messageList
.
push
(
object
)
this
.
messageContent
=
''
this
.
scrollToBottom
(
true
)
}).
catch
(
response
=>
{
this
.
$notify
.
error
({
title
:
'
失败
'
,
message
:
response
.
data
.
errmsg
||
'
请求失败
'
})
})
},
/**
* 打开加载
...
...
@@ -335,9 +470,11 @@ export default {
/deep/ .main {
padding: 0;
}
/deep/ .el-textarea__inner {
border: none;
}
/deep/ .ul {
padding-inline-start: 0;
}
...
...
@@ -387,6 +524,7 @@ export default {
.conversation-item-info {
width: 100%;
.conversation-item-info__nick {
height: 20px;
font-size: 13px;
...
...
@@ -398,6 +536,7 @@ export default {
.conversation-item-info__message {
width: 100%;
.conversation-item-info__message__profile {
flex: 1;
height: 16px;
...
...
@@ -426,6 +565,7 @@ export default {
}
}
}
.conversation-item-active {
background: rgb(238, 241, 246);
}
...
...
@@ -434,28 +574,44 @@ export default {
.chat-message {
display: flex;
flex-direction: column;
height:100%;
height:
100%;
.chat-message-top {
background: white;
padding: 8px;
}
.chat-message-list {
width: 100%;
flex: 1;
padding-top: 10px;
.chat-message-list__more {
width: 100%;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #989CA8;
line-height: 40px;
text-align: center;
}
.chat-message-list__content {
width: 100%;
.chat-message-list__content__item {
width: 100%;
margin-bottom: 20px;
}
}
}
.chat-message-input {
border: 1px solid #DCDFE6;
border-radius: 4px;
background-color: #FFFFFF;
padding: 12px 0;
.chat-message-input__send {
text-align: end;
margin: 12px 12px 0 0;
...
...
src/views/customerService/components/chatMessageItem.vue
View file @
18cb1194
<
template
>
<div
class=
"message_components"
>
<!--自己发送的消息-->
<div
v-if=
"chatMessageItem.msgType === 1"
class=
"message-self"
>
<div
class=
"message-self_content"
>
{{
chatMessageItem
.
messageContent
}}
</div>
<img
:src=
"chatMessageItem.avatar"
class=
"message-self__avatar"
>
</div>
<!--其他人发送的消息-->
<div
v-if=
"chatMessageItem.msgType === 2"
class=
"message-other"
>
<img
:src=
"chatMessageItem.avatar"
class=
"message-other__avatar"
>
<div
class=
"message-other_content"
>
{{
chatMessageItem
.
messageContent
}}
</div>
</div>
<!--人工接管聊天-->
<div
v-if=
"chatMessageItem.msgType === 3"
class=
"message-enter"
>
{{
chatMessageItem
.
messageContent
}}
</div>
<!--人工退出聊天-->
<div
v-if=
"chatMessageItem.msgType === 4"
class=
"message-enter"
>
{{
chatMessageItem
.
messageContent
}}
</div>
</div>
</
template
>
...
...
@@ -60,7 +66,7 @@ export default {
width: fit-content;
max-width: 60%;
background: #FFFFFF;
border-radius: 11px 11px
0px 11px
;
border-radius: 11px 11px
11px 0
;
padding: 10px 12px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
...
...
@@ -74,6 +80,15 @@ export default {
margin: 0 10px;
}
}
.message-enter {
width: 100%;
text-align: center;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #989CA8;
line-height: 20px;
}
}
</
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