|
@@ -1,6 +1,6 @@
|
|
|
<template>
|
|
|
<el-container class="message--layout" :class="{'mini-layout': getMiniStatus}">
|
|
|
- <AsideView v-if="!getMiniStatus"></AsideView>
|
|
|
+ <AsideView v-if="!getMiniStatus" :badge="unReadCount" :logo="login.headImg"></AsideView>
|
|
|
<el-main class="message--main">
|
|
|
<el-container v-if="!getMiniStatus" class="message--list-group">
|
|
|
<div class="j-container">
|
|
@@ -10,10 +10,11 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-container>
|
|
|
- <el-container class="message--chat-group">
|
|
|
+ <el-container v-if="showMsgBox" class="message--chat-group">
|
|
|
<ChatView
|
|
|
- :info="service"
|
|
|
- :actionList="actionList"
|
|
|
+ :info="other"
|
|
|
+ :actionList="getActionList"
|
|
|
+ :isLink="isTurnPeople"
|
|
|
:msgList="getMsgList"
|
|
|
:listInfo="listInfo"
|
|
|
:session-id="other.sessionId"
|
|
@@ -21,10 +22,14 @@
|
|
|
@load-more="onLoadMore"
|
|
|
@upload-image="onUploadImage"
|
|
|
@upload-attach="onUploadAttach"
|
|
|
+ @turn="onTurnPeople"
|
|
|
ref="userSide"
|
|
|
>
|
|
|
</ChatView>
|
|
|
</el-container>
|
|
|
+ <el-container class="message--empty-group" v-else>
|
|
|
+ <div class="empty-message-img"></div>
|
|
|
+ </el-container>
|
|
|
</el-main>
|
|
|
</el-container>
|
|
|
</template>
|
|
@@ -34,9 +39,9 @@ import { Container, Main, Avatar, Badge } from 'element-ui'
|
|
|
import ChatView from '@/views/ChatView'
|
|
|
import AsideView from '@/components/aside/'
|
|
|
import MessageList from '@/components/MessageList'
|
|
|
-import { mapState, mapMutations, mapActions } from 'vuex'
|
|
|
-import { robotInfo, robotReply, createChatSession, findMessage, getWebSocketNode } from '@/api/modules/'
|
|
|
-import { dateFormatter, isJSON, isHyperlink } from '@/utils/'
|
|
|
+import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
|
|
|
+import { robotInfo, createChatSession, findMessage, getUserList, getMessageCount, getUserInfo } from '@/api/modules/'
|
|
|
+import { dateFormatter, isJSON, isHyperlink, momentTime } from '@/utils/'
|
|
|
|
|
|
export default {
|
|
|
name: 'CustomerServiceView',
|
|
@@ -52,6 +57,7 @@ export default {
|
|
|
data () {
|
|
|
return {
|
|
|
mini: false,
|
|
|
+ unReadCount: 0,
|
|
|
// 所有会话
|
|
|
userList: [],
|
|
|
// 客服信息
|
|
@@ -59,12 +65,11 @@ export default {
|
|
|
name: '剑鱼标讯',
|
|
|
headImg: 'https://www.jianyu360.cn/common-module/public/image/auto.png',
|
|
|
entId: '',
|
|
|
- userType: 1
|
|
|
+ userType: 1,
|
|
|
+ chatStatus: 0
|
|
|
},
|
|
|
- // 聊天框操作配置项
|
|
|
- actionList: ['image', 'attach', 'rate'],
|
|
|
listInfo: {
|
|
|
- pageSize: 10,
|
|
|
+ pageSize: 20,
|
|
|
pageIndex: 1,
|
|
|
loading: false,
|
|
|
finished: false,
|
|
@@ -74,22 +79,48 @@ export default {
|
|
|
msgList: [],
|
|
|
// 当前登录用户信息
|
|
|
login: {
|
|
|
+ nickName: '',
|
|
|
+ headImg: '',
|
|
|
userType: 2 // 当前用户身份 1:客服, 2: 用户
|
|
|
},
|
|
|
// 会话对方信息
|
|
|
other: {
|
|
|
userType: null, // 会话对方身份(客服or用户)
|
|
|
- userId: 1, // 会话对方的userId
|
|
|
- sessionId: null // 会话id
|
|
|
- }
|
|
|
+ userId: null, // 会话对方的userId
|
|
|
+ sessionId: null, // 会话id
|
|
|
+ entId: null,
|
|
|
+ name: '',
|
|
|
+ info: {},
|
|
|
+ itemType: null // 会话对方的类型 1:站内信消息 2:点对点消息 3:群消息 4:机器人消息 5:客服消息 6系统信息
|
|
|
+ },
|
|
|
+ showMsgBox: false
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
...mapState('message', ['userSessionBadge']),
|
|
|
+ ...mapGetters('webSocket', ['socketMsg']),
|
|
|
+ // 聊天框操作配置项
|
|
|
+ getActionList () {
|
|
|
+ if (this.other.userType === 1) {
|
|
|
+ return ['image', 'rate']
|
|
|
+ } else {
|
|
|
+ return ['image']
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isTurnPeople () {
|
|
|
+ return this.other.userType === 1
|
|
|
+ },
|
|
|
getMiniStatus () {
|
|
|
- console.log(this.mini)
|
|
|
return Boolean(this.mini)
|
|
|
},
|
|
|
+ getItemType () {
|
|
|
+ const type = this.other.userType
|
|
|
+ if (type === 2) {
|
|
|
+ return 2
|
|
|
+ } else {
|
|
|
+ return 4 || 5
|
|
|
+ }
|
|
|
+ },
|
|
|
getMsgList () {
|
|
|
const list = this.msgList
|
|
|
list.forEach(v => {
|
|
@@ -103,31 +134,22 @@ export default {
|
|
|
return result
|
|
|
}
|
|
|
},
|
|
|
+ watch: {
|
|
|
+ socketMsg (val) {
|
|
|
+ console.log(val, 'val')
|
|
|
+ // 监听接收到的消息,进行处理
|
|
|
+ this.todoMessage(val)
|
|
|
+ }
|
|
|
+ },
|
|
|
created () {
|
|
|
- this.mini = Number(this.$route.query.mini) || 0
|
|
|
- this.service.entId = String(this.$route.query.entId)
|
|
|
- this.other.userId = String(this.$route.query.userId)
|
|
|
- this.getRobotInfo()
|
|
|
+ this.mini = Number(this.$route.query.mini)
|
|
|
+ this.other.userId = this.$route.query.userId // 选择的用户的userId、也是客服所在的企业entId
|
|
|
+ this.getUserInfoFn()
|
|
|
this.getSocketUrl()
|
|
|
+ this.getUserListFn()
|
|
|
+ this.getMessageCountFn()
|
|
|
},
|
|
|
- mounted () {
|
|
|
- /**
|
|
|
- * 判断当前用户是否建立会话
|
|
|
- */
|
|
|
- const storage = this.userSessionBadge
|
|
|
- if (storage && Object.keys(storage).length > 0) {
|
|
|
- console.log(storage, 'storage')
|
|
|
- for (const key in storage) {
|
|
|
- if (key === this.other.userId && storage[key]) {
|
|
|
- this.getFindMessage()
|
|
|
- } else {
|
|
|
- this.createSessionFn()
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- this.createSessionFn()
|
|
|
- }
|
|
|
- },
|
|
|
+ mounted () {},
|
|
|
methods: {
|
|
|
...mapMutations({
|
|
|
setUserSessionBadge: 'message/setUserSessionBadge'
|
|
@@ -137,95 +159,149 @@ export default {
|
|
|
webSocketSend: 'webSocket/webSocketSend',
|
|
|
saveMessageAction: 'message/saveMessageAction'
|
|
|
}),
|
|
|
+ // 转人工
|
|
|
+ onTurnPeople (content = '') {
|
|
|
+ this.webSocketSend({
|
|
|
+ type: 2,
|
|
|
+ content: content,
|
|
|
+ rUserId: '455b415651' || this.other.userId,
|
|
|
+ rUserType: 2,
|
|
|
+ headImg: this.login.headImg,
|
|
|
+ nickName: this.login.nickName,
|
|
|
+ sessionId: this.other.sessionId
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 处理websocket接收的数据
|
|
|
+ todoMessage (res) {
|
|
|
+ const data = JSON.parse(res.data)
|
|
|
+ const { content, sUserId } = data.data
|
|
|
+ console.log(data)
|
|
|
+ if (sUserId === this.other.userId) {
|
|
|
+ this.msgList.push({
|
|
|
+ content,
|
|
|
+ create_time: dateFormatter(new Date().getTime())
|
|
|
+ })
|
|
|
+ this.$refs.userSide?.setScrollTop()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 获取用户信息
|
|
|
+ async getUserInfoFn () {
|
|
|
+ const { data } = await getUserInfo()
|
|
|
+ const { nickname, headimg } = data
|
|
|
+ this.login.nickName = nickname
|
|
|
+ this.login.headImg = headimg
|
|
|
+ },
|
|
|
// 获取websocket地址
|
|
|
async getSocketUrl () {
|
|
|
- const { data } = await getWebSocketNode()
|
|
|
- if (data) {
|
|
|
- this.webSocketInit(data)
|
|
|
- }
|
|
|
+ this.webSocketInit()
|
|
|
+ // const { data } = await getWebSocketNode()
|
|
|
+ // if (data) {
|
|
|
+ // this.webSocketInit(data)
|
|
|
+ // }
|
|
|
},
|
|
|
// 切换用户聊天窗口
|
|
|
onSelectCurMsg (data) {
|
|
|
console.log(data)
|
|
|
- // this.msgList = []
|
|
|
- // const { userId, nickName, userType } = data
|
|
|
- // this.curItem.userId = userId
|
|
|
- // this.curItem.name = nickName
|
|
|
- // this.curItem.userType = userType
|
|
|
- // this.showMsgBox = true
|
|
|
- // this.createSessionFn()
|
|
|
- },
|
|
|
- async getRobotInfo () {
|
|
|
- const { data } = await robotInfo({
|
|
|
- entId: this.$route.query.entId
|
|
|
+ const { userId, nickName, userType } = data
|
|
|
+ if (this.other.userId === userId && this.showMsgBox) return
|
|
|
+ this.listInfo.pageIndex = 1
|
|
|
+ this.msgList = []
|
|
|
+ this.other.userId = userId
|
|
|
+ this.other.name = nickName
|
|
|
+ this.other.userType = userType
|
|
|
+ this.showMsgBox = true
|
|
|
+ this.createSessionFn()
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.userSide.setScrollTop()
|
|
|
})
|
|
|
- if (data) {
|
|
|
- this.service.name = data.nickname
|
|
|
- this.service.headImg = data.headimage
|
|
|
- }
|
|
|
},
|
|
|
// 创建会话
|
|
|
async createSessionFn (msgType = 4) {
|
|
|
- const { data } = await createChatSession({
|
|
|
- userType: this.login.userType, // 用户类型:2用户 1客服
|
|
|
- msgType: msgType, // 消息类型 ;1:站内信消息 2:点对点消息 3:群消息 4:机器人消息 5:客服消息
|
|
|
- receiveEntId: this.service.entId // 机器人企业标识
|
|
|
- // receiveAppId: receiveAppId, // 机器人Appid
|
|
|
- // receiveId: receiveId // 用户标识(userType=1时使用)
|
|
|
- })
|
|
|
- if (data) {
|
|
|
- this.other.sessionId = data
|
|
|
- // 存储会话标识
|
|
|
- const userStorage = {
|
|
|
- // key: 用户或客服的userId, value: 会话id
|
|
|
- [this.other.userId]: data
|
|
|
+ this.showMsgBox = true
|
|
|
+ // 先判断是否需要建立会话
|
|
|
+ const storageInfo = this.userSessionBadge
|
|
|
+ // 如果是跟用户聊 则不用创建会话
|
|
|
+ if (this.other.userType === 2) {
|
|
|
+ this.getFindMessage()
|
|
|
+ } else {
|
|
|
+ // 如果是跟客服聊 先判断与该客服是否创建过会话
|
|
|
+ if (storageInfo && Object.keys(storageInfo).length > 0) {
|
|
|
+ for (const key in storageInfo) {
|
|
|
+ if (key === this.other.userId && storageInfo[key]) {
|
|
|
+ this.other.sessionId = storageInfo[key]
|
|
|
+ this.getFindMessage()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- this.setUserSessionBadge(userStorage)
|
|
|
- // 设置首次建立连接时间
|
|
|
- const connectTime = dateFormatter(new Date().getTime())
|
|
|
- // 获取机器人首次回复
|
|
|
- this.getRobotReply()
|
|
|
- this.msgList.push({
|
|
|
- title: '建立会话时间',
|
|
|
- content: `${connectTime} 客户和剑鱼标讯建立了会话`,
|
|
|
- fool: 2,
|
|
|
- create_time: connectTime
|
|
|
- })
|
|
|
- // 保存建立会话时间
|
|
|
- this.saveMessageAction({
|
|
|
- ownType: 1,
|
|
|
- title: '建立会话时间',
|
|
|
- content: `${connectTime} 客户和剑鱼标讯建立了会话`,
|
|
|
- item: 8,
|
|
|
- itemType: 6,
|
|
|
- appid: 10000,
|
|
|
- receiveId: data,
|
|
|
- type: 1,
|
|
|
- link: ''
|
|
|
+ const { data } = await createChatSession({
|
|
|
+ userType: this.login.userType, // 用户类型:2用户 1客服
|
|
|
+ msgType: msgType, // 消息类型 ;1:站内信消息 2:点对点消息 3:群消息 4:机器人消息 5:客服消息
|
|
|
+ receiveEntId: this.other.userId // 机器人企业标识
|
|
|
+ // receiveAppId: receiveAppId, // 机器人Appid
|
|
|
+ // receiveId: receiveId // 用户标识(userType=1时使用)
|
|
|
})
|
|
|
+ if (data) {
|
|
|
+ this.other.sessionId = data
|
|
|
+ // 存储会话标识
|
|
|
+ const userStorage = {
|
|
|
+ // key: 用户或客服的userId, value: 会话id
|
|
|
+ [this.other.userId]: data
|
|
|
+ }
|
|
|
+ this.setUserSessionBadge(userStorage)
|
|
|
+ const connectTime = dateFormatter(new Date().getTime())
|
|
|
+ // 获取机器人头像昵称首次回复
|
|
|
+ this.getRobotInfo()
|
|
|
+ const params = {
|
|
|
+ ownType: 1,
|
|
|
+ title: '建立会话时间',
|
|
|
+ content: `${connectTime}您和剑鱼标讯建立了会话`,
|
|
|
+ item: 8,
|
|
|
+ itemType: 6, // 系统信息
|
|
|
+ appid: 10000,
|
|
|
+ receiveId: data,
|
|
|
+ type: 1,
|
|
|
+ link: ''
|
|
|
+ }
|
|
|
+ // 保存建立会话时间
|
|
|
+ this.saveMessageAction(params).then(res => {
|
|
|
+ const { error_code: code } = res
|
|
|
+ if (code === 0) {
|
|
|
+ params.create_time = dateFormatter(new Date().getTime())
|
|
|
+ params.fool = 2
|
|
|
+ this.msgList.push(params)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
- // 机器人获取首次自动回复
|
|
|
- async getRobotReply () {
|
|
|
- const { data } = await robotReply({
|
|
|
- entId: this.service.entId
|
|
|
+ // 机器人头像、昵称、首次自动回复
|
|
|
+ async getRobotInfo () {
|
|
|
+ const { data } = await robotInfo({
|
|
|
+ entId: this.other.userId
|
|
|
})
|
|
|
if (data) {
|
|
|
- this.msgList.push({
|
|
|
- content: data?.reply,
|
|
|
- fool: 2,
|
|
|
- create_time: dateFormatter(new Date().getTime())
|
|
|
- })
|
|
|
+ this.service.name = data.nickname
|
|
|
+ this.service.headImg = data.headimage
|
|
|
+ this.service.chatStatus = data.chatStatus // 0: 机器人 1:排队中 2:会话中
|
|
|
this.saveMessageAction({
|
|
|
ownType: 2,
|
|
|
title: '机器人首次回复',
|
|
|
content: data?.reply,
|
|
|
item: 8,
|
|
|
- itemType: 4,
|
|
|
+ itemType: 4, // 机器人信息
|
|
|
appid: 10000,
|
|
|
- sendId: this.other.sessionId,
|
|
|
+ sendId: this.other.sessionId, // 发送人标识(客服发送信息时该值为会话标识,用户聊天时该值不传)
|
|
|
type: 1,
|
|
|
link: ''
|
|
|
+ }).then(res => {
|
|
|
+ if (res.code === 0) {
|
|
|
+ this.msgList.push({
|
|
|
+ content: data?.reply,
|
|
|
+ fool: 2,
|
|
|
+ create_time: dateFormatter(new Date().getTime())
|
|
|
+ })
|
|
|
+ }
|
|
|
})
|
|
|
}
|
|
|
},
|
|
@@ -253,62 +329,137 @@ export default {
|
|
|
onSendMsg (data) {
|
|
|
const { content } = data
|
|
|
const params = {
|
|
|
- receiveId: this.other.sessionId,
|
|
|
- ownType: 1,
|
|
|
- title: isHyperlink(content) ? '链接' : '文本消息',
|
|
|
+ receiveId: this.other.userType === 2 ? this.other.userId : this.other.sessionId,
|
|
|
+ ownType: this.other.userType, // 对方身份
|
|
|
+ title: isHyperlink(content) ? '链接' : '文本',
|
|
|
item: 8,
|
|
|
- itemType: 5,
|
|
|
+ itemType: this.other.userType === 2 ? 2 : 5, // 机器人是5 人工是4
|
|
|
link: isHyperlink(content) ? content : '',
|
|
|
- fool: 1,
|
|
|
- type: 1,
|
|
|
+ type: isHyperlink(content) ? 2 : 1,
|
|
|
appid: '10000',
|
|
|
...data
|
|
|
}
|
|
|
- this.msgList.push(params)
|
|
|
- this.saveMessageAction(params)
|
|
|
- },
|
|
|
- // 加载更多
|
|
|
- onLoadMore () {
|
|
|
- this.listInfo.pageIndex++
|
|
|
- this.getFindMessage()
|
|
|
+ this.saveMessageAction(params).then(res => {
|
|
|
+ const { error_code: code } = res
|
|
|
+ if (code === 0) {
|
|
|
+ params.fool = 1
|
|
|
+ this.msgList.push(params)
|
|
|
+ const msgSocket = {
|
|
|
+ type: 6,
|
|
|
+ content: content,
|
|
|
+ contentType: isHyperlink(content) ? 2 : 1,
|
|
|
+ sUserType: 2,
|
|
|
+ rUserId: '455b415651' || this.other.userId,
|
|
|
+ rUserType: this.other.userType,
|
|
|
+ sessionId: this.other.sessionId
|
|
|
+ }
|
|
|
+ this.webSocketSend(msgSocket)
|
|
|
+ const kfKeywords = ['客服', '人工', '人工客服']
|
|
|
+ if (kfKeywords.includes(content)) {
|
|
|
+ // 转人工客服
|
|
|
+ this.onTurnPeople(content)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ params.error = true
|
|
|
+ this.msgList.push(params)
|
|
|
+ }
|
|
|
+ this.getUserListFn()
|
|
|
+ this.$refs.userSide?.setScrollTop()
|
|
|
+ })
|
|
|
},
|
|
|
// 发送、存储图片消息
|
|
|
onUploadImage (data) {
|
|
|
console.log(data)
|
|
|
const params = {
|
|
|
- receiveId: this.other.sessionId,
|
|
|
- ownType: 1,
|
|
|
+ receiveId: this.other.userType === 2 ? this.other.userId : this.other.sessionId,
|
|
|
+ ownType: this.other.userType, // 对方身份
|
|
|
title: '图片',
|
|
|
item: 8,
|
|
|
- itemType: 5,
|
|
|
+ itemType: this.other.userType === 2 ? 2 : 5,
|
|
|
link: '',
|
|
|
fool: 1,
|
|
|
- type: 1,
|
|
|
+ type: 3,
|
|
|
appid: '10000',
|
|
|
content: JSON.stringify(data),
|
|
|
create_time: dateFormatter(new Date().getTime())
|
|
|
}
|
|
|
- this.msgList.push(params)
|
|
|
- this.saveMessageAction(params)
|
|
|
+ this.saveMessageAction(params).then(res => {
|
|
|
+ if (res.code === 0) {
|
|
|
+ this.msgList.push(params)
|
|
|
+ } else {
|
|
|
+ params.error = true
|
|
|
+ this.msgList.push(params)
|
|
|
+ }
|
|
|
+ })
|
|
|
this.$refs.userSide?.setScrollTop()
|
|
|
},
|
|
|
+ // 发送附件消息
|
|
|
onUploadAttach (data) {
|
|
|
const params = {
|
|
|
- receiveId: this.other.sessionId,
|
|
|
- ownType: 1,
|
|
|
+ receiveId: this.other.userType === 2 ? this.other.userId : this.other.sessionId,
|
|
|
+ ownType: this.other.userType, // 对方身份
|
|
|
title: '附件',
|
|
|
item: 8,
|
|
|
- itemType: 5,
|
|
|
+ itemType: this.other.userType === 2 ? 2 : 5,
|
|
|
link: '',
|
|
|
fool: 1,
|
|
|
- type: 1,
|
|
|
+ type: 3,
|
|
|
appid: '10000',
|
|
|
content: JSON.stringify(data),
|
|
|
create_time: dateFormatter(new Date().getTime())
|
|
|
}
|
|
|
- this.msgList.push(params)
|
|
|
- this.saveMessageAction(params)
|
|
|
+ this.saveMessageAction(params).then(res => {
|
|
|
+ if (res.code === 0) {
|
|
|
+ this.msgList.push(params)
|
|
|
+ } else {
|
|
|
+ params.error = true
|
|
|
+ this.msgList.push(params)
|
|
|
+ }
|
|
|
+ })
|
|
|
this.$refs.userSide?.setScrollTop()
|
|
|
+ },
|
|
|
+ // 加载更多
|
|
|
+ onLoadMore () {
|
|
|
+ this.listInfo.pageIndex++
|
|
|
+ this.getFindMessage()
|
|
|
+ },
|
|
|
+ // 获取已咨询过的客户列表
|
|
|
+ async getUserListFn () {
|
|
|
+ const { data } = await getUserList({
|
|
|
+ userType: 2
|
|
|
+ })
|
|
|
+ if (data) {
|
|
|
+ this.userList = data.map(v => {
|
|
|
+ return {
|
|
|
+ title: v.title,
|
|
|
+ headImg: v.headimg,
|
|
|
+ nickName: v.name,
|
|
|
+ badge: v.number,
|
|
|
+ allNumber: v.allNumber,
|
|
|
+ message: v.content,
|
|
|
+ userId: v.userId,
|
|
|
+ userType: v.userType,
|
|
|
+ lastTime: momentTime(v.create_time, 'HH:mm:ss'),
|
|
|
+ active: false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.userList.forEach(s => {
|
|
|
+ if (s.userId === this.other.userId) {
|
|
|
+ s.active = true
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ this.userList = []
|
|
|
+ this.createSessionFn()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 获取未读消息总量
|
|
|
+ async getMessageCountFn () {
|
|
|
+ const params = {
|
|
|
+ userType: 2 // 用户类型:2用户 1客服
|
|
|
+ }
|
|
|
+ const { data } = await getMessageCount(params)
|
|
|
+ this.unReadCount = data
|
|
|
}
|
|
|
}
|
|
|
}
|