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.
 
 
 
 
 
 
zhishifufei_php/application/kefu/view/dashboard/mobile_index.php

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}