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.
1031 lines
39 KiB
1031 lines
39 KiB
{extend name="public/container"}
|
|
{block name="title"}客服工作台{/block}
|
|
{block name="head"}
|
|
<style>
|
|
@import "/static/css/google.min.css";
|
|
|
|
.chat-textarea textarea.ivu-input {
|
|
border: none;
|
|
resize: none;
|
|
}
|
|
|
|
.kefu-layouts {
|
|
padding-top: 30px;
|
|
height: 100%;
|
|
display: flex;
|
|
background: #ccc;
|
|
overflow: scroll;
|
|
}
|
|
|
|
.content-wrapper {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 1200px;
|
|
height: 808px;
|
|
margin: 0 auto;
|
|
background: #fff;
|
|
}
|
|
|
|
.content-wrapper .container {
|
|
flex: 1;
|
|
display: flex;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content {
|
|
width: 600px;
|
|
height: 100%;
|
|
border-right: 1px solid #ececec;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body {
|
|
height: 530px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .time {
|
|
text-align: center;
|
|
color: #999;
|
|
font-size: 14px;
|
|
margin: 18px 0;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .flex-box {
|
|
display: flex;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .avatar {
|
|
width: 40px;
|
|
height: 40px;
|
|
margin-right: 16px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .avatar img {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper {
|
|
max-width: 320px;
|
|
background: #f5f5f5;
|
|
border-radius: 10px;
|
|
color: #000;
|
|
font-size: 14px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper .txt-wrapper {
|
|
word-break: break-all;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper .pad16 {
|
|
padding: 9px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper .img-wraper img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
display: block;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper .order-wrapper {
|
|
display: flex;
|
|
width: 320px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper .order-wrapper .img-box {
|
|
width: 60px;
|
|
height: 60px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper .order-wrapper .img-box img {
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper .order-wrapper .order-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
width: 224px;
|
|
margin-left: 10px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper .order-wrapper .order-info .price-box {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
font-size: 14px;
|
|
color: #f00;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper .order-wrapper .order-info .price-box .more {
|
|
font-size: 12px;
|
|
color: #1890ff;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper .order-wrapper .order-info .name {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item .msg-wrapper .order-wrapper .order-info .sku {
|
|
margin: 1px 0;
|
|
color: #999;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item.right-box .flex-box {
|
|
flex-direction: row-reverse;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item.right-box .flex-box .avatar {
|
|
margin-right: 0;
|
|
margin-left: 16px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item.right-box .flex-box .msg-wrapper {
|
|
background: #cde0ff;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-body .chat-item.right-box.gary .msg-wrapper {
|
|
background: #f5f5f5;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea {
|
|
height: 214px;
|
|
border-top: 1px solid #ececec;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 15px 0;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper .left-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper .left-wrapper .icon-item {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-left: 20px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper .left-wrapper .icon-item .iconfont {
|
|
font-size: 22px;
|
|
color: #333;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper .right-wrapper {
|
|
position: relative;
|
|
padding-right: 20px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper .right-wrapper .icon-item {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 15px;
|
|
color: #333;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper .right-wrapper .icon-item span {
|
|
margin-left: 10px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper .right-wrapper .transfer-box {
|
|
z-index: 60;
|
|
position: absolute;
|
|
right: 1px;
|
|
bottom: 43px;
|
|
width: 140px;
|
|
background: #fff;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
padding: 16px;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper .right-wrapper .transfer-bg {
|
|
z-index: 50;
|
|
position: fixed;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: transparent;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper .emoji-box {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
transform: translateY(-100%);
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
width: 60%;
|
|
padding: 15px 9px;
|
|
box-shadow: 0px 0px 13px 1px rgba(0, 0, 0, 0.1);
|
|
background: #fff;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper .emoji-box .emoji-item {
|
|
margin-right: 13px;
|
|
margin-bottom: 8px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.content-wrapper .container .chat-content .chat-textarea .chat-btn-wrapper .emoji-box .emoji-item:nth-child(10n) {
|
|
margin-right: 0;
|
|
}
|
|
|
|
.send-btn {
|
|
position: absolute;
|
|
right: 0;
|
|
bottom: 10px;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
margin-top: 10px;
|
|
margin-right: 10px;
|
|
width: 80px;
|
|
}
|
|
|
|
.send-btn .btns {
|
|
width: 100%;
|
|
background: #3875ea;
|
|
}
|
|
|
|
.send-btn .btns[disabled] {
|
|
background: #ccc;
|
|
color: #fff;
|
|
}
|
|
|
|
.bg {
|
|
z-index: 100;
|
|
position: fixed;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
.happy-scroll-content {
|
|
width: 100%;
|
|
}
|
|
|
|
.happy-scroll-content .demo-spin-icon-load {
|
|
animation: ani-demo-spin 1s linear infinite;
|
|
}
|
|
|
|
.happy-scroll-content .demo-spin-col {
|
|
height: 100px;
|
|
position: relative;
|
|
border: 1px solid #eee;
|
|
}
|
|
|
|
@-moz-keyframes ani-demo-spin {
|
|
from {
|
|
transform: rotate(0deg);
|
|
}
|
|
|
|
50% {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
@-webkit-keyframes ani-demo-spin {
|
|
from {
|
|
transform: rotate(0deg);
|
|
}
|
|
|
|
50% {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
@-o-keyframes ani-demo-spin {
|
|
from {
|
|
transform: rotate(0deg);
|
|
}
|
|
|
|
50% {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
@keyframes ani-demo-spin {
|
|
from {
|
|
transform: rotate(0deg);
|
|
}
|
|
|
|
50% {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
.isMsgbox>>>.ivu-modal-body {
|
|
padding: 0;
|
|
}
|
|
|
|
.kf_mobile .textarea-box textarea {
|
|
resize: none !important;
|
|
height: 148px;
|
|
border-color: transparent;
|
|
font-size: 14px !important;
|
|
}
|
|
|
|
.kf_mobile .textarea-box textarea:focus {
|
|
box-shadow: none;
|
|
}
|
|
</style>
|
|
{/block}
|
|
{block name="content"}
|
|
<div class="kefu-layouts kf_mobile" v-cloak id="app">
|
|
<div class="content-wrapper">
|
|
<base-header :kefu-info="kefuInfo" :online="online" @set-online="setOnline" @search="bindSearch"></base-header>
|
|
<div class="container">
|
|
<chat-list @set-data-id="setDataId" @change-type="changeType" :user-online="userOnline" :new-recored="newRecored" :search-data="searchData"></chat-list>
|
|
<div class="chat-content">
|
|
<div class="chat-body">
|
|
<happy-scroll size="5" resize hide-horizontal :scroll-top="scrollTop" @vertical-start="scrollHandler">
|
|
<div style="width: 600px; padding: 20px" id="chat_scroll" ref="scrollBox">
|
|
<Spin v-show="isLoad">
|
|
<Icon type="ios-loading" size="18" class="demo-spin-icon-load"></Icon>
|
|
<div>Loading</div>
|
|
</Spin>
|
|
<div class="chat-item" v-for="(item, index) in records" :key="index" :class="item.classList" :id="`chat_${item.id}`">
|
|
<div class="time" v-show="item.show">{{ item.time }}</div>
|
|
<div class="time" v-if="item.msn_type == 24 && item.kefu_info">已将用户转接到{{ item.kefu_info.nickname }}</div>
|
|
<div class="flex-box" v-else-if="item.msn_type != 24">
|
|
<div class="avatar">
|
|
<img :src="item.avatar" alt="" />
|
|
</div>
|
|
<div class="msg-wrapper">
|
|
<!-- 文档 -->
|
|
<template v-if="item.msn_type <= 2">
|
|
<div class="txt-wrapper pad16" v-html="item.msn"></div>
|
|
</template>
|
|
<!-- 图片 -->
|
|
<template v-else-if="item.msn_type == 3">
|
|
<div class="img-wraper" v-viewer>
|
|
<img :src="item.msn" alt="" />
|
|
</div>
|
|
</template>
|
|
<!-- 商品 -->
|
|
<template v-else-if="item.msn_type == 5">
|
|
<div class="order-wrapper pad16">
|
|
<div class="img-box">
|
|
<img :src="item.productInfo?.image" alt="" />
|
|
</div>
|
|
<div class="order-info">
|
|
<div class="name line1">
|
|
{{ item.productInfo?.store_name }}
|
|
</div>
|
|
<div class="sku">{{ getSkuScaleContent(item) }}</div>
|
|
<div class="price-box">
|
|
<div class="num">
|
|
¥ {{ item.productInfo?.price }}
|
|
</div>
|
|
<a herf="javascript:;" class="more" @click.stop="lookGoods(item)">查看商品 ></a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<!-- 订单 -->
|
|
<template v-else-if="item.msn_type == 21">
|
|
<div class="order-wrapper pad16">
|
|
<div class="img-box">
|
|
<img :src="
|
|
item.orderInfo.cartInfo[0].productInfo.image
|
|
" alt="" />
|
|
</div>
|
|
<div class="order-info">
|
|
<div class="name line1">
|
|
{{ item.orderInfo.order_id }}
|
|
</div>
|
|
<div class="sku">
|
|
商品数量:{{ item.orderInfo.total_num }}
|
|
类型:{{ item.orderInfo.type_name }}
|
|
</div>
|
|
<div class="price-box">
|
|
<div class="num">
|
|
¥ {{ item.orderInfo.pay_price }}
|
|
</div>
|
|
<a href="javascript:;" class="more" @click.stop="lookOrder(item)">查看订单 ></a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div ref="scroll_flag"></div>
|
|
</div>
|
|
</happy-scroll>
|
|
</div>
|
|
<div class="chat-textarea">
|
|
<div class="chat-btn-wrapper">
|
|
<div class="left-wrapper">
|
|
<div class="icon-item" @click.stop="isEmoji = !isEmoji">
|
|
<span class="iconfont iconbiaoqing1"></span>
|
|
</div>
|
|
<div class="icon-item">
|
|
<Upload :show-upload-list="false" :format="['jpg', 'jpeg', 'png', 'gif']" :on-format-error="handleFormatError" :before-upload="handleUpload">
|
|
<span class="iconfont icontupian1"></span>
|
|
</Upload>
|
|
</div>
|
|
<div class="icon-item" @click.stop.stop="isMsg = true">
|
|
<span class="iconfont iconliaotian"></span>
|
|
</div>
|
|
</div>
|
|
<div class="right-wrapper">
|
|
<div class="icon-item" @click.stop="isTransfer = !isTransfer">
|
|
<span class="iconfont iconzhuanjie"></span>
|
|
<span>转接</span>
|
|
</div>
|
|
<div class="transfer-box" v-if="isTransfer">
|
|
<transfer @close="msgClose" @transferPeople="transferPeople" :user-uid="userActive.to_uid"></transfer>
|
|
</div>
|
|
<div class="transfer-bg" v-if="isTransfer" @click.stop="isTransfer = false"></div>
|
|
</div>
|
|
<!-- 表情 -->
|
|
<div class="emoji-box" v-show="isEmoji">
|
|
<div class="emoji-item" v-for="(emoji, index) in emojiList" :key="index">
|
|
<i class="em" :class="emoji" @click.stop="select(emoji)"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="textarea-box" style="position: relative">
|
|
<i-input v-paste="handleParse" v-model="chatCon" type="textarea" :rows="4" @keydown.native="listen($event)" placeholder="请输入文字内容" @on-enter="bindEnter" style="font-size: 14px"></i-input>
|
|
<div class="send-btn">
|
|
<i-button class="btns" type="primary" :disabled="disabled" @click.stop="sendText">发送</i-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<right-menu :is-tourist="tourist" :uid="userActive.to_uid" :web-type="userActive.type" @bind-push="bindPush"></right-menu>
|
|
</div>
|
|
</div>
|
|
<!-- 用户标签 -->
|
|
<Modal v-model="isMsg" :mask="true" class="none-radius isMsgbox" width="600" :footer-hide="true">
|
|
<msg-window v-if="isMsg" @close="msgClose" @active-txt="activeTxt"></msg-window>
|
|
</Modal>
|
|
<!-- 商品弹窗 -->
|
|
<div v-if="isProductBox">
|
|
<div class="bg" @click.stop="isProductBox = false"></div>
|
|
<goods-detail :goods-info="goodsInfo"></goods-detail>
|
|
</div>
|
|
<!-- 订单详情 -->
|
|
<div v-if="isOrder">
|
|
<Modal v-model="isOrder" title="订单信息" width="700" :footer-hide="true" :mask="true" class="none-radius">
|
|
<order-detail :order-info="orderInfo"></order-detail>
|
|
</Modal>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/block}
|
|
{block name="script"}
|
|
<script>
|
|
window.$P = JSON.parse.bind(JSON);
|
|
|
|
const chunk = (arr, num) => {
|
|
num = num * 1 || 1;
|
|
var ret = [];
|
|
arr.forEach(function(item, i) {
|
|
if (i % num === 0) {
|
|
ret.push([]);
|
|
}
|
|
ret[ret.length - 1].push(item);
|
|
});
|
|
return ret;
|
|
};
|
|
|
|
const mp3 = new Audio("/kefu-assets/assets/audio/notice.wav");
|
|
|
|
require([
|
|
"vue",
|
|
"iview",
|
|
"happy-scroll",
|
|
"vue-scroll",
|
|
"dayjs",
|
|
"v-viewer",
|
|
"kefu-assets/api/kefu",
|
|
"kefu-assets/libs/socket",
|
|
"kefu-assets/libs/emoji",
|
|
"kefu-assets/components/pc/base-header/index",
|
|
"kefu-assets/components/pc/chat-list/index",
|
|
"kefu-assets/components/pc/right-menu/index",
|
|
"kefu-assets/components/pc/msg-window/index",
|
|
"kefu-assets/components/pc/transfer/index",
|
|
"kefu-assets/components/pc/goods-detail/index",
|
|
"kefu-assets/components/pc/order-detail/index",
|
|
], (Vue, iView, happyScroll, vueScroll, dayjs, VueViewer, kefuApi, Socket, emojiList, baseHeader, chatList, rightMenu, msgWindow, transfer, goodsDetail, orderDetail) => {
|
|
Vue.use(happyScroll.default);
|
|
Vue.use(vueScroll);
|
|
Vue.use(iView);
|
|
Vue.use(VueViewer.default);
|
|
|
|
Vue.prototype.bus = new Vue();
|
|
Vue.prototype.$socket = Socket;
|
|
|
|
const kefuInfo = $P(`{$kefuInfo}`);
|
|
|
|
new Vue({
|
|
el: "#app",
|
|
components: {
|
|
baseHeader,
|
|
chatList,
|
|
rightMenu,
|
|
msgWindow,
|
|
transfer,
|
|
goodsDetail,
|
|
orderDetail
|
|
},
|
|
data() {
|
|
return {
|
|
isEmoji: false,
|
|
chatCon: "",
|
|
emojiGroup: chunk(emojiList, 20), // 表情列表
|
|
emojiList: emojiList,
|
|
html: "",
|
|
userActive: {}, //左侧用户列表选中信息
|
|
kefuInfo, //客服信息
|
|
isMsg: false,
|
|
isTransfer: false,
|
|
activeMsg: "", // 选中的话术
|
|
chatList: [],
|
|
text: "",
|
|
limit: 20,
|
|
upperId: 0,
|
|
online: true, //当前客服在线状态
|
|
scrollTop: 0,
|
|
isScroll: true,
|
|
oldHeight: 0,
|
|
isLoad: false,
|
|
isProductBox: false,
|
|
goodsInfo: {},
|
|
isOrder: false,
|
|
orderInfo: {},
|
|
upload: "",
|
|
userOnline: {},
|
|
newRecored: {}, //新对话信息
|
|
searchData: "", // 搜索文字
|
|
scrollNum: 0, //滚动次数
|
|
transferId: "", //转接id
|
|
bodyClose: false,
|
|
tourist: 0,
|
|
};
|
|
},
|
|
computed: {
|
|
disabled() {
|
|
if (this.chatCon.length == 0) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
records() {
|
|
return this.chatList.map((item, index) => {
|
|
item.time = dayjs(item.add_time * 1000).format("YYYY/MM/DD HH:mm:ss");
|
|
if (index) {
|
|
if (item.add_time - this.chatList[index - 1].add_time >= 300) {
|
|
item.show = true;
|
|
} else {
|
|
item.show = false;
|
|
}
|
|
} else {
|
|
item.show = true;
|
|
}
|
|
|
|
item.classList = [];
|
|
|
|
this.kefuInfo.id == item.uid && item.is_kefu_send && item.classList.push('right-box');
|
|
item.msn_type == 5 && item.classList.push('gray');
|
|
|
|
return item;
|
|
});
|
|
},
|
|
},
|
|
// 指令粘贴指令定义
|
|
directives: {
|
|
paste: {
|
|
bind(el, binding, vnode) {
|
|
el.addEventListener("paste", function(event) {
|
|
//这里直接监听元素的粘贴事件
|
|
binding.value(event);
|
|
});
|
|
},
|
|
},
|
|
},
|
|
watch: {
|
|
// socketStatus:{
|
|
// handler(nVal,Val){
|
|
// if(nVal){
|
|
// Socket.send({
|
|
// data: util.cookies.kefuGet('token'),
|
|
// type: "kefu_login"
|
|
// });
|
|
// }
|
|
// },
|
|
// deep:true
|
|
// }
|
|
},
|
|
created() {
|
|
this.getToken()
|
|
.then(token => {
|
|
if (!token) return;
|
|
this.$socket.then(ws => {
|
|
ws.send({
|
|
type: "kefu_login",
|
|
data: token
|
|
});
|
|
|
|
ws.$on(["reply", "chat"], (data) => {
|
|
if (data.is_kefu_send) {
|
|
this.online = true;
|
|
}
|
|
if (!data.is_kefu_send &&
|
|
data.uid != this.userActive.to_uid) return;
|
|
if (data.msn_type == 1) {
|
|
data.msn = this.replace_em(data.msn);
|
|
}
|
|
if (data.msn_type == 2) {
|
|
if (data.msn.indexOf("[") == -1) {
|
|
data.msn = this.replace_em(`[${data.msn}]`);
|
|
}
|
|
}
|
|
this.chatList.push(data);
|
|
this.$nextTick(() => {
|
|
var container = document.querySelector("#chat_scroll");
|
|
this.scrollTop = container.offsetHeight;
|
|
});
|
|
});
|
|
ws.$on("socket_error", () => {
|
|
this.$Message.error("连接失败");
|
|
});
|
|
ws.$on("err_tip", (data) => {
|
|
this.$Message.error(data.msg);
|
|
});
|
|
//用户上线提醒广播
|
|
ws.$on("user_online", (data) => {
|
|
this.userOnline = data;
|
|
});
|
|
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
|
|
}
|
|
})
|
|
}
|
|
});
|
|
});
|
|
ws.$on("accept_transfer", (data) => {
|
|
this.$Notice.success({
|
|
title: "同意转接通知",
|
|
desc: data.nickname + "已同意您的转接请求"
|
|
});
|
|
ws.send({
|
|
type: "chat",
|
|
data: {
|
|
msn_type: 24,
|
|
msn: data.kefu_id,
|
|
to_uid: data.uid,
|
|
}
|
|
});
|
|
});
|
|
ws.$on("reject_transfer", (data) => {
|
|
this.$Notice.error({
|
|
title: "拒绝转接通知",
|
|
desc: data.nickname + "已拒绝您的转接请求"
|
|
});
|
|
});
|
|
});
|
|
});
|
|
},
|
|
mounted() {
|
|
window.addEventListener("click", function() {
|
|
self.isEmoji = false;
|
|
});
|
|
this.text = this.replace_em("[em-smiling_imp]");
|
|
},
|
|
methods: {
|
|
getSkuScaleContent(item) {
|
|
return `库存:${item.productInfo?.stock } 销量:${
|
|
parseInt(item.productInfo?.sales) +
|
|
parseInt(
|
|
item.productInfo?.ficti
|
|
? item.productInfo.ficti
|
|
: 0
|
|
)
|
|
}`;
|
|
},
|
|
async getToken() {
|
|
try {
|
|
const res = await kefuApi.getToken();
|
|
return res.data.token;
|
|
} catch (err) {
|
|
this.$Message.error(err.msg);
|
|
return false;
|
|
}
|
|
},
|
|
handleFormatError(file) {
|
|
this.$Message.error("上传图片只能是 jpg、jpg、jpeg、gif 格式!");
|
|
},
|
|
bindEnter(e) {
|
|
if (e.target.value == "") {
|
|
return this.$Message.error("请输入消息");
|
|
}
|
|
this.sendMsg(e.target.value, 1);
|
|
this.chatCon = "";
|
|
},
|
|
//微信截图上传图片时触发
|
|
handleParse(e) {
|
|
let file = null;
|
|
if (
|
|
e.clipboardData &&
|
|
e.clipboardData.items[0] &&
|
|
e.clipboardData.items[0].type &&
|
|
e.clipboardData.items[0].type.indexOf("image") > -1
|
|
) {
|
|
//这里就是判断是否有粘贴进来的文件且文件为图片格式
|
|
file = e.clipboardData.items[0].getAsFile();
|
|
} else {
|
|
this.$Message.warning("上传的文件必须为图片且无法复制本地图片且无法同时复制多张图片");
|
|
return;
|
|
}
|
|
this.update(file);
|
|
},
|
|
update(e) {
|
|
// 上传照片
|
|
let file = e;
|
|
let param = new FormData(); // 创建form对象
|
|
param.append("filename", "file"); // 通过append向form对象添加数据进去
|
|
param.append("file", file); // 通过append向form对象添加数据进去
|
|
|
|
kefuApi.uploadImg(param)
|
|
.then(res => {
|
|
if (res.code === 200) {
|
|
this.$Message.success("上传成功!");
|
|
this.sendMsg(res.data.url, 3);
|
|
} else {
|
|
this.$Message.error(res.msg);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
|
|
});
|
|
},
|
|
handleUpload(e) {
|
|
this.update(e);
|
|
return false;
|
|
},
|
|
//订单详情
|
|
lookOrder(item) {
|
|
this.orderInfo = {
|
|
...item.orderInfo,
|
|
nickname: this.userActive.nickname
|
|
};
|
|
this.isOrder = true;
|
|
},
|
|
setOnline(data) {
|
|
this.online = data;
|
|
this.$socket.then((ws) => {
|
|
ws.send({
|
|
data: {
|
|
online: data,
|
|
},
|
|
type: "online",
|
|
});
|
|
});
|
|
},
|
|
// 阻止浏览器默认换行操作
|
|
listen(e) {
|
|
if (e.keyCode == 13) {
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
},
|
|
// 输入框选择表情
|
|
select(data) {
|
|
let val = `[${data}]`;
|
|
this.chatCon += val;
|
|
this.isEmoji = false;
|
|
},
|
|
// 聊天表情转换
|
|
replace_em(str) {
|
|
str = str.replace(/\[em-([a-z_]*)\]/g, "<span class='em em-$1'/></span>");
|
|
return str;
|
|
},
|
|
// 获取是否游客
|
|
changeType(data) {
|
|
this.tourist = data;
|
|
},
|
|
// 获取列表用户信息
|
|
setDataId(data) {
|
|
this.userActive = data;
|
|
this.chatList = [];
|
|
this.upperId = 0;
|
|
this.oldHeight = 0;
|
|
this.isScroll = true;
|
|
if (data) {
|
|
window.document.title = data.nickname ?
|
|
`正在和${data.nickname}对话中 - ${this.kefuInfo.site_name}` :
|
|
"正在和游客对话中 - " + this.kefuInfo.site_name;
|
|
this.getChatList();
|
|
|
|
kefuApi.setMsgIsRead(data.to_uid);
|
|
} else {
|
|
window.document.title = this.kefuInfo.site_name;
|
|
}
|
|
},
|
|
msgClose() {
|
|
this.isTransfer = false;
|
|
},
|
|
// 话术选中
|
|
activeTxt(data) {
|
|
this.chatCon = data;
|
|
this.isMsg = false;
|
|
},
|
|
// 文本发送
|
|
sendText() {
|
|
this.sendMsg(this.chatCon, 1);
|
|
this.chatCon = "";
|
|
},
|
|
|
|
// 统一发送处理
|
|
sendMsg(msn, msn_type) {
|
|
|
|
if (this.userActive && this.userActive.user_id) {
|
|
let obj = {
|
|
type: "chat",
|
|
data: {
|
|
msn,
|
|
msn_type,
|
|
to_uid: this.userActive.to_uid,
|
|
is_tourist: this.tourist,
|
|
},
|
|
};
|
|
this.$socket.then((ws) => {
|
|
ws.send(obj);
|
|
});
|
|
}
|
|
},
|
|
send(type, data) {
|
|
Socket.send({
|
|
data,
|
|
type,
|
|
});
|
|
},
|
|
// 获取聊天列表
|
|
getChatList() {
|
|
kefuApi.serviceList({
|
|
limit: this.limit,
|
|
uid: this.userActive.to_uid,
|
|
upper_id: this.upperId,
|
|
is_tourist: this.tourist,
|
|
}).then((res) => {
|
|
res.data.forEach((el) => {
|
|
if (el.msn_type == 1) {
|
|
el.msn = this.replace_em(el.msn);
|
|
} else if (el.msn_type == 2) {
|
|
el.msn = this.replace_em(`[${el.msn}]`);
|
|
}
|
|
});
|
|
let selector = "";
|
|
if (this.upperId == 0) {
|
|
selector = "";
|
|
} else {
|
|
selector = `chat_${this.chatList[0].id}`;
|
|
}
|
|
|
|
// this.chatList = res.data.concat(this.chatList)
|
|
this.chatList = [...res.data, ...this.chatList];
|
|
this.upperId = res.data.length > 0 ? res.data[0].id : 0;
|
|
this.isLoad = false;
|
|
this.$nextTick(() => {
|
|
// this.scrollToTop()
|
|
this.isScroll = res.data.length >= this.limit;
|
|
this.setPageScrollTo(selector);
|
|
});
|
|
});
|
|
},
|
|
// 设置页面滚动位置
|
|
setPageScrollTo(selector) {
|
|
this.$nextTick(() => {
|
|
if (selector) {
|
|
setTimeout(() => {
|
|
let num =
|
|
parseFloat(document.getElementById(selector).offsetTop) - 60;
|
|
this.scrollTop = num;
|
|
}, 0);
|
|
} else {
|
|
var container = document.querySelector("#chat_scroll");
|
|
this.scrollTop = container.offsetHeight;
|
|
setTimeout((res) => {
|
|
if (this.scrollTop != this.$refs.scrollBox.offsetHeight) {
|
|
this.scrollTop =
|
|
document.querySelector("#chat_scroll").offsetHeight;
|
|
}
|
|
}, 300);
|
|
}
|
|
});
|
|
},
|
|
//滚动到顶部
|
|
scrollHandler() {
|
|
let self = this;
|
|
if (this.isScroll && this.upperId) {
|
|
this.isLoad = true;
|
|
this.getChatList();
|
|
}
|
|
},
|
|
// 滚动条动画
|
|
scrollToTop(duration) {
|
|
var container = document.querySelector("#chat_scroll");
|
|
this.scrollTop = container.offsetHeight - this.oldHeight;
|
|
setTimeout((res) => {
|
|
this.scrollTop = this.$refs.scrollBox.offsetHeight - this.oldHeight;
|
|
}, 300);
|
|
},
|
|
// 商品推送
|
|
bindPush(data) {
|
|
this.sendMsg(data, 5);
|
|
},
|
|
// 商品详情
|
|
lookGoods(item) {
|
|
this.goodsInfo = item.productInfo;
|
|
this.isProductBox = true;
|
|
},
|
|
// 搜索用户
|
|
bindSearch(data) {
|
|
this.searchData = data;
|
|
this.oldHeight = 0;
|
|
this.upperId = 0;
|
|
this.isScroll = false;
|
|
},
|
|
// 客服转接
|
|
transferPeople(data) {
|
|
this.transferId = data.id;
|
|
this.isTransfer = false;
|
|
this.$Message.success("转接成功");
|
|
// Socket.then((ws) => {
|
|
// ws.send({
|
|
// type: "to_chat",
|
|
// data: {
|
|
// id: data.uid
|
|
// },
|
|
// });
|
|
// });
|
|
},
|
|
// 客服转接确定
|
|
transferOk() {},
|
|
},
|
|
});
|
|
});
|
|
</script>
|
|
{/block} |