You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
611 lines
21 KiB
611 lines
21 KiB
{extend name="public/container"}
|
|
{block name="title"}客服工作台{/block}
|
|
{block name="head"}
|
|
<style>
|
|
.chat-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 100%;
|
|
height: 100%;
|
|
padding-bottom: 0.15rem;
|
|
background: #fff;
|
|
}
|
|
|
|
.chat-list .head-box .hd {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
height: 1rem;
|
|
padding: 0 0.3rem;
|
|
background: linear-gradient(90deg, #3875ea 0%, #1890fc 100%);
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
color: #fff;
|
|
font-size: 0.26rem;
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper img {
|
|
width: 0.58rem;
|
|
height: 0.58rem;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper .info {
|
|
margin-left: 0.12rem;
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper .info .status {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 0.2rem;
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper .info .status .doc {
|
|
width: 0.14rem;
|
|
height: 0.14rem;
|
|
margin-right: 0.1rem;
|
|
background-color: #27f2cb;
|
|
border-radius: 50%;
|
|
margin-top: 0.04rem;
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper .info .status .doc.off {
|
|
background: #919191;
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper .down-wrapper {
|
|
z-index: 50;
|
|
position: absolute;
|
|
left: 0;
|
|
bottom: -1.9rem;
|
|
width: 2.14rem;
|
|
background: #434343;
|
|
border-radius: 0.1rem;
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper .down-wrapper .item {
|
|
display: flex;
|
|
align-items: center;
|
|
height: 0.8rem;
|
|
padding-left: 0.3rem;
|
|
border-bottom: 1px solid rgba(240, 241, 242, 0.16);
|
|
font-size: 0.28rem;
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper .down-wrapper .item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper .down-wrapper .item .dot {
|
|
width: 0.12rem;
|
|
height: 0.12rem;
|
|
margin-right: 0.16rem;
|
|
border-radius: 50%;
|
|
background: linear-gradient(180deg, #bcbcbc 0%, #919191 100%);
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper .down-wrapper .item .dot.green {
|
|
background: linear-gradient(143deg, #27f2cb 0%, #14e3b4 100%);
|
|
}
|
|
|
|
.chat-list .head-box .hd .left-wrapper .down-wrapper .item .iconfont {
|
|
margin-left: 0.36rem;
|
|
color: #b9b9b9;
|
|
font-size: 0.18rem;
|
|
}
|
|
|
|
.chat-list .head-box .hd .right-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
color: #fff;
|
|
font-size: 0.24rem;
|
|
}
|
|
|
|
.chat-list .head-box .search-box {
|
|
padding: .2rem 0.3rem;
|
|
border-bottom: 1px solid #eceff8;
|
|
}
|
|
|
|
.chat-list .head-box .search-box>>>.ivu-input {
|
|
display: block;
|
|
width: 100%;
|
|
height: 0.68rem;
|
|
background: #f5f6f9;
|
|
border-radius: 0.39rem;
|
|
box-sizing: border-box;
|
|
font-size: 0.28rem;
|
|
border-radius: 0.39rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.chat-list .head-box .search-box>>>.ivu-input,
|
|
.chat-list .head-box .search-box .ivu-input:hover,
|
|
.chat-list .head-box .search-box .ivu-input:focus {
|
|
border: transparent;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.chat-list .head-box .tab-box {
|
|
display: flex;
|
|
padding: 0.2rem 0;
|
|
}
|
|
|
|
.chat-list .head-box .tab-box .tab-item {
|
|
flex: 1;
|
|
height: 100%;
|
|
line-height: 0.6rem;
|
|
text-align: center;
|
|
font-size: 0.3rem;
|
|
}
|
|
|
|
.chat-list .head-box .tab-box .tab-item.on {
|
|
color: #3875ea;
|
|
}
|
|
|
|
.chat-list .head-box .tab-box .tab-item:first-child {
|
|
border-right: 1px solid #ddd;
|
|
}
|
|
|
|
.chat-list .list-box {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.chat-list .list-box .item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 0.23rem 0.3rem;
|
|
height: 1.5rem;
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper .img-box {
|
|
width: 0.96rem;
|
|
height: 0.96rem;
|
|
position: relative;
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper .online {
|
|
position: absolute;
|
|
right: 0.1rem;
|
|
bottom: 1px;
|
|
width: 0.16rem;
|
|
height: 0.16rem;
|
|
background: linear-gradient(143deg, #bcbcbc 0%, #919191 100%);
|
|
border-radius: 50%;
|
|
border: 1px solid #fff;
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper .online.on {
|
|
background: linear-gradient(143deg, #27f2cb 0%, #14e3b4 100%);
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper img {
|
|
width: 0.96rem;
|
|
height: 0.96rem;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper .info {
|
|
margin-left: 0.2rem;
|
|
width: 3.5rem;
|
|
height: 0.96rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper .info .title {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 0.3rem;
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper .info .title .label {
|
|
margin-left: 0.15rem;
|
|
font-size: 0.2rem;
|
|
padding: 0.05rem 0.1rem;
|
|
background: rgba(56, 117, 234, 0.14);
|
|
color: #3875EA;
|
|
border-radius: 0.04rem;
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper .info .title .label.h5 {
|
|
background: rgba(255, 162, 0, 0.18);
|
|
color: #d08800;
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper .info .title .label.wx {
|
|
background: rgba(0, 186, 100, 0.14);
|
|
color: #00a219;
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper .info .title .label.pc {
|
|
background: rgba(133, 64, 227, 0.14);
|
|
color: #820adb;
|
|
}
|
|
|
|
.chat-list .list-box .item .left-wrapper .info .msg {
|
|
font-size: 0.24rem;
|
|
color: #9f9f9f;
|
|
}
|
|
|
|
.chat-list .list-box .item .right-wrapper {
|
|
height: 0.96rem;
|
|
color: #9f9f9f;
|
|
font-size: 0.22rem;
|
|
text-align: right;
|
|
}
|
|
|
|
.chat-list .list-box .item .right-wrapper .num {
|
|
min-width: 0.12rem;
|
|
background-color: #f74c31;
|
|
color: #fff;
|
|
border-radius: 0.15rem;
|
|
right: 0 rpx;
|
|
bottom: 0 rpx;
|
|
font-size: 0.2rem;
|
|
padding: 0 0.08rem;
|
|
}
|
|
</style>
|
|
<script>
|
|
document.documentElement.style.fontSize = "calc(100vw/7.5)";
|
|
</script>
|
|
{/block}
|
|
{block name="content"}
|
|
<div class="chat-list" id="app" v-cloak>
|
|
<div class="head-box">
|
|
<div class="hd">
|
|
<div class="left-wrapper">
|
|
<img :src="kefuInfo.avatar">
|
|
<div class="info" @click="isOnLine = !isOnLine">
|
|
<div>{{kefuInfo.nickname}}</div>
|
|
<div class="status">
|
|
<span class="doc" :class="{off:!kefuInfo.online}"></span>
|
|
<span>{{kefuInfo.online?'在线':'离线'}}</span>
|
|
</div>
|
|
</div>
|
|
<div class="down-wrapper" v-show="isOnLine">
|
|
<div class="item" @click="changOnline(1)">
|
|
<span class="dot green"></span>在线
|
|
<span class="iconfont iconduihao" v-if="kefuInfo.online"></span>
|
|
</div>
|
|
<div class="item" @click="changOnline(0)">
|
|
<span class="dot"></span>离线
|
|
<span class="iconfont iconduihao" v-if="!kefuInfo.online"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="right-wrapper" @click="outLogin">
|
|
<div class="icon-box"><span class="iconfont icontuichu"></span></div>
|
|
|
|
<div style="margin-left: 5px;">退出登录</div>
|
|
</div>
|
|
</div>
|
|
<!-- <div class="tab-box">
|
|
<div class="tab-item" :class="{on:tabCur == item.key}" v-for="(item,index) in tabList" @click="changeClass(item)">{{item.title}}</div>
|
|
</div> -->
|
|
<div class="search-box">
|
|
<i-input v-model="searchTxt" placeholder="搜索用户名称" @on-enter="bindSearch" />
|
|
</div>
|
|
</div>
|
|
<div class="list-box" v-if="list.length>0">
|
|
<vue-scroll ref="vs" :ops="ops" @load-before-deactivate="handleBeforeDeactivate">
|
|
<div class="item" v-for="(item,index) in list" :key="index" @click="goPage(item)">
|
|
<div class="left-wrapper">
|
|
<div class="img-box">
|
|
<img :src="item.avatar">
|
|
<div class="online" :class="{on:item.online}"></div>
|
|
</div>
|
|
<div class="info">
|
|
<div class="title">
|
|
<span class="line1">{{item.nickname}}</span>
|
|
<template v-if="item.type == 2">
|
|
<span class="label">小程序</span>
|
|
</template>
|
|
<template v-if="item.type == 3">
|
|
<span class="label h5">H5</span>
|
|
</template>
|
|
<template v-if="item.type == 1">
|
|
<span class="label wx">公众号</span>
|
|
</template>
|
|
<template v-if="item.type == 0">
|
|
<span class="label pc">PC端</span>
|
|
</template>
|
|
<template v-if="item.type == 9">
|
|
<span class="label pc">App</span>
|
|
</template>
|
|
</div>
|
|
<div class="msg line1" v-if="item.message_type == 1">{{item.message}}</div>
|
|
<div class="msg" v-if="item.message_type == 2">[表情]</div>
|
|
<div class="msg" v-if="item.message_type == 3">[图片]</div>
|
|
<div class="msg" v-if="item.message_type == 5">[商品]</div>
|
|
<div class="msg" v-if="item.message_type == 21">[订单]</div>
|
|
<div class="msg" v-if="item.message_type == 24">[转接]</div>
|
|
</div>
|
|
</div>
|
|
<div class="right-wrapper">
|
|
<div class="time">{{item.update_time | toDay}}</div>
|
|
<span class="num" v-if="item.mssage_num>0">{{item.mssage_num}}</span>
|
|
</div>
|
|
</div>
|
|
<div class="slot-load" slot="load-deactive"></div>
|
|
<div class="slot-load" slot="load-active">下滑加载更多</div>
|
|
|
|
</vue-scroll>
|
|
</div>
|
|
<empty v-else status="3" msg="暂无用户列表"></empty>
|
|
</div>
|
|
{/block}
|
|
{block name="script"}
|
|
<script>
|
|
window.$P = JSON.parse.bind(JSON);
|
|
|
|
const mp3 = new Audio("/kefu-assets/assets/audio/notice.wav");
|
|
|
|
require([
|
|
"vue",
|
|
"kefu-assets/api/kefu",
|
|
"kefu-assets/libs/socket",
|
|
"iview",
|
|
"happy-scroll",
|
|
"vue-scroll",
|
|
"kefu-assets/components/empty/index"
|
|
], (Vue, kefuApi, Socket, iView, happyScroll, vueScroll, empty) => {
|
|
|
|
Vue.use(happyScroll.default);
|
|
Vue.use(vueScroll);
|
|
Vue.use(iView);
|
|
|
|
Vue.prototype.bus = new Vue();
|
|
Vue.prototype.$socket = Socket;
|
|
|
|
const kefuInfo = $P(`{$kefuInfo}`);
|
|
|
|
new Vue({
|
|
el: "#app",
|
|
components: {
|
|
empty
|
|
},
|
|
data() {
|
|
return {
|
|
ops: {
|
|
vuescroll: {
|
|
mode: 'slide',
|
|
enable: false,
|
|
tips: {
|
|
deactive: 'Push to Load',
|
|
active: 'Release to Load',
|
|
start: 'Loading...',
|
|
beforeDeactive: 'Load Successfully!'
|
|
},
|
|
auto: false,
|
|
autoLoadDistance: 0,
|
|
pullRefresh: {
|
|
enable: false
|
|
},
|
|
pushLoad: {
|
|
enable: true,
|
|
auto: true,
|
|
autoLoadDistance: 10
|
|
}
|
|
},
|
|
bar: {
|
|
background: '#393232',
|
|
opacity: '.5',
|
|
size: '2px'
|
|
}
|
|
},
|
|
list: [],
|
|
page: 1,
|
|
limit: 10,
|
|
isScroll: true,
|
|
searchTxt: '',
|
|
isOpen: true,
|
|
kefuInfo,
|
|
isOnLine: false,
|
|
tabCur: 0,
|
|
// tabList: [{
|
|
// key: 0,
|
|
// title: '用户列表'
|
|
// },
|
|
// {
|
|
// key: 1,
|
|
// title: '游客列表'
|
|
// }
|
|
// ]
|
|
}
|
|
},
|
|
filters: {
|
|
toDay: function(value) {
|
|
if (!value) return ''
|
|
var date = new Date(); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
|
|
var Y = date.getFullYear() + '-';
|
|
var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
|
|
var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
|
|
var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
|
|
var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes());
|
|
var s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds());
|
|
|
|
value = M + D + h + m;
|
|
return value;
|
|
}
|
|
},
|
|
created() {
|
|
this.getList();
|
|
this.getToken()
|
|
.then(token => {
|
|
if (!token) return;
|
|
this.$socket.then(ws => {
|
|
ws.send({
|
|
type: "kefu_login",
|
|
data: token
|
|
});
|
|
|
|
ws.$on("chat", data => {
|
|
if (data.is_kefu_send) {
|
|
this.online = true;
|
|
} else {
|
|
const record = this.list.find(i => i.to_uid == data.uid);
|
|
if (record) {
|
|
Object.assign(record, {
|
|
add_time: data.add_time,
|
|
message: data.msn,
|
|
message_type: data.msn_type,
|
|
mssage_num: record.mssage_num + 1
|
|
});
|
|
} else {
|
|
this.page = 1;
|
|
this.isScroll = true;
|
|
this.getList(true);
|
|
}
|
|
mp3.play();
|
|
}
|
|
|
|
});
|
|
|
|
ws.$on("socket_error", () => {
|
|
this.$Message.error("连接失败");
|
|
});
|
|
|
|
ws.$on("err_tip", (data) => {
|
|
this.$Message.error(data.msg);
|
|
});
|
|
|
|
ws.$on("offline", (data) => {
|
|
if (!data.self) return;
|
|
this.online = false;
|
|
});
|
|
|
|
ws.$on("request_transfer", (data) => {
|
|
this.$Modal.confirm({
|
|
title: "请求转接用户",
|
|
content: data.nickname + "请求将用户转接给您,是否同意?",
|
|
cancelText: "拒绝",
|
|
onOk: () => {
|
|
ws.send({
|
|
type: "accept_transfer",
|
|
data: {
|
|
kefu_id: data.kefu_id,
|
|
uid: data.uid
|
|
}
|
|
});
|
|
},
|
|
onCancel: () => {
|
|
ws.send({
|
|
type: "reject_transfer",
|
|
data: {
|
|
kefu_id: data.kefu_id,
|
|
uid: data.uid
|
|
}
|
|
})
|
|
}
|
|
});
|
|
});
|
|
|
|
});
|
|
});
|
|
},
|
|
methods: {
|
|
async getToken() {
|
|
try {
|
|
const res = await kefuApi.getToken();
|
|
return res.data.token;
|
|
} catch (err) {
|
|
this.$Message.error(err.msg);
|
|
return false;
|
|
}
|
|
},
|
|
// 列表切换
|
|
changeClass(item) {
|
|
if (this.tabCur == item.key) return
|
|
this.tabCur = item.key
|
|
this.page = 1
|
|
this.list = []
|
|
this.isScroll = true
|
|
this.getList()
|
|
},
|
|
// 客服上下线
|
|
changOnline(key) {
|
|
this.kefuInfo.online = key
|
|
this.isOnLine = false
|
|
this.$socket.then((ws) => {
|
|
ws.send({
|
|
data: {
|
|
online: !!key,
|
|
},
|
|
type: "online",
|
|
});
|
|
});
|
|
},
|
|
getList(forceUpdate = false) {
|
|
if (!this.isScroll) return
|
|
return kefuApi.record({
|
|
nickname: this.searchTxt,
|
|
page: this.page,
|
|
limit: this.limit,
|
|
is_tourist: this.tabCur
|
|
}).then(res => {
|
|
this.isScroll = res.data.length >= this.limit
|
|
if (forceUpdate) {
|
|
this.list = res.data;
|
|
} else {
|
|
this.list = this.list.concat(res.data)
|
|
}
|
|
this.page++;
|
|
setTimeout(() => {
|
|
this.$refs.vs && this.$refs.vs.refresh();
|
|
}, 100);
|
|
})
|
|
},
|
|
// 客服退出
|
|
outLogin() {
|
|
this.$Modal.confirm({
|
|
title: '退出登录确认',
|
|
content: '您确定退出登录当前账户吗?',
|
|
onOk: () => {
|
|
kefuApi.logout()
|
|
.then(res => {
|
|
this.$Message.success(res.msg);
|
|
setTimeout(() => {
|
|
window.location.href = "/kefu/dashboard/login";
|
|
}, 500);
|
|
})
|
|
.catch(err => {
|
|
this.$Message.error(err.msg);
|
|
});
|
|
},
|
|
onCancel: () => {
|
|
|
|
}
|
|
});
|
|
},
|
|
// 搜索
|
|
bindSearch(e) {
|
|
this.page = 1
|
|
this.list = []
|
|
this.isScroll = true
|
|
this.getList()
|
|
},
|
|
// 进入对话
|
|
goPage(item) {
|
|
window.location.href = "{:Url('mobile_chat')}?uid=" + item.to_uid;
|
|
},
|
|
handleBeforeDeactivate(vm, refreshDom, done) {
|
|
this.getList();
|
|
done();
|
|
},
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{/block} |