kefu/static/templates/default/chat_main.html

3322 lines
159 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0," name="viewport" />
<title>智能在线客服系统</title>
<link rel="stylesheet" href="/static/cdn/element-ui/2.15.1/theme-chalk/index.min.css">
<script src="/static/cdn/vue/2.6.11/vue.min.js"></script>
<script src="/static/cdn/element-ui/2.15.1/index.js"></script>
<script src="/static/cdn/jquery/3.6.0/jquery.min.js"></script>
<script src="/static/js/functions.js?v=20240626"></script>
<script src="/static/js/reconnecting-websocket.min.js"></script>
<script src="/static/js/ua-parser.min.js"></script>
<link rel="stylesheet" href="/static/css/icono.min.css" />
<link rel="stylesheet" href="/static/css/front.css"/>
<link rel="stylesheet" href="/static/css/common.css?v=20240623"/>
<link rel="stylesheet" href="/static/css/icon/iconfont.css?v=fgjlgfda"/>
<style>
.el-collapse-item__wrap{
border-bottom: none;
}
.el-collapse-item__content{
padding-bottom: 0px;
}
.el-message-box{
min-width: 500px;
width: auto;
overflow: auto;
}
</style>
</head>
<body>
<div id="app" class="chatMainPage">
<template>
<el-row>
<el-col :span="5" class="chatBg chatLeft">
<div>
<el-tabs v-model="leftTabActive">
<el-tab-pane :label="flyLang.visitorList" name="second" class="kefuVisitorList">
<div class="searchVisitor">
<el-input
size="small"
placeholder="name / visitorId / ClienIP"
v-model="visitorName"
clearable>
</el-input>
<el-button style="margin:0px 2px;" size="mini" circle icon="el-icon-search" @click="getVisitorPage(1)"></el-button>
<el-popover
placement="right"
width="150"
trigger="click">
<div class="searchVisitorPlus">
<el-button @click="visitorTag='';kefuName='';visitorName='';readType='';getVisitorPage(1)" size="small" plain>全部</el-button>
<div>
<el-button @click="readType='unread';getVisitorPage(1)" size="small" plain><{flyLang.unread}></el-button>
</div>
<div>
<el-button @click="clearUnreadNum()" size="small" plain>一键已读</el-button>
</div>
<div>
<el-tag v-for="item in allTags" @click="visitorTag=item.id;getVisitorPage(1)"><{item.name}></el-tag>
</div>
<el-select style="width: 100%" size="small" v-show="kfConfig.role_id!='3'" @change="getVisitorPage(1)" placeholder="子账户" v-model="kefuName" style="width: 90px">
<el-option v-for="v in otherKefus" :label="v.nickname" :value="v.name" ></el-option>
</el-select>
</div>
<el-button slot="reference" size="mini" type="primary" icon="el-icon-more" circle></el-button>
</el-popover>
</div>
<el-collapse v-model="visitorCollapse">
<el-collapse-item name="1">
<template slot="title">
&nbsp;🟢&nbsp;<{flyLang.visitorOnline}>
</template>
<div class="noVisitor" v-show="users.length==0">
<div><img src="/static/images/no-visitor.png"/></div>
<{flyLang.noVisitors}>
</div>
<div class="onlineUsers hasLastMsg"
v-for="item in users" :key="item.visitor_id"
v-bind:class="{'cur': item.visitor_id==currentGuest }" v-on:click="talkTo(item.visitor_id,item.username)"
>
<el-badge :value="item.unread_num" :hidden="!item.unread_num" class="item">
<el-avatar :size="40" :src="item.avator" :title="item.username"></el-avatar>
</el-badge>
<div>
<div class="visitorUsername"><{item.username}>
<el-tag type="success"
effect="plain"
size="mini"
>
<{flyLang.online}>
</el-tag>
</div>
<div class="lastNewMsg" v-html="replaceFace(replaceHtml(item.last_message))"></div>
</div>
<div class="visitorTime"><{formatTime("Y-m-d H:M:S",item.last_time)}></div>
</div>
</el-collapse-item>
<el-collapse-item :title="flyLang.visitorAll" name="2">
<template slot="title">
&nbsp;🕰️&nbsp;<{flyLang.visitorAll}>
</template>
<div class="noVisitor" v-show="visitors.length==0">
<div><img src="/static/images/no-visitor.png"/></div>
<{flyLang.noVisitors}>
</div>
<div class="onlineUsers hasLastMsg"
v-for="item in visitors" :key="item.visitor_id"
v-bind:class="{'cur': item.visitor_id==currentGuest }" v-on:click="talkTo(item.visitor_id,item.username)"
>
<el-badge :value="item.unread_num" :hidden="!item.unread_num" class="item">
<el-avatar :size="40" :src="item.avator" :title="item.username"></el-avatar>
</el-badge>
<div>
<div class="visitorUsername"><{item.username}>
</div>
<div class="lastNewMsg" v-if="item.last_message" v-html="replaceFace(replaceHtml(item.last_message))"></div>
<div class="lastNewMsg" v-else>NO NEWS</div>
</div>
<div class="visitorTime"><{formatTime("Y-m-d H:M:S",item.updated_at)}></div>
</div>
<el-pagination
class="kefuVisitorPage"
background
@current-change="visitorPage"
:current-page="visitorCurrentPage"
small
layout="prev,pager, next"
:page-size="visitorPageSize"
:total="visitorCount">
</el-pagination>
</el-collapse-item>
</el-collapse>
</el-tab-pane>
</el-tabs>
</div>
</el-col>
<el-col :span="19" v-show="visitor.visitor_id==''" class="kefuNoVisitor">
<div>
<img src="/static/images/no-chat.png"/>
<h4><{flyLang.noSelectChat}></h4>
<p><{flyLang.haveRest}></p>
</div>
</el-col>
<el-col :span="13" class="chatMiddle chatBgContext" v-show="visitor.visitor_id!=''" v-loading="loading" @click="resizeChatBox;">
<div style="background: #fff;text-align: center;
padding: 11px 0px;
font-size: 13px;
border-bottom: 1px solid #e3e8ef;
color: #1c293b;">
<{chatTitle}>&nbsp已访问&nbsp<b><{visitor.visit_num}></b>次.
&nbsp同IP访客 &nbsp<b><{visitor.sameIp}></b>
<a @click="visitorName=visitor.client_ip;getVisitorPage(1)">查询</a>
<div style="float: right">
<a style="margin:0px 10px" @click="getVisitorBlacks(1);visitorBlackDialog=true;">访客黑名单</a>
<a style="margin-right: 20px" @click="getIpblacks();ipBlacksDialog=true;">IP黑名单</a>
</div>
</div>
<div class="chatBox">
<div style="cursor: pointer" class="chatTime" v-on:click="loadMoreMessages('')" v-show="showLoadMore"><{flyLang.moremessage}></div>
<el-row :gutter="2" v-for="v in msgList" v-bind:class="{'chatBoxMe': v.is_kefu==true}">
<div class="chatTime"><{v.time}></div>
<div class="chatRow">
<el-avatar :title="v.name" v-if="v.is_kefu==false" class="chatRowAvator" shape="circle" :size="48" :src="v.avator"></el-avatar>
<div class="chatMsgContent">
<div class="chatUser"><{v.name}></div>
<div v-if="v.is_kefu==true">
<div class="chatContent" v-html="v.content"></div>
<div class="chatReadStatus">
<!--扩展按钮-->
<el-popover
placement="left"
width="100"
trigger="click">
<div class="searchVisitorPlus">
<el-button @click="trainDialog=true;trainContent=replaceHtml(v.content)" size="small" plain>训练</el-button>
</div>
<div class="searchVisitorPlus">
<el-button @click="replyContentDialog=true;replyTitle=replaceHtml(v.content);replyContent=v.content" size="small" plain>收藏</el-button>
</div>
<div class="searchVisitorPlus">
<el-button @click="deleteMessage(v.msg_id)" size="small" plain>撤回</el-button>
</div>
<el-button slot="reference" size="mini" icon="el-icon-more" circle></el-button>
</el-popover>
<!--//扩展按钮-->
<{v.read_status}>
</div>
</div>
<div v-if="v.is_kefu==false" class="chatContent">
<div v-html="v.content" class="dstContent"></div>
<a href="javascript:void(0);" class="translateBtn"><{flyLang.translate}></a>
</div>
<!--扩展按钮-->
<el-popover
v-if="v.is_kefu==false"
placement="right"
width="100"
trigger="click">
<div class="searchVisitorPlus">
<el-button @click="trainDialog=true;trainContent=replaceHtml(v.content)" size="small" plain>训练</el-button>
</div>
<div class="searchVisitorPlus">
<el-button @click="replyContentDialog=true;replyTitle=replaceHtml(v.content);replyContent=v.content" size="small" plain>收藏</el-button>
</div>
<el-button slot="reference" size="mini" icon="el-icon-more" circle></el-button>
</el-popover>
<!--//扩展按钮-->
</div>
<el-avatar v-if="v.is_kefu==true" :title="v.name" class="chatRowAvator" :size="48" :src="v.avator"></el-avatar>
</div>
</el-row>
</div>
<div class="kefuFuncBox">
<div style="top: -16px;font-size: 12px;color: #8b8b8b;position:absolute;height: 20px;word-break: break-all;overflow: hidden;"
v-show="chatInputing!=''"
> <i class="el-icon-edit"><{chatInputing}></i></div>
<!--进度条-->
<el-progress stroke-width="6" :text-inside="true" :percentage="percentage" v-show="percentage!=0"></el-progress>
<!--//进度条-->
<!--输入搜索-->
<div class="searchList" v-show="searchList.length!=0">
<div v-on:click="kefuEditor.txt.html(item.content);searchList=[]" class="searchItem" v-for="item in searchList" v-html="item.htmlTitle"></div>
</div>
<!--//输入搜索-->
<div class="iconBtnsBox">
<div class="faceBox kefuFaceBox" style="display: none">
<ul class="faceBoxList">
<li v-on:click="faceIconClick(i)" class="faceIcon" v-for="(v,i) in face" :title="v.name"><img :src=v.path></li>
</ul>
<div class="clear"></div>
</div>
<!--表情-->
<div class="emojis" v-show="showEmojis">
<span v-for="item in emojis" @click="kefuEditor.cmd.do('insertHTML', item);showEmojis=false"><{item}></span>
</div>
<!--//表情-->
<div class="iconBtns kefuIconBtns alignCenter">
<div style="font-size: 24px;" :title="flyLang.sendEmoji" class="iconfont icon-xiaolian" @click="showEmojis==false?showEmojis=true:showEmojis=false"></div>
<el-tooltip :content="flyLang.uploadImg" placement="top">
<div v-show="KefuUploadImgBtn!='true'" :title="flyLang.uploadImg" class="el-icon-picture" id="uploadImg" v-on:click="uploadImg('/kefu/uploadimg')" style="font-size: 24px;"></div>
</el-tooltip>
<el-tooltip :content="flyLang.uploadFile" placement="top">
<div v-show="KefuUploadFileBtn!='true'" class="el-icon-upload" id="uploadFile" v-on:click="uploadFile('/kefu/uploadFile')" style="font-size: 26px;"></div>
</el-tooltip>
<el-tooltip :content="flyLang.sendVoice" placement="top">
<div v-show="KefuAudioBtn!='true'" :title="flyLang.uploadImg" class="el-icon-microphone" v-on:click="audioDialog=true" style="font-size: 24px;"></div>
</el-tooltip>
<el-tooltip :content="flyLang.sendComment" placement="top">
<div class="el-icon-star-on" v-on:click="sendComment()" style="font-size: 26px;"></div>
</el-tooltip>
<el-tooltip :content="flyLang.transferKefu" placement="top">
<div class="kefuIcon" v-on:click="transKefu">
<img src="/static/images/transfer.png"/>
</div>
</el-tooltip>
<el-tooltip :content="flyLang.audio" placement="top">
<div class="el-icon-phone-outline" @click="callPhone()" style="font-size: 20px;">
</div>
</el-tooltip>
<el-tooltip :content="flyLang.video" placement="top">
<div class="el-icon-video-camera" @click="callVideo()" style="font-size: 22px;">
</div>
</el-tooltip>
<el-tooltip :content="flyLang.translate" placement="top">
<el-select size="mini" @change="translate" :placeholder="flyLang.translate" v-model="dstLang" style="width: 80px">
<el-option :label="item.name" :value="item.code" v-for="item in allLang"></el-option>
</el-select>
</el-tooltip>
<el-tooltip content="知识库AI辅助回答" placement="top">
<div class="iconfont icon-rengongzhineng" @click="aiHelper.dialog=true" style="font-size: 20px;">
</div>
</el-tooltip>
<el-tooltip content="该访客是否关闭AI回复" placement="top">
<div>
<el-switch
v-model="robotStatus"
active-color="#13ce66"
inactive-color="#ff4949"
@change="switchRobotStatus(robotStatus)"
>
</el-switch>
</div>
</el-tooltip>
</div>
<el-button class="kefuSendBtn" :disabled="sendDisabled" size="small" type="primary" v-on:click="chatToUser()"><{flyLang.sent}></el-button>
</div>
<div class="clear"></div>
<div id="kefuEditor" class="kefuEditor">
<div v-html="messageContent"></div>
</div>
{{/* <el-input @blur="resizeChatBox" @focus="resizeChatBox" :autosize="{ minRows: 4, maxRows: 10}" type="textarea" class="chatArea" v-model="messageContent" v-on:keyup.enter.native="chatToUser" :placeholder="flyLang.textarea"></el-input>*/}}
</div>
</el-col>
<el-col :span="6" class="chatBg chatRight" v-show="visitor.visitor_id!=''">
<el-tabs>
<el-tab-pane :label="flyLang.visitorProfile">
<div style="padding: 10px;color: #666666;font-size: 14px;">
<div class="alignCenter">
<el-avatar shape="circle" :size="40" :src="visitor.avator"></el-avatar>
<span style="margin-left: 10px;"><{visitor.name}> #<{visitor.id}></span>
</div>
<div class="profile">
<div class="profileItem" style="height:auto;line-height:unset;">
<el-tag
type='success'
style="margin: 4px;"
:key="tag"
v-for="tag in dynamicTags"
closable
effect="dark"
:disable-transitions="true"
@close="delTag(tag.tag_name)">
<{tag.tag_name}>
</el-tag>
<el-select size="mini" @change="addTag" clearable="true" v-model="inputValue" style="width: 100px">
<el-option :label="item.name" :value="item.name" v-for="item in allTags" v-bind:key="item.id"></el-option>
</el-select>
<el-input
class="input-new-tag"
v-if="inputVisible"
v-model="inputValue"
ref="saveTagInput"
size="small"
@keyup.enter.native="addTag"
@blur="addTag"
>
</el-input>
<el-button style="margin: 4px;" v-else class="button-new-tag" size="mini" @click="showInput">+ New Tag</el-button>
</div>
<div style="margin-bottom: 10px">
<el-button size="mini" @click="closeVisitor(visitor.visitor_id)"><{flyLang.closeSession}></el-button>
<el-button size="mini" @click="addVisitorBlack()"><{flyLang.banVisitor}></el-button>
<el-button size="mini" @click="addIpblack(visitor.source_ip)"><{flyLang.banIp}></el-button>
</div>
</div>
</div>
<div class="profile">
<div class="profileItem" >
<div class="profileLeft"><i class="el-icon-place"></i>
<{flyLang.ipAddress}>
</div>
<div class="profileRight">
<a v-on:click="openUrl('https://www.baidu.com/s?wd='+visitor.client_ip)"><span v-show="visitor.sameIp>1"><{visitor.sameIp}>】</span><{visitor.client_ip}></a>
<el-button style="margin:0px 2px;" size="mini" circle icon="el-icon-search" @click="visitorName=visitor.client_ip;getVisitorPage(1)"></el-button>
</div>
<div class="clear"></div>
</div>
<div class="profileItem">
<div class="profileLeft"><i class="el-icon-s-platform"></i>
<{flyLang.OsVersion}>
</div>
<div class="profileRight" v-html="visitor.osVersion"></div>
<div class="clear"></div>
</div>
<div class="profileItem" :title="visitor.browser">
<div class="profileLeft"><i class="el-icon-eleme"></i>
<{flyLang.Browser}>
</div>
<div class="profileRight" v-html="visitor.browser"></div>
<div class="clear"></div>
</div>
<div class="profileItem" v-on:click="openUrl('https://www.baidu.com/s?wd='+visitor.client_ip)">
<div class="profileLeft">
<i class="el-icon-map-location"></i>
<{flyLang.city}>
</div>
<div class="profileRight"><{visitor.city}></div>
<div class="clear"></div>
</div>
<div class="profileItem">
<div class="profileLeft">
<i class="el-icon-time"></i>
<{flyLang.firstTime}>
</div>
<div class="profileRight"><{visitor.created_at}></div>
<div class="clear"></div>
</div>
<div class="profileItem">
<div class="profileLeft">
<i class="el-icon-time"></i>
<{flyLang.lastTime}>
</div>
<div class="profileRight"><{visitor.updated_at}></div>
<div class="clear"></div>
</div>
<el-tooltip :content="visitor.refer" placement="left">
<div class="profileItem" :title="visitor.refer">
<div class="profileLeft">
<i class="el-icon-guide"></i>
<{flyLang.title}>
</div>
<div class="profileRight"><{visitor.refer}></div>
<div class="clear"></div>
</div>
</el-tooltip>
<el-tooltip :content="visitor.visitor_id" placement="left">
<div class="profileItem">
<div class="profileLeft">
<i class="el-icon-paperclip"></i>
visitorId
</div>
<div class="profileRight"><{visitor.visitor_id}></div>
<div class="clear"></div>
</div>
</el-tooltip>
<el-tooltip v-for="v in visitorExtra" :content="v.val" placement="left">
<div class="profileItem" :title="v.val">
<div class="profileLeft">
<i class="el-icon-paperclip"></i>
<{v.key}>
</div>
<div class="profileRight" v-on:click="copyText(v.val)"><{v.val}></div>
<div class="clear"></div>
</div>
</el-tooltip>
</div>
<el-tabs v-model="rightTabActive" @tab-click="handleTabClick" class="bottomTabBox">
<el-tab-pane :label="flyLang.comment" name="visitorInfo">
<div class="profile">
<div class="profileItem">
<div class="profileLeft">
<{flyLang.realname}>
</div>
<div class="profileRight">
<el-input @change="saveVisitorAttr({'real_name':visitorAttrs.real_name})" v-model="visitorAttrs.real_name" size="mini" :placeholder="flyLang.realname"></el-input>
</div>
<div class="clear"></div>
</div>
<div class="profileItem">
<div class="profileLeft">
<{flyLang.tel}>
</div>
<div class="profileRight">
<el-input @change="saveVisitorAttr({'tel':visitorAttrs.tel})" v-model="visitorAttrs.tel" size="mini" :placeholder="flyLang.tel"></el-input>
</div>
<div class="clear"></div>
</div>
<div class="profileItem">
<div class="profileLeft">
<{flyLang.email}>
</div>
<div class="profileRight">
<el-input @change="saveVisitorAttr({'email':visitorAttrs.email})" v-model="visitorAttrs.email" size="mini" :placeholder="flyLang.email"></el-input>
</div>
<div class="clear"></div>
</div>
<div class="profileItem">
<div class="profileLeft">
QQ
</div>
<div class="profileRight">
<el-input @change="saveVisitorAttr({'qq':visitorAttrs.qq})" v-model="visitorAttrs.qq" size="mini" placeholder="QQ"></el-input>
</div>
<div class="clear"></div>
</div>
<div class="profileItem">
<div class="profileLeft">
<{flyLang.wechat}>
</div>
<div class="profileRight">
<el-input @change="saveVisitorAttr({'wechat':visitorAttrs.wechat})" v-model="visitorAttrs.wechat" size="mini" :placeholder="flyLang.wechat"></el-input>
</div>
<div class="clear"></div>
</div>
<div style="padding: 10px">
<el-input :rows="4" type="textarea" v-model="visitorAttrs.comment" @change="saveVisitorAttr({'comment':visitorAttrs.comment})" :placeholder="flyLang.comment"></el-input>
</div>
</div>
</el-tab-pane>
<el-tab-pane :label="flyLang.visitorAction" name="visitorMove">
<div class="visitorCard visitorMove" style="padding: 10px">
<el-timeline>
<el-timeline-item
v-for="v in visitorAction.activities"
:timestamp="v.created_at">
<el-tooltip placement="left">
<div slot="content">
访客城市: <{v.city}><br/>
来源标题: <{v.refer}><br/>
访问标题: <{v.title}><br/>
访客语言: <{v.language}><br/>
访客OS: <{v.os_version}><br/>
访客IP: <{v.client_ip}><br/>
访客UA: <{v.ua}><br/>
</div>
<a :href="v.refer_url" target="_blank"><{v.refer}></a>
</el-tooltip>
</el-timeline-item>
</el-timeline>
</div>
<el-pagination
background
@current-change="getVisitorExt"
:current-page="visitorAction.currentPage"
small
layout="prev,pager, next"
:page-size="visitorAction.pageSize"
:total="visitorAction.count">
</el-pagination>
</el-tab-pane>
{{/* <el-tab-pane label="商品列表" name="goodsList">*/}}
{{/* <div class="productCardBox" v-for="item in goodsList.list" >*/}}
{{/* <div class="productCard">*/}}
{{/* <img :src="item.goods_img" class="productImg">*/}}
{{/* <div class="productInfo">*/}}
{{/* <h2 class="productCardTitle" @click="window.open(item.goods_url)"><{item.goods_name}></h2>*/}}
{{/* <div class="productCardBtn">*/}}
{{/* <div class="productCardPrice"><{item.goods_price}></div>*/}}
{{/* <a @click="sendProductCard(item)">发送</a>*/}}
{{/* </div>*/}}
{{/* </div>*/}}
{{/* </div>*/}}
{{/* </div>*/}}
{{/* </el-tab-pane>*/}}
{{/* <el-tab-pane label="订单列表" name="ordersList">*/}}
{{/* <div v-show="ordersListApi==''" class="alertBox">*/}}
{{/* <i class="el-icon-warning-outline"></i>*/}}
{{/* <div>请先前往<a href="/setting_configs">设置页面</a>,配置第三方订单列表接口地址</div>*/}}
{{/* </div>*/}}
{{/* <div class="productCardBox" v-for="item in ordersList.list" >*/}}
{{/* <div class="productCardSn">订单号:<{item.order_id}></div>*/}}
{{/* <div class="productCard">*/}}
{{/* <img v-if="item.goods_img" :src="item.goods_img" class="productImg">*/}}
{{/* <div class="productInfo">*/}}
{{/* <h2 class="productCardTitle" @click="window.open(item.order_url)"><{item.order_name}></h2>*/}}
{{/* <div class="productCardBtn">*/}}
{{/* <div class="productCardPrice"><{item.order_price}></div>*/}}
{{/* <a @click="sendOrderCard(item)">发送</a>*/}}
{{/* </div>*/}}
{{/* </div>*/}}
{{/* </div>*/}}
{{/* </div>*/}}
{{/* <el-pagination*/}}
{{/* v-show="ordersList.list.length!=0"*/}}
{{/* background*/}}
{{/* @current-change="getOrdersList"*/}}
{{/* :current-page="getOrdersList.page"*/}}
{{/* small*/}}
{{/* layout="prev,pager, next"*/}}
{{/* :page-size="getOrdersList.pagesize"*/}}
{{/* :total="getOrdersList.count">*/}}
{{/* </el-pagination>*/}}
{{/* </el-tab-pane>*/}}
</el-tabs>
</el-tab-pane>
<el-tab-pane label="快捷">
<el-tabs type="border-card">
<el-tab-pane label="我的">
<div class="replyBox">
<div style="text-align: right;padding-right: 10px"><a href="javascript:void(0);" @click="replyIsTeam=1;replyGroupDialog = true">+<{flyLang.addGroup}></a>
</div>
<el-input placeholder="搜索快捷回复" v-model="replySearch" class="replySearch" @input="searchReply">
</el-input>
<div class="replyContent">
<el-collapse v-show="replys.length!=0" v-model="replySearchListActive">
<el-collapse-item v-for="reply in replys" :key="reply.group_id" :title="reply.group_name" :name="reply.group_id">
<template slot="title">
<i class="iconfont icon-folder-fill" style="margin-left:10px;font-size: 12px;color:rgb(118 118 118);"></i>&nbsp;<{reply.group_name}>
</template>
<div class="replyItem" @click="setMessageContent(item.item_content)" v-for="item in reply.items" >
<el-popover
placement="left"
width="300"
trigger="hover">
<div v-html="replaceContent(item.item_content)">
</div>
<div class="replyItemTitle" slot="reference"><i class="header-icon el-icon-chat-line-square"></i> <{item.item_name}></div>
</el-popover>
<el-button v-show="replySearch==''" @click="editReplyContent('no',item.item_id,item.item_name,item.item_content)" type="text">编辑</el-button>
<el-button v-show="replySearch==''" @click="deleteReplyContent(item.item_id)" type="text">删除</el-button></div>
<el-button v-show="replySearch==''" @click="replyContentDialog=true;groupName=reply.group_name;groupId=reply.group_id" type="text">+添加回复内容</el-button>
<el-button v-show="replySearch==''" @click="deleteReplyGroup(reply.group_id)" type="text">-删除组</el-button>
</el-collapse-item>
</el-collapse>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="企业公用">
<div class="replyBox">
<div v-show="kfConfig.role_id!='3'" style="text-align: right;padding-right: 10px"><a href="javascript:void(0);" @click="replyIsTeam=2;replyGroupDialog = true">+<{flyLang.addGroup}></a>
</div>
<div class="replyContent">
<el-collapse v-show="entReplys.length!=0">
<el-collapse-item v-for="reply in entReplys" :key="reply.group_id" :title="reply.group_name" :name="reply.group_id">
<template slot="title">
<i class="iconfont icon-folder-fill" style="margin-left:10px;font-size: 12px;color:rgb(118 118 118);"></i>&nbsp;<{reply.group_name}>
</template>
<div class="replyItem" @click="setMessageContent(item.item_content)" v-for="item in reply.items" >
<el-popover
placement="left"
width="300"
trigger="hover">
<div v-html="replaceContent(item.item_content)">
</div>
<div class="replyItemTitle" slot="reference"><i class="header-icon el-icon-chat-line-square"></i> <{item.item_name}></div>
</el-popover>
<el-button v-show="kfConfig.role_id!='3'" @click="editReplyContent('no',item.item_id,item.item_name,item.item_content)" type="text">编辑</el-button>
<el-button v-show="kfConfig.role_id!='3'" @click="deleteReplyContent(item.item_id)" type="text">删除</el-button></div>
<el-button v-show="kfConfig.role_id!='3'" @click="replyContentDialog=true;groupName=reply.group_name;groupId=reply.group_id" type="text">+添加回复内容</el-button>
<el-button v-show="kfConfig.role_id!='3'" @click="deleteReplyGroup(reply.group_id)" type="text">-删除组</el-button>
</el-collapse-item>
</el-collapse>
</div>
</div>
</el-tab-pane>
</el-tabs>
</el-tab-pane>
<el-tab-pane :label="getConfig('IframePageTitle')" v-if="getConfig('IframePageTitle')!=''">
<iframe :src="getConfig('IframePageUrl')+'?visitor_id='+visitor.visitor_id+'&ent_id='+kfConfig.ent_id+'&kefu_name='+kfConfig.name" width="100%" frameborder="0" style="height: calc(100vh - 40px);overflow: hidden"></iframe>
</el-tab-pane>
<el-tab-pane :label="getConfig('IframePageTitle2')" v-if="getConfig('IframePageTitle2')!=''">
<iframe :src="getConfig('IframePageUrl2')+'?visitor_id='+visitor.visitor_id+'&ent_id='+kfConfig.ent_id+'&kefu_name='+kfConfig.name" width="100%" frameborder="0" style="height: calc(100vh - 40px);overflow: hidden"></iframe>
</el-tab-pane>
</el-tabs>
</el-col>
</el-row>
<!--图片放大-->
<div id="bigPic" class="bigPic">
<img src="/static/images/3.jpg"/>
</div>
<!--//图片放大-->
<!--图片预览-->
<el-image
style="display: none;"
ref="preview"
class="hideImgDiv"
:src="imgPreviewSrc[0]"
:preview-src-list="imgPreviewSrc"
z-index="9999"
></el-image>
<audio id="chatMessageAudio">
<source id="chatMessageAudioSource" src="/static/images/alert2.ogg" type="audio/mpeg" />
</audio>
<audio id="chatMessageSendAudio">
<source id="chatMessageSendAudioSource" src="/static/images/sent.ogg" type="audio/mpeg" />
</audio>
<!--转接-->
<el-dialog
:title="flyLang.transferKefu"
:visible.sync="transKefuDialog"
width="30%"
top="0"
>
<el-table
:data="otherKefus"
style="width: 100%">
<el-table-column
prop="nickname"
:label="flyLang.staff">
</el-table-column>
<el-table-column
prop="status"
:label="flyLang.doIt">
<template slot-scope="scope">
<el-tag v-show="scope.row.status=='offline'"
disable-transitions><{flyLang.offline}></el-tag>
<el-button v-show="scope.row.status=='online'" type="primary" @click="transKefuVisitor(scope.row.name,visitor.visitor_id)"><{flyLang.confirm}></el-button>
</template>
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button @click="transKefuDialog = false"><{flyLang.cancel}></el-button>
</span>
</el-dialog>
<!--//转接-->
<!--回复分组-->
<el-dialog
:title="flyLang.addGroup"
:visible.sync="replyGroupDialog"
width="50%"
height="500px"
top="0"
>
<el-input v-model="groupName"></el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="addReplyGroup"><{flyLang.save}></el-button>
<el-button @click="replyGroupDialog = false"><{flyLang.cancel}></el-button>
</span>
</el-dialog>
<!--//回复分组-->
<!--回复内容-->
<el-dialog
title="添加回复内容"
:visible.sync="replyContentDialog"
width="50%"
height="600px"
top="0"
@opened="initEditor()"
@closed="destoryEditor()"
:close-on-click-modal="false"
>
<el-select v-model="groupId" style="margin: 10px 0px">
<el-option :label="item.group_name" :value="item.group_id" v-for="item in replys" v-bind:key="item.group_id"></el-option>
</el-select>
<el-input style="margin-bottom: 10px;" placeholder="标题" v-model="replyTitle"></el-input>
<div id="welcomeEditor" style="border: 1px solid #dcdfe6;"></div>
<span slot="footer" class="dialog-footer">
<el-button @click="addReplyContent"><{flyLang.save}></el-button>
<el-button @click="replyContentDialog = false"><{flyLang.cancel}></el-button>
</span>
</el-dialog>
<el-dialog
title="编辑回复内容"
:visible.sync="editReplyContentDialog"
width="50%"
height="600px"
top="0"
@opened="initEditor('#replyEditor')"
@closed="destoryEditor()"
:close-on-click-modal="false"
>
<el-input style="margin-bottom: 10px;" placeholder="标题" v-model="replyTitle"></el-input>
<div id="replyEditor" style="border: 1px solid #dcdfe6;"></div>
<span slot="footer" class="dialog-footer">
<el-button @click="editReplyContent('yes')"><{flyLang.save}></el-button>
<el-button @click="editReplyContentDialog = false"><{flyLang.cancel}></el-button>
</span>
</el-dialog>
<!--//回复内容-->
<!--训练内容-->
<el-dialog
title="训练内容"
:visible.sync="trainDialog"
width="50%"
height="600px"
@opened="getFileList"
:close-on-click-modal="false"
>
<el-select v-model="fileId" style="margin: 10px 0px">
<el-option :label="item.file_name" :value="item.id" v-for="item in fileList" v-bind:key="item.id"></el-option>
</el-select>
<el-input type="textarea" rows="8" placeholder="修正内容" maxlength="500" v-model="trainContent"></el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="postTrain"><{flyLang.save}></el-button>
<el-button @click="trainDialog = false"><{flyLang.cancel}></el-button>
</span>
</el-dialog>
<!--//训练内容-->
<!--录音-->
<el-dialog
:visible.sync="audioDialog"
width="60%"
top="10%">
<div class="dialogRecoder">
<el-progress :color="colors" type="dashboard" :format="recoderFormat" :stroke-width="10" :percentage="recoderSecond"></el-progress>
<br/>
<audio v-show="recorderEnd" controls ref="audio" muted="muted" src="" id="audio"></audio>
<br/>
<el-button id="start" @click="startRecoder($event)" size="small" type="primary"><{flyLang.start}></el-button>
<el-button @click="stopRecoder($event)" size="small" type="warning"><{flyLang.stop}></el-button>
<el-button @click="cancelRecoder()" size="small" type="danger"><{flyLang.cancel}></el-button>
<el-button @click="sendRecoder()" size="small" type="success"><{flyLang.sent}></el-button>
</div>
</el-dialog>
<!--//录音-->
<!--AI辅助-->
<el-dialog
title="AI助手"
:visible.sync="aiHelper.dialog"
width="60%"
top="10%">
<div class="">
<el-button type="primary" size="mini" @click="customerIntent">会话意图总结</el-button>
<el-button type="primary" size="mini" @click="getKefuAiKnowledge();aiHelper.dialog = false">生成辅助回答</el-button>
<div v-html="aiHelper.reply" style="margin-top: 10px">
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button size="mini" @click="aiHelper.dialog = false"><{flyLang.cancel}></el-button>
</span>
</el-dialog>
<!--//录音-->
<!--访客黑名单-->
<el-dialog
title="访客黑名单"
:visible.sync="visitorBlackDialog"
width="50%"
top="20px"
>
<el-table
:data="visitorBlacks.list"
style="width: 100%">
<el-table-column
prop="name"
label="名称">
</el-table-column>
<el-table-column
prop="visitor_id"
label="访客ID">
</el-table-column>
<el-table-column
prop="kefu_name"
label="操作客服">
</el-table-column>
<el-table-column
prop="created_at"
label="时间">
</el-table-column>
<el-table-column
label="操作">
<template slot-scope="scope">
<a v-on:click="delVisitorBlack(scope.row.id)">删除</a>
</template>
</el-table-column>
</el-table>
<el-pagination
background
@current-change="getVisitorBlacks"
:current-page="visitorBlacks.currentPage"
small
layout="prev,pager, next"
:page-size="visitorBlacks.pagesize"
:total="visitorBlacks.count">
</el-pagination>
</el-dialog>
<!--//访客黑名单-->
<!--IP黑名单-->
<el-dialog
title="IP黑名单"
:visible.sync="ipBlacksDialog"
width="50%"
top="20px"
>
<el-table
:data="ipBlacks"
style="width: 100%">
<el-table-column
prop="name"
label="名称">
</el-table-column>
<el-table-column
prop="ip"
label="IP地址">
</el-table-column>
<el-table-column
prop="kefu_id"
label="操作客服">
</el-table-column>
<el-table-column
prop="create_at"
label="时间">
</el-table-column>
<el-table-column
label="操作">
<template slot-scope="scope">
<a v-on:click="delIpblack(scope.row.ip)">删除</a>
</template>
</el-table-column>
</el-table>
</el-dialog>
<!--//IP黑名单-->
</template>
</div>
</body>
<script>
var KefuAudioBtn='{{.KefuAudioBtn}}';
var KefuUploadImgBtn='{{.KefuUploadImgBtn}}';
var KefuUploadFileBtn='{{.KefuUploadFileBtn}}';
var KefuCleanBtn='{{.KefuCleanBtn}}';
var BaiduFanyiAppId='{{.BaiduFanyiAppId}}';
</script>
<script src="/static/js/chat-lang.js?v=sdsd45jhkhjv45"></script>
<script src="/static/js/wangEditor.min.js"></script>
<script src="/static/js/recoder.js"></script>
<script src="/static/js/peer.js"></script>
<script src="/static/js/markdown-it.min.js"></script>
<script>
var LANG=checkLang();
var app=new Vue({
el: '#app',
delimiters:["<{","}>"],
data: {
visitorCollapse:['1','2'],
loading:false,
flyLang:KEFU_LANG[LANG],
visible:false,
fullscreenLoading:true,
leftTabActive:"second",
rightTabActive:"visitorInfo",
users:[],
usersMap:[],
server:getWsBaseUrl()+"/ws_kefu?token="+localStorage.getItem("token"),
socket:null,
messageContent:"",
currentGuest:"",
msgList:[],
chatTitle:KEFU_LANG[LANG].chatIntro,
chatInputing:"",
kfConfig:{
id : "kf_1",
name : "客服丽丽",
avator : "",
to_id : "",
role_id:"",
ent_id:"",
},
visitor:{
name:"",
visitor_id:"",
refer:"",
client_ip:"",
city:"",
status:"",
state:"",
source_ip:"",
created_at:"",
osVersion:"",
browser:"",
sameIp:0,
},
visitorAction:{
count:0,
currentPage:1,
pageSize:6,
activities:[],
},
visitorBlackDialog:false,
visitorBlacks:{
count:0,
page:1,
pagesize:16,
list:[],
},
visitorAttrs:{},
visitorExtra:[],
visitors:[],
visitorCount:0,
visitorCurrentPage:1,
visitorPageSize:10,
visitorName:"",
kefuName:"",
visitorTag:"",
readType:"",
face:emojiGifsMap(),
transKefuDialog:false,
otherKefus:[],
replyIsTeam:1,
replyGroupDialog:false,
replyContentDialog:false,
editReplyContentDialog:false,
trainDialog:false,
trainContent:"",
fileList:[],
fileId:"",
searchList:[],
replySearch:"",
replySearchList:[],
replySearchListActive:[],
groupName:"",
groupId:"",
configs:[],
replys:[],
entReplys:[],
replyId:"",
replyContent:"",
replyTitle:"",
ipBlacksDialog:false,
ipBlacks:[],
sendDisabled:false,
peerjsId:"",
localStream:null,
isCalling:"",
call:null,
currentPage:1,
showLoadMore:false,
loadMoreDisable:false,
alertSounding:false,
alertSoundingTimer:null,
newMessageComing:false,
dynamicTags: [],
inputVisible: false,
inputValue: '',
allTags:[],
editor:null,
kefuEditor:null,
currentMessage:{},
recorder:null,
recorderAudio:null,
recordeTimer:null,
audioDialog:false,
recoderSecond:0,
recorderEnd:false,
KefuAudioBtn:KefuAudioBtn,
KefuUploadImgBtn:KefuUploadImgBtn,
KefuUploadFileBtn:KefuUploadFileBtn,
KefuCleanBtn:KefuCleanBtn,
BaiduFanyiAppId:BaiduFanyiAppId,
percentage:0,
colors: [
{color: '#f56c6c', percentage: 20},
{color: '#e6a23c', percentage: 40},
{color: '#5cb87a', percentage: 60},
{color: '#1989fa', percentage: 80},
{color: '#6f7ad3', percentage: 100}
],
imgPreviewSrc:[
""],
dstLang:"",
allLang:[
{name:"English",code:"en"},
{name:"中文",code:"zh"},
{name:"русский язык",code:"ru"},
{name:"Deutsch",code:"de"},
{name:"Französisch",code:"fra"},
{name:"日本語",code:"jp"},
{name:"한국어 공부 해요.",code:"kor"},
{name:"ViệtName",code:"vie"},
],
robotStatus:true,
markdownIt:null,
showEmojis:false,
aiHelper:{
dialog:false,
reply:"",
},
emojis:{"smile":"😄","smiley":"😃","grinning":"😀","blush":"😊","wink":"😉","heart_eyes":"😍","kissing_heart":"😘","kissing_closed_eyes":"😚","kissing":"😗","kissing_smiling_eyes":"😙","stuck_out_tongue_winking_eye":"😜","stuck_out_tongue_closed_eyes":"😝","stuck_out_tongue":"😛","flushed":"😳","grin":"😁","pensive":"😔","relieved":"😌","unamused":"😒","disappointed":"😞","persevere":"😣","cry":"😢","joy":"😂","sob":"😭","sleepy":"😪","disappointed_relieved":"😥","cold_sweat":"😰","sweat_smile":"😅","sweat":"😓","weary":"😩","tired_face":"😫","fearful":"😨","scream":"😱","angry":"😠","rage":"😡","triumph":"😤","confounded":"😖","laughing":"😆","yum":"😋","mask":"😷","sunglasses":"😎","sleeping":"😴","dizzy_face":"😵","astonished":"😲","worried":"😟","frowning":"😦","anguished":"😧","imp":"👿","open_mouth":"😮","grimacing":"😬","neutral_face":"😐","confused":"😕","hushed":"😯","smirk":"😏","expressionless":"😑","man_with_gua_pi_mao":"👲","man_with_turban":"👳","cop":"👮","construction_worker":"👷","guardsman":"💂","baby":"👶","boy":"👦","girl":"👧","man":"👨","woman":"👩","older_man":"👴","older_woman":"👵","person_with_blond_hair":"👱","angel":"👼","princess":"👸","smiley_cat":"😺","smile_cat":"😸","heart_eyes_cat":"😻","kissing_cat":"😽","smirk_cat":"😼","scream_cat":"🙀","crying_cat_face":"😿","joy_cat":"😹","pouting_cat":"😾","japanese_ogre":"👹","japanese_goblin":"👺","see_no_evil":"🙈","hear_no_evil":"🙉","speak_no_evil":"🙊","skull":"💀","alien":"👽","hankey":"💩","fire":"🔥","sparkles":"✨","star2":"🌟","dizzy":"💫","boom":"💥","anger":"💢","sweat_drops":"💦","droplet":"💧","zzz":"💤","dash":"💨","ear":"👂","eyes":"👀","nose":"👃","tongue":"👅","lips":"👄","thumbs_up":"👍","-1":"👎","ok_hand":"👌","facepunch":"👊","fist":"✊","wave":"👋","hand":"✋","open_hands":"👐","point_up_2":"👆","point_down":"👇","point_right":"👉","point_left":"👈","raised_hands":"🙌","pray":"🙏","clap":"👏","muscle":"💪","walking":"🚶","runner":"🏃","dancer":"💃","couple":"👫","family":"👪","couplekiss":"💏","couple_with_heart":"💑","dancers":"👯","ok_woman":"🙆","no_good":"🙅","information_desk_person":"💁","raising_hand":"🙋","massage":"💆","haircut":"💇","nail_care":"💅","bride_with_veil":"👰","person_with_pouting_face":"🙎","person_frowning":"🙍","bow":"🙇","tophat":"🎩","crown":"👑","womans_hat":"👒","athletic_shoe":"👟","mans_shoe":"👞","sandal":"👡","high_heel":"👠","boot":"👢","shirt":"👕","necktie":"👔","womans_clothes":"👚","dress":"👗","running_shirt_with_sash":"🎽","jeans":"👖","kimono":"👘","bikini":"👙","briefcase":"💼","handbag":"👜","pouch":"👝","purse":"👛","eyeglasses":"👓","ribbon":"🎀","closed_umbrella":"🌂","lipstick":"💄","yellow_heart":"💛","blue_heart":"💙","purple_heart":"💜","green_heart":"💚","broken_heart":"💔","heartpulse":"💗","heartbeat":"💓","two_hearts":"💕","sparkling_heart":"💖","revolving_hearts":"💞","cupid":"💘","love_letter":"💌","kiss":"💋","ring":"💍","gem":"💎","bust_in_silhouette":"👤","speech_balloon":"💬","footprints":"👣"},
//商品列表
goodsList:{
count:1,
page:1,
pagesize:10,
list:[
{
goods_id:"1",
goods_name:"纯坚果零食大礼包一整箱干果类网红爆款解馋小吃休闲食品送女友",
goods_img:"https://img.alicdn.com/bao/uploaded/i1/2201453915278/O1CN01dZolFu1orN8dFeKAj_!!0-item_pic.jpg_200x200q90.jpg_.webp",
goods_price:"¥9.9",
goods_url:"https://img.alicdn.com/bao/uploaded/i1/2201453915278/O1CN01dZolFu1orN8dFeKAj_!!0-item_pic.jpg_200x200q90.jpg_.webp",
}
],
},
goodsListApi:"",
//订单列表
ordersList:{
count:1,
page:1,
pagesize:10,
list:[
// {
// order_id:"1",
// order_name:"纯坚果零食大礼包一整箱干果类网红爆款解馋小吃休闲食品送女友",
// goods_img:"https://img.alicdn.com/bao/uploaded/i1/2201453915278/O1CN01dZolFu1orN8dFeKAj_!!0-item_pic.jpg_200x200q90.jpg_.webp",
// order_price:"¥9.9",
// order_url:"https://img.alicdn.com/bao/uploaded/i1/2201453915278/O1CN01dZolFu1orN8dFeKAj_!!0-item_pic.jpg_200x200q90.jpg_.webp",
// }
],
},
ordersListApi:"",
},
methods: {
//跳转
openUrl(url) {
window.location.href = url;
},
// sendKefuOnline(){
// let mes = {}
// mes.type = "kfOnline";
// mes.data = this.kfConfig;
// this.socket.send(JSON.stringify(mes));
// },
//心跳
ping(){
var _this=this;
var mes = {};
mes.type = "ping";
setInterval(function () {
if(_this.socket!=null){
_this.socket.send(JSON.stringify(mes));
}
},58000);
// setInterval(function(){
// _this.getOnlineVisitors();
// },300000);
// setInterval(function(){
// _this.getVisitorPage(1);
// },60000);
},
//初始化websocket
initConn() {
let socket = new ReconnectingWebSocket(this.server);//创建Socket实例
this.socket = socket
this.socket.debug=true;
this.socket.onmessage = this.OnMessage;
this.socket.onopen = this.OnOpen;
this.socket.onerror = this.OnError;
this.socket.onclose = this.OnClose;
},
OnClose:function(event) {
console.log("ws:onclose",event);
this.reloadDialog("连接关闭,重连次数 "+this.socket.reconnectAttempts);
},
OnError:function(event) {
console.log("ws:onerror",event);
this.reloadDialog("连接错误,重连次数 "+this.socket.reconnectAttempts);
},
OnOpen() {
//this.sendKefuOnline();
if(this.visitor.visitor_id!=""){
this.currentPage=1;
this.loadMoreMessages(this.visitor.visitor_id);
}
},
OnMessage(e) {
const redata = JSON.parse(e.data);
switch (redata.type){
case "read":
if (redata.data.visitor_id == this.visitor.visitor_id) {
for(var i=0;i<this.msgList.length;i++){
this.$set(this.msgList[i],'read_status',KEFU_LANG[LANG]['read']);
}
}
case "inputing":
this.handleInputing(redata.data);
//this.sendKefuOnline();
break;
case "allUsers":
this.handleOnlineUsers(redata.data);
//this.sendKefuOnline();
break;
case "userOnline":
this.addOnlineUser(redata.data);
break;
case "userOffline":
this.removeOfflineUser(redata.data);
//this.sendKefuOnline();
break;
// case "callpeer":
// this.handleCall(redata.data);
// break;
// case "callCancel":
// this.handleCallCancel(redata.data);
// break;
case "comment":
this.$notify({
title: '成功',
message: redata.data,
type: 'success'
});
break;
case "notice":
//发送通知
var _this=this;
window.parent.postMessage({
name:redata.data.username,
body: redata.data.content,
icon: redata.data.avator
},"*");
break;
}
if (redata.type == "message") {
console.log(redata);
let msg = redata.data
let content = {}
let _this=this;
content.avator = msg.avator;
content.name = msg.name;
if(msg.is_kefu=="yes"){
content.is_kefu = true;
content.content = replaceSpecialTag(msg.content);
}else{
content.is_kefu = false;
content.content = this.markdownHtml(replaceSpecialTag(msg.content));
}
content.time = msg.time;
content.msg_id=msg.msg_id;
//JS接口hook
jsHook=_this.getConfig("JsHook");
if(jsHook){
eval(jsHook);
}
if (msg.id == this.currentGuest) {
this.msgList.push(content);
}
for(let i=0;i<this.users.length;i++){
if(this.users[i].visitor_id==msg.id){
var lastMessage=replaceFace(replaceHtml(msg.content));
if (lastMessage=="") lastMessage="...";
this.$set(this.users[i],'last_message',lastMessage.substr(0,16));
this.$set(this.users[i],'last_time',msg.time);
if(this.users[i].unread_num){
this.$set(this.users[i],'unread_num',++this.users[i].unread_num);
}else{
this.$set(this.users[i],'unread_num',1);
}
if(this.users[i].visitor_id==this.visitor.visitor_id){
this.$set(this.users[i],'unread_num',0);
}
}
}
for(let i=0;i<this.visitors.length;i++){
if(this.visitors[i].visitor_id==msg.id){
var lastMessage=replaceFace(replaceHtml(msg.content));
if (lastMessage=="") lastMessage="...";
this.$set(this.visitors[i],'last_message',lastMessage.substr(0,16));
this.$set(this.visitors[i],'unread_num',++this.visitors[i].unread_num);
}
if(this.visitors[i].visitor_id==this.visitor.visitor_id){
this.$set(this.visitors[i],'unread_num',0);
}
}
this.scrollBottom();
if(content.is_kefu){
return;
}
window.parent.postMessage({
name:msg.name,
body: msg.content,
icon: msg.avator
},"*");
if(this.visitor.visitor_id!=msg.id && _this.getConfig("VisitorMessageAlert")=="on"){
_this.newVisitorForceAlert(msg.name+"新消息提醒");
}
_this.chatInputing="";
_this.newMessageComing=true;
}
},
//接手客户
talkTo(guestId,name) {
this.loading=true;
this.currentGuest = guestId;
this.visitor.visitor_id=guestId;
//this.chatTitle=name+"|"+guestId+",正在处理中...";
//发送给客户
// let mes = {}
// mes.type = "kfConnect";
// this.kfConfig.to_id=guestId;
// mes.data = this.kfConfig;
// this.socket.send(JSON.stringify(mes));
//获取标签
this.getTags(guestId);
//获取当前访客信息
this.getVistorInfo(guestId);
//获取当前访客动态信息
this.resetVisitorAction();
this.getVisitorExt(1);
this.getVisitorAttr(guestId);
//获取当前客户消息
//this.getMesssagesByVisitorId(guestId);
this.currentPage=1;
this.loadMoreDisable=false;
this.loadMoreMessages(guestId);
var cleanAlertSound=true;
for(var i=0;i<this.users.length;i++){
if(this.users[i].visitor_id==guestId){
this.$set(this.users[i],'hidden_new_message',true);
this.$set(this.users[i],'unread_num',0);
}
if(this.users[i].unread_num>0){
cleanAlertSound=false;
}
}
var unreadNum=0;
for(let i=0;i<this.visitors.length;i++){
if(this.visitors[i].visitor_id==guestId){
unreadNum=this.visitors[i].unread_num?this.visitors[i].unread_num:0;
this.$set(this.visitors[i],'unread_num',0);
}
if(this.visitors[i].unread_num>0){
cleanAlertSound=false;
}
}
//发送给父iframe未读数
window.parent.postMessage({type:"read_num",data:unreadNum},"*");
//获取机器人信息
this.robotStatus=this.getConfig("RobotStatus")=="3" ? false:true;
},
//发送给客户
chatToUser(isFile) {
let message=this.messageContent;
var editorHtml=this.kefuEditor.txt.html();
if(!isFile && editorHtml!="<div></div>" && editorHtml!=""){
message=editorHtml;
}
message=trim(message,"\n");
message=trim(message,"\r\n");
message=trim(message,"<div></div>");
message=trim(message,"<p>");
message=trim(message,"</p>");
message=trim(message,"<br/>");
if(message=="" || this.currentGuest==""){
return;
}
if(this.sendDisabled){
return;
}
this.sendDisabled=true;
let _this=this;
let mes = {};
mes.type = "kefu";
mes.content = message;
mes.from_id = this.kfConfig.id;
mes.to_id = this.currentGuest;
$.ajax({
type:"post",
url:"/kefu/message",
data:mes,
headers:{
"token":localStorage.getItem("token")
},
success: function(data) {
_this.sendDisabled=false;
if(data.code!=200){
_this.$message({
message: data.msg,
type: 'error'
});
}
_this.messageContent = "";
if(!isFile) _this.kefuEditor.txt.clear();
_this.sendSound();
}
});
this.scrollBottom();
},
//处理当前在线用户列表
addOnlineUser:function (retData) {
var flag=false;
var newUser={};
if(retData.last_message==""){
retData.last_message=this.flyLang.newVisitor;
}
newUser.last_message=retData.last_message.substr(0,16);
newUser.status=1;
newUser.username=retData.username;
newUser.hidden_new_message=true;
newUser.visitor_id=retData.uid;
newUser.avator=retData.avator;
newUser.updated_at=getNowDate();
newUser.last_time=retData.last_time;
for(let i=0;i<this.users.length;i++){
if(this.users[i].visitor_id==newUser.visitor_id){
flag=true;
}
}
if(!flag){
this.users.unshift(newUser);
}
var newUserflag=false;
for(let i=0;i<this.visitors.length;i++){
if(this.visitors[i].visitor_id==newUser.visitor_id){
newUserflag=true;
break;
}
}
if(!newUserflag){
newUser.unread_num=0;
this.visitors.unshift(newUser);
}
if(this.visitor.visitor_id==newUser.visitor_id){
this.getVistorInfo(newUser.visitor_id)
}
},
//处理当前在线用户列表
removeOfflineUser:function (retData) {
this.users=removeObjects(this.users,"visitor_id",retData.uid)
// for(let i=0;i<this.users.length;i++){
// if(this.users[i].visitor_id==retData.uid){
// this.users.splice(i,1);
// }
// }
// let vid=retData.uid;
// for(let i=0;i<this.visitors.length;i++){
// if(this.visitors[i].visitor_id==vid){
// this.visitors[i].status=0;
// break;
// }
// }
},
//处理当前在线用户列表
handleOnlineUsers:function (retData) {
if (this.currentGuest == "") {
this.chatTitle = "连接成功,等待处理中...";
}
this.usersMap=[];
for(let i=0;i<retData.length;i++){
this.usersMap[retData[i].uid]=retData[i].username;
retData[i].last_message=_this.flyLang.newVisitor;
}
if(this.users.length==0){
this.users = retData;
}
for(let i=0;i<this.visitors.length;i++){
let vid=this.visitors[i].visitor_id;
if(typeof this.usersMap[vid]=="undefined"){
this.visitors[i].status=0;
}else{
this.visitors[i].status=1;
}
}
},
//处理正在输入
handleInputing:function (retData) {
if(retData.from==this.visitor.visitor_id){
this.chatInputing=""+retData.content+"...";
if(retData.content==""){
this.chatInputing="";
}else{
this.showVisitorMove(retData.content)
}
}
for(var i=0;i<this.users.length;i++){
if(this.users[i].visitor_id==retData.from){
this.$set(this.users[i],'last_message',retData.content.substr(0,16)+"...");
}
}
},
showVisitorMove:function(content){
// $(".chatBox").append("<div class=\"chatTime timeLine\"><span>访客动态:"+content+"</span></div>");
// this.scrollBottom();
//this.activities.push(content);
},
//新访客强制提醒
newVisitorForceAlert:function (title) {
var _this=this;
if(_this.alertSounding){
return;
}
var timer=_this.newVisitorForceAlertSound();
this.$confirm(title, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(function(){
clearInterval(timer);
_this.alertSounding=false;
}).catch(function(){
clearInterval(timer);
_this.alertSounding=false;
});
_this.alertSounding=true;
},
newVisitorForceAlertSound(){
var _this=this;
var timer=setInterval(function(){
_this.alertSound();
},3000);
_this.alertSoundingTimer=timer;
return timer;
},
//获取客服信息
getKefuInfo(){
let _this=this;
$.ajax({
type:"get",
url:"/kefu/kefuinfo",
headers:{
"token":localStorage.getItem("token")
},
success: function(data) {
if(data.code==200 && data.result!=null){
_this.kfConfig.id=data.result.id;
_this.kfConfig.name=data.result.name;
_this.kfConfig.avator=data.result.avator;
_this.kfConfig.role_id=data.result.role_id;
_this.kfConfig.ent_id=data.result.ent_id;
_this.getVisitorPage(1);
}
if(data.code!=200){
_this.$message({
message: data.msg,
type: 'error'
});
}
}
});
},
//获取客服信息
getOnlineVisitors(){
let _this=this;
$.ajax({
type:"get",
url:"/visitors_kefu_online",
headers:{
"token":localStorage.getItem("token")
},
success: function(data) {
if(data.code==200 && data.result!=null){
var list=data.result;
easyFor(list,function(i,item){
var lastMessage=replaceHtml(replaceSpecialTag(item.last_message));
if (lastMessage=="") lastMessage="...";
list[i].last_message=lastMessage.substr(0,16);
})
_this.users=list;
// for(var i=0;i<_this.users.length;i++){
//
// _this.$set(_this.users[i],'last_message',lastMessage);
// _this.$set(_this.users[i],'hidden_new_message',true);
// }
}
if(data.code!=200){
_this.$message({
message: data.msg,
type: 'error'
});
window.location.href="/ironMan/signInx";
}
if(data.code==400){
window.location.href="/ironMan/signInx";
}
}
});
},
//获取信息列表
getMesssagesByVisitorId(visitorId,isAll){
let _this=this;
$.ajax({
type:"get",
url:"/kefu/messages?visitor_id="+visitorId,
headers:{
"token":localStorage.getItem("token")
},
success: function(data) {
if(data.code==200 && data.result!=null){
let msgList=data.result;
_this.msgList=[];
if(!isAll&&msgList.length>10){
var i=msgList.length-10
}else{
var i=0;
}
for(;i<msgList.length;i++){
let visitorMes=msgList[i];
let content = {}
if(visitorMes["mes_type"]=="kefu"){
content.is_kefu = true;
}else{
content.is_kefu = false;
}
content.avator = visitorMes["avator"];
content.name = visitorMes["name"];
content.content = replaceSpecialTag(visitorMes["content"]);
content.time = visitorMes["time"];
_this.msgList.push(content);
_this.scrollBottom();
}
}
if(data.code!=200){
_this.$message({
message: data.msg,
type: 'error'
});
}
if(data.code==400){
window.location.href="/ironMan/signInx";
}
}
});
},
//获取客服信息
getVistorInfo(vid){
let _this=this;
this.resetVisitorAction();
//$(".timeLine").remove();
$.ajax({
type:"get",
url:"/kefu/visitor",
data:{visitorId:vid},
headers:{
"token":localStorage.getItem("token")
},
success: function(data) {
if(data.result!=null){
let r=data.result;
_this.visitor=r;
_this.visitor.sameIp=data.same_ip;
_this.visitor.created_at=data.create_time;
_this.visitor.updated_at=data.last_time;
//解析ua
let uap = new UAParser().setUA(data.ua);
let uaResult = uap.getResult();
let osName=uaResult.os.name ? uaResult.os.name : "";
let osVersion=uaResult.os.version ? uaResult.os.version : "";
let browserName=uaResult.browser.name ? uaResult.browser.name : "";
let browserVersion=uaResult.browser.version ? uaResult.browser.version : "";
let osArchitecture=uaResult.cpu.architecture ? uaResult.cpu.architecture : "";
if (osArchitecture == "ia32") {
osArchitecture = " 32Bit";
} else if (osArchitecture == "amd64") {
osArchitecture = " 64Bit";
}
_this.visitor.osVersion=getOsImg(osName)+" "+osVersion+" "+osArchitecture;
_this.visitor.browser=getBrowerImg(browserName)+" "+browserVersion;
if(r.refer!=""){
_this.visitor.refer=r.refer
}else{
_this.visitor.refer="-";
}
//_this.visitor.visitor_id=r.visitor_id;
_this.chatTitle=r.name;
_this.visitorExtra=[];
_this.visitor.state=r.state;
//访客状态位,第一位是否开启机器人回复
_this.robotStatus=r.state.substr(0,1)=="2"?false:true;
if(r.extra!=""){
try{
var extra=JSON.parse(b64ToUtf8(r.extra));
if (typeof extra=="string"){
extra=JSON.parse(extra);
}
for(var key in extra){
if(extra[key]==""){
extra[key]="无";
}
if(key=="visitorAvatar"||key=="visitorName"||key=="visitorProduct") continue;
var temp={key:key,val:extra[key]}
_this.visitorExtra.push(temp);
}
}catch (e) {}
}
}
if(data.code!=200){
_this.$message({
message: data.msg,
type: 'error'
});
window.location.href="/ironMan/signInx";
}
}
});
},
//关闭访客
closeVisitor(visitorId){
var _this=this;
this.$confirm('此操作将会关闭该访客会话, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(function(){
$.ajax({
type:"get",
url:"/kefu/message_close",
data:{visitor_id:visitorId},
headers:{
"token":localStorage.getItem("token")
},
success: function(data) {
if(data.code!=200){
_this.$message({
message: data.msg,
type: 'error'
});
}
}
});
});
},
//处理tab切换
handleTabClick(tab, event){
let _this=this;
if(tab.name=="first"){
this.getOnlineVisitors();
}
if(tab.name=="second"){
this.getOnlineVisitors();
this.getVisitorPage(1);
}
if(tab.name=="visitorMove"){
this.resetVisitorAction();
this.getVisitorExt(1);
}
//商品列表
if(tab.name=="goodsList"){
}
//订单列表
if(tab.name=="ordersList"){
this.ordersListApi=this.getConfig("OrdersListApi");
this.getOrdersList(1);
}
},
//所有访客分页展示
visitorPage(page){
this.getVisitorPage(page);
},
//获取访客分页
getVisitorPage(page){
let _this=this;
this.visitorCurrentPage=page;
var pagesize=18;
var kefuName=this.kefuName;
if(this.kfConfig.role_id=="3"){
kefuName=this.kfConfig.name;
}
var parames={kefuName:kefuName,page:page,pagesize:pagesize,
visitorName:this.visitorName,
visitorTag:this.visitorTag,
readType:this.readType
};
$.ajax({
type:"get",
url:"/kefu/visitorList",
data:parames,
headers:{
"token":localStorage.getItem("token")
},
success: function(data) {
if(data.result.list!=null){
var list=data.result.list;
easyFor(list,function(i,item){
var lastMessage=replaceHtml(replaceSpecialTag(item.last_message));
if(lastMessage=="") lastMessage="……"
list[i].last_message=lastMessage.substr(0,16);
});
_this.visitors=list;
_this.visitorCount=data.result.count;
_this.visitorPageSize=data.result.pagesize;
}
if(data.code!=200){
_this.$message({
message: data.msg,
type: 'error'
});
}
}
});
},
replaceContent(content){
return replaceSpecialTag(content)
},
replaceHtml(content){
return replaceHtml(content);
},
replaceFace(str){
var faces=emojiGifs();
return str.replace(/face\[([^\s\[\]]+?)\]/g, function (face) { // 转义表情
var alt = face.replace(/^face/g, '');
return '<img style="width: 20px;" alt="' + alt + '" title="' + alt + '" src="' + faces[alt] + '">';
});
},
//滚到底部
scrollBottom(){
this.$nextTick(() => {
$('.chatBox').scrollTop($(".chatBox")[0].scrollHeight);
});
},
//jquery
initJquery(){
this.$nextTick(() => {
var _this=this;
$(function () {
//展示表情
// var faces=placeFace();
// $.each(faceTitles, function (index, item) {
// _this.face.push({"name":item,"path":faces[item]});
// });
$("body").on("click","#faceBtn",function() {
$('.faceBox').show();
});
$("body").on("mouseover",".chatBoxMe",function() {
$(".chatDeleteBtn").hide();
$(this).find(".chatDeleteBtn").show();
});
$("body").on("mouseover",".chatBoxMe",function() {
$(".chatDeleteBtn").hide();
$(this).find(".chatDeleteBtn").show();
});
$("body").on("mouseleave",".chatBoxMe",function() {
$(".chatDeleteBtn").hide();
});
$("body").on("mouseleave",".faceBox",function() {
$(this).hide();
});
$("body").on("click",".chatDeleteBtn",function() {
$(this).parents(".chatBoxMe").hide();
});
$("body").on("click",".chatContent img",function() {
var url=$(this).attr("data-src");
if(!url){
url=$(this).attr("src");
}
_this.imgPreviewSrc=[url];
_this.$refs.preview.clickHandler();
return false;
});
//翻译
$("body").on("click",".translateBtn",function() {
var translateBtn=$(this);
var obj=translateBtn.parent().find(".dstContent");
var src=obj.text();
_this.sendAjax("/kefu/translate","get",{words:src,from:"auto",to:"zh"},function(data){
var dst=data.dst;
if(dst==""){
_this.$message({
message: "没有翻译结果!",
type: 'error'
});
return;
}
obj.append("<br/>"+data.dst);
translateBtn.hide();
});
});
makeResizable("kefuFuncBox",function(){
$("#kefuEditor .w-e-text-container").height($('.kefuFuncBox').height()-120);
});
var ms= 1000*2;
var lastClick = Date.now() - ms;
$("body").mouseover(function(){
if(!_this.newMessageComing||_this.visitor.visitor_id==""){
return;
}
if (Date.now() - lastClick >= ms) {
lastClick = Date.now();
_this.sendAjax("/kefu/messages_read",'post',{visitor_id:_this.visitor.visitor_id},function(ret){
_this.newMessageComing=false;
});
}
});
//$(".chatBox").css("height","calc(100% - 180px)");
//ctrl+enter
$("body").keypress(function(e) {
if (e.ctrlKey && e.which == 13 || e.which == 10){
var html=_this.kefuEditor.txt.html();
_this.kefuEditor.txt.html(html);
}else if( e.which == 13 || e.which == 10){
_this.chatToUser();
}
});
//初始化editor
_this.initKefuEditor();
//页面展示事件
// 监听页面状态
document.addEventListener('visibilitychange', function() {
// 页面状态变化为不可见时触发
if (document.visibilityState == 'hidden') {}
// 页面状态变化为可见时触发
if (document.visibilityState == 'visible') {
//_this.initConn();
}
}
);
//markdown解析
_this.markdownIt=window.markdownit({
html: true,
linkify: true,
typographer: true,
});
});
});
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?82938760e00806c6c57adee91f39aa5e";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
},
//表情点击事件
faceIconClick(index){
$('.faceBox').hide();
//var content=this.editor.txt.html();
var content=replaceSpecialTag("face"+this.face[index].name);
//this.editor.txt.html(content);
this.kefuEditor.cmd.do('insertHTML', content)
//this.messageContent=content;
},
//上传图片
uploadImg (url){
let _this=this;
$('#uploadImg').after('<input type="file" accept="image/gif,image/jpeg,image/jpg,image/png" id="uploadImgFile" name="file" style="display:none" >');
$("#uploadImgFile").click();
$("#uploadImgFile").change(function (e) {
var formData = new FormData();
var file = $("#uploadImgFile")[0].files[0];
formData.append("imgfile",file); //传给后台的file的key值是可以自己定义的
filter(file) && $.ajax({
url: url || '',
type: "post",
data: formData,
contentType: false,
processData: false,
headers: {
"token": localStorage.getItem("token")
},
dataType: 'JSON',
mimeType: "multipart/form-data",
//添加自定义属性,监听上下文的进度
xhr: function() {
//创建原生的ajax请求对象
var xhr = $.ajaxSettings.xhr();
//监听进度的一个事件
xhr.upload.onprogress = function(e) {
console.log(e.total); //文件大小
console.log(e.loaded); //上传多少
var w = parseInt((e.loaded / e.total) * 100)
console.log(w);
_this.percentage=w;
if(w>=100){
_this.percentage=0;
}
}
return xhr
},
success: function (res) {
if(res.code!=200){
_this.$message({
message: res.msg,
type: 'error'
});
}else{
_this.$message({
message: "图片上传成功!",
type: 'success'
});
_this.messageContent='img[' + res.result.path + ']';
//_this.messageContent=replaceSpecialTag(_this.messageContent);
//_this.editor.txt.html(_this.messageContent);
_this.chatToUser(true);
}
},
error: function (data) {
console.log(data);
_this.$message({
message: "上传失败!"+data.responseText,
type: 'error'
});
}
});
});
},
//上传文件
uploadFile:function (url){
let _this=this;
$('#uploadFile').after('<input type="file" id="uploadRealFile" name="file2" style="display:none" >');
$("#uploadRealFile").click();
$("#uploadRealFile").change(function (e) {
var formData = new FormData();
var file = $("#uploadRealFile")[0].files[0];
formData.append("realfile",file); //传给后台的file的key值是可以自己定义的
console.log(formData);
$.ajax({
url: url || '',
type: "post",
data: formData,
contentType: false,
processData: false,
dataType: 'JSON',
headers: {
"token": localStorage.getItem("token")
},
mimeType: "multipart/form-data",
//添加自定义属性,监听上下文的进度
xhr: function() {
//创建原生的ajax请求对象
var xhr = $.ajaxSettings.xhr();
//监听进度的一个事件
xhr.upload.onprogress = function(e) {
console.log(e.total); //文件大小
console.log(e.loaded); //上传多少
var w = parseInt((e.loaded / e.total) * 100)
console.log(w);
_this.percentage=w;
if(w>=100){
_this.percentage=0;
}
}
return xhr
},
success: function (res) {
if(res.code!=200){
_this.$message({
message: res.msg,
type: 'error'
});
}else{
_this.$message({
message: "文件上传成功!",
type: 'success'
});
//_this.messageContent+='file[' + res.result.path + ']';
var data=JSON.stringify({
name:res.result.name,
ext:res.result.ext,
size:res.result.size,
path:res.result.path,
})
_this.messageContent='mutiFile[' + data+ ']';
_this.chatToUser(true);
}
},
error: function (data) {
console.log(data);
_this.$message({
message: "上传失败!"+data.responseText,
type: 'error'
});
}
});
});
},
//发送产品卡片
sendProductCard(goods){
let data=JSON.stringify({
"title":goods.goods_name,
"price":goods.goods_price,
"url":goods.goods_url,
"img":goods.goods_img,
})
this.messageContent='product[' + data+ ']';
this.chatToUser();
},
//发送订单卡片
sendOrderCard(goods){
let data=JSON.stringify({
"title":'订单编号:'+goods.order_id+"<br/>产品名称:"+goods.order_name,
"price":goods.order_price,
"url":goods.order_url,
"img":goods.goods_img,
})
this.messageContent='product[' + data+ ']';
this.chatToUser();
},
addIpblack(ip){
let _this=this;
$.ajax({
type:"post",
url:"/kefu/ipblack",
data:{ip:ip,name:this.visitor.name},
headers:{
"token":localStorage.getItem("token")
},
success: function(data) {
if(data.code!=200){
_this.$message({
message: data.msg,
type: 'error'
});
}else{
_this.$message({
message: data.msg,
type: 'success'
});
}
}
});
},
//粘贴上传图片
onPasteUpload(event){
let items = event.clipboardData && event.clipboardData.items;
let file = null
if (items && items.length) {
// 检索剪切板items
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
file = items[i].getAsFile()
}
}
}
if (!file) {
return;
}
let _this=this;
var formData = new FormData();
formData.append('imgfile', file);
$.ajax({
url: '/kefu/uploadimg',
type: "post",
data: formData,
contentType: false,
processData: false,
dataType: 'JSON',
headers: {
"token": localStorage.getItem("token")
},
mimeType: "multipart/form-data",
//添加自定义属性,监听上下文的进度
xhr: function() {
//创建原生的ajax请求对象
var xhr = $.ajaxSettings.xhr();
//监听进度的一个事件
xhr.upload.onprogress = function(e) {
console.log(e.total); //文件大小
console.log(e.loaded); //上传多少
var w = parseInt((e.loaded / e.total) * 100)
console.log(w);
_this.percentage=w;
if(w>=100){
_this.percentage=0;
}
}
return xhr
},
success: function (res) {
if(res.code!=200){
_this.$message({
message: res.msg,
type: 'error'
});
}else{
_this.$message({
message: "图片上传成功!",
type: 'success'
});
_this.messageContent='img[' + res.result.path + ']';
_this.chatToUser(true);
_this.messageContent="";
}
},
error: function (data) {
console.log(data);
_this.$message({
message: "上传失败!"+data.responseText,
type: 'error'
});
}
});
},
openUrl(url){
window.open(url);
},
//提示音
alertSound(){
var b = document.getElementById("chatMessageAudio");
var src=this.getConfig("KefuAlertSound");
if(src!=""){
b.src=src;
}
var p = b.play();
p && p.then(function(){}).catch(function(e){});
},
sendSound(){
var b = document.getElementById("chatMessageSendAudio");
var p = b.play();
p && p.then(function(){}).catch(function(e){});
},
getOtherKefu(){
var _this=this;
this.sendAjax("/other_kefulist","get",{},function(result){
_this.otherKefus=result;
});
},
//转移客服
transKefu(){
this.transKefuDialog=true;
this.getOtherKefu();
},
//转移访客客服
transKefuVisitor(kefu,visitorId){
var _this=this;
this.sendAjax("/trans_kefu","get",{kefu_id:kefu,visitor_id:visitorId},function(result){
//_this.otherKefus=result;
_this.transKefuDialog = false;
_this.visitor.visitor_id="";
});
},
//保存回复分组
addReplyGroup(){
var _this=this;
this.sendAjax("/kefu/replyGroup","post",{group_name:_this.groupName,is_team:this.replyIsTeam},function(result){
//_this.otherKefus=result;
_this.replyGroupDialog = false
_this.groupName="";
_this.getReplys();
_this.getEntReplys();
});
},
//添加回复内容
addReplyContent(){
var _this=this;
var content=this.editor.txt.html();
this.sendAjax("/reply_content","post",{group_id:_this.groupId,item_name:_this.replyTitle,content:content},function(result){
//_this.otherKefus=result;
_this.replyContentDialog = false
_this.replyContent="";
_this.getReplys();
_this.getEntReplys();
});
},
//获取快捷回复
getReplys(){
var _this=this;
this.sendAjax("/replys","get",{},function(result){
_this.replys=result;
});
},
//获取企业公用快捷回复
getEntReplys(){
var _this=this;
this.sendAjax("/kefu/entReplys","get",{},function(result){
_this.entReplys=result;
});
},
getConfigs(){
var _this=this;
this.sendAjax("/kefu/ent_configs","get",{},function(result){
_this.configs=result;
});
},
getConfig(key){
for(index in this.configs){
if(key==this.configs[index].conf_key){
return this.configs[index].conf_value;
}
}
return "";
},
//删除回复
deleteReplyGroup(id){
var _this=this;
this.$confirm('此操作将删除本组所有回复内容, 是否继续?', _this.flyLang.tips, {
confirmButtonText: _this.flyLang.confirm,
cancelButtonText: _this.flyLang.cancel,
type: 'warning'
}).then(function(){
_this.sendAjax("/reply?id="+id,"delete",{},function(result){
_this.getReplys();
_this.getEntReplys();
_this.$message({
type: 'success',
message: 'success!'
});
});
});
},
//删除回复
deleteReplyContent(id){
var _this=this;
this.$confirm('此操作将删除这条快捷回复内容, 是否继续?', _this.flyLang.tips, {
confirmButtonText: _this.flyLang.confirm,
cancelButtonText: _this.flyLang.cancel,
type: 'warning'
}).then(function(){
_this.sendAjax("/reply_content?id="+id,"delete",{},function(result){
_this.getReplys();
_this.getEntReplys();
_this.$message({
type: 'success',
message: 'success!'
});
});
});
},
//编辑回复
editReplyContent(save,id,title,content){
var _this=this;
if(save=='yes'){
var data={
reply_id:this.replyId,
reply_title:this.replyTitle,
reply_content:this.editor.txt.html()
}
this.sendAjax("/reply_content_save","post",data,function(result){
_this.editReplyContentDialog=false;
_this.getReplys();
_this.getEntReplys();
});
}else{
this.editReplyContentDialog=true;
this.replyId=id;
this.replyTitle=title;
this.replyContent=content;
}
},
//搜索回复
searchReply(){
var _this=this;
_this.replySearchListActive=[];
if(this.replySearch==""){
this.getReplys();
return;
}
this.sendAjax("/kefu/replySearch","post",{search:this.replySearch},function(result){
_this.replys=result;
for (var i in result) {
_this.replySearchListActive.push(result[i].group_id);
}
});
},
//获取访客动态
getVisitorExt(currentPage){
var _this=this;
var visitorId=this.visitor.visitor_id
this.sendAjax("/kefu/visitorExt","get",{visitor_id:visitorId,page:currentPage,pagesize:_this.visitorAction.pageSize},function(result){
if(result.count>=1){
firstItem=result.list[0];
_this.$nextTick(function(){
//_this.$set(_this.visitor,"osVersion",firstItem.os_version);
//_this.$set(_this.visitor,"browser",myBrowser(firstItem.ua));
});
}
_this.visitorAction.activities=result.list;
_this.visitorAction.count=result.count;
});
},
resetVisitorAction(){
this.visitorAction.activities=[];
this.visitorAction.count=0;
this.visitorAction.currentPage=1;
},
//获取黑名单
getIpblacks(){
var _this=this;
this.sendAjax("/kefu/ipblacks","get",{},function(result){
_this.ipBlacks=result;
});
},
//删除黑名单
delIpblack(ip){
let _this=this;
this.sendAjax("/kefu/ipblack?ip="+ip,"DELETE",{ip:ip},function(result){
_this.getIpblacks();
});
},
//获取访客黑名单
getVisitorBlacks(page){
var _this=this;
this.sendAjax("/kefu/visitorBlacks","get",{
page:page,pagesize:this.visitorBlacks.pagesize
},function(result){
_this.visitorBlacks=result;
});
},
//获取订单列表
getOrdersList(page){
let _this=this;
if(_this.ordersListApi==='') return;
this.sendAjax(_this.ordersListApi,"post",{
page:page,pagesize:this.ordersList.pagesize,
visitor_id:this.visitor.visitor_id
},function(result){
_this.ordersList=result;
});
},
//添加进访客黑名单
addVisitorBlack(){
var _this=this;
this.$confirm('此操作将会拉黑该访客, 是否继续?', _this.flyLang.tips, {
confirmButtonText: _this.flyLang.confirm,
cancelButtonText: _this.flyLang.cancel,
type: 'warning'
}).then(function(){
_this.sendAjax("/kefu/visitorBlack","post",{
"visitor_id":_this.visitor.visitor_id,
"name":_this.visitor.name
},function(result){
_this.$message({
message: result.msg,
type: 'success'
});
});
});
},
//删除访客黑名单
delVisitorBlack(id){
let _this=this;
this.sendAjax("/kefu/delVisitorBlack","get",{id:id},function(result){
_this.getVisitorBlacks(1);
});
},
//打电话
callPhone:function(){
var _this=this;
sendAjax("/kefu/callVisitor","post",{
peer_id:"222",
visitor_id:_this.visitor.visitor_id,
"action":"callPhone"
},function(result){
_this.$message({
message: result.msg,
type: 'success'
});
});
},
//打视频
callVideo:function(){
var _this=this;
sendAjax("/kefu/callVisitor","post",{
peer_id:"222",
visitor_id:_this.visitor.visitor_id,
"action":"callVideo"
},function(result){
_this.$message({
message: result.msg,
type: 'success'
});
});
},
initPeerjs:function(visitorId){
var _this=this;
var peer = new Peer();
this.peer=peer;
this.peer.on('open', function(id) {
console.log('My peer ID is: ' + id);
_this.peerjsId=id;
_this.sendAjax("/kefu/callVisitor","post",{
peer_id:_this.peerjsId,
visitor_id:visitorId,
"action":"accept"
},function(result){
// _this.$alert(retData.name+'正在通话..', '提示', {
// confirmButtonText: '挂断',
// callback: function(){
// if(_this.mediaConnection!=null){
// _this.mediaConnection.close();
// }
// console.log(_this.mediaConnection);
// }
// });
});
});
this.peer.on('close', function() {
_this.callClear();
console.log('My peer close');
});
this.peer.on('disconnected', function() {
_this.callClear();
console.log('My peer disconnected');
});
this.peer.on('error', function() {
_this.callClear();
console.log('My peer error');
});
this.peer.on('call', function(call) {
var isVideo=false
_this.$confirm(_this.flyLang.videoAudio, _this.flyLang.tips, {
confirmButtonText: _this.flyLang.video,
cancelButtonText: _this.flyLang.recoder,
type: 'warning'
}).then(() => {
isVideo=true;
_this.startUserMedia(visitorId,isVideo,call);
}).catch(() => {
isVideo=false;
_this.startUserMedia(visitorId,isVideo,call);
});
});
},
//划词搜索
selectText(){
return false;
var _this=this;
$('body').click(function(){
try{
var selecter = window.getSelection().toString();
if (selecter != null && selecter.trim() != ""){
_this.replySearch=selecter.trim();
_this.searchReply();
}else{
_this.replySearch="";
}
} catch (err){
var selecter = document.selection.createRange();
var s = selecter.text;
if (s != null && s.trim() != ""){
_this.replySearch=s.trim();
_this.searchReply();
}else{
_this.replySearch="";
}
}
var status=$('.faceBox').css("display");
if(status=="block"){
$('.faceBox').hide();
}
});
},
//翻译
translate(){
var _this=this;
var content=replaceHtml(this.kefuEditor.txt.html());
if(content==""){
return;
}
let allLang=this.allLang;
let langName="中文";
for(index in allLang){
if(allLang[index].code==_this.dstLang){
langName=allLang[index].name
}
}
this.sendAjax("/kefu/translate","get",{words:content,from:"auto",to:_this.dstLang,lang:langName},function(data){
_this.dstLang="";
var dst=data.dst;
if(dst==""){
_this.$message({
message: _this.flyLang.nodata,
type: 'error'
});
return;
}
_this.setMessageContent(dst);
});
},
sendAjax(url,method,params,callback,headers){
let _this=this;
$.ajax({
type: method,
url: url,
data:params,
headers: {
"token": localStorage.getItem("token"),
"lang":LANG,
},
error:function(res){
var data=JSON.parse(res.responseText);
if(data.code==200|| data.code==20000){
}else{
_this.$message({
message: data.msg,
type: 'error'
});
}
},
success: function(data) {
if(data.code==200 || data.code==20000){
if(data.result!=null){
callback(data.result);
}else{
callback(data);
}
}else{
_this.$message({
message: data.msg,
type: 'error'
});
}
},
});
},
resizeChatBox(){
var height=$(".kefuFuncBox").height()+47;
$(".chatBox").css("height","calc(100% - "+height+"px)");
},
deleteMessage(msgId){
var _this=this;
this.sendAjax("/kefu/message_delete","post",{msg_id:msgId,visitor_id:this.visitor.visitor_id},function(result){
for(var i=0;i<_this.msgList.length;i++){
if(_this.msgList[i].msg_id==msgId){
_this.msgList.splice(i,1);
}
}
});
},
deleteVisitorMessage(visitorId){
var _this=this;
this.$confirm('此操作将清除访客所有记录, 是否继续?', _this.flyLang.tips, {
confirmButtonText: _this.flyLang.confirm,
cancelButtonText: _this.flyLang.cancel,
type: 'warning'
}).then(function(){
_this.sendAjax("/kefu/delVisitorMessage","get",{visitor_id:visitorId},function(result){
_this.$message({
type: 'success',
message: '删除成功!'
});
_this.msgList=[]
});
});
},
loadMoreMessages:function(visitor_id){
var _this=this;
var pagesize=10;
if(_this.loadMoreDisable){
return;
}
var moreMessage=KEFU_LANG[LANG]['moremessage'];
this.flyLang.moremessage=this.flyLang.loading;
this.loadMoreDisable=true;
if(!visitor_id){
visitor_id=this.visitor.visitor_id;
}
if(this.currentPage==1){
this.msgList=[];
}
this.sendAjax("/kefu/messages_page","get",{pagesize:pagesize,page:this.currentPage,visitor_id:visitor_id},function(result){
var len=result.list.length;
if(result.list.length!=0){
if(len<pagesize){
_this.showLoadMore=false;
}else{
_this.showLoadMore=true;
}
let msgList=result.list;
for(var i=0;i<msgList.length;i++) {
let visitorMes = msgList[i];
let content = {}
if (visitorMes["mes_type"] == "kefu") {
content.is_kefu = true;
content.content =_this.markdownHtml(replaceSpecialTag(visitorMes["content"]));
} else {
content.is_kefu = false;
content.content =replaceSpecialTag(visitorMes["content"]);
}
if (visitorMes["read_status"] == "read") {
content.read_status = KEFU_LANG[LANG].read;
} else {
content.read_status = KEFU_LANG[LANG].unread;
}
content.avator = visitorMes["avator"];
content.name = visitorMes["name"];
content.msg_id = visitorMes["msg_id"];
content.time = visitorMes["time"];
_this.msgList.unshift(content);
}
if(_this.currentPage==1){
_this.scrollBottom();
}
}else{
_this.showLoadMore=false;
}
_this.currentPage++;
_this.flyLang.moremessage=moreMessage;
_this.loadMoreDisable=false;
_this.loading=false;
});
},
setVisitorListItem:function(visitorId,key,value){
for(let i=0;i<this.users.length;i++){
if(this.users[i].visitor_id==visitorId){
this.$set(this.users[i],key,value);
break;
}
}
for(let i=0;i<this.visitors.length;i++){
if(this.visitors[i].visitor_id==visitorId){
this.$set(this.visitors[i],key,value);
break;
}
}
},
getVisitorAttr:function(visitorId){
var _this=this;
this.sendAjax("/kefu/visitor_attr","get",{visitor_id:visitorId},function(result){
_this.visitorAttrs=result;
});
},
//保存访客属性
saveVisitorAttr:function(obj){
var info={
'visitor_id':this.visitor.visitor_id,
'visitor_attr':obj
}
var _this=this;
$.ajax({
type: 'post',
url: '/kefu/visitor_attrs',
data:JSON.stringify(info),
dataType:"json",
contentType: "application/json",
headers: {
"token": localStorage.getItem("token")
},
success: function(data) {
if(data.code!=200){
_this.$message({
message: _this.flyLang.failed,
type: 'error'
});
return;
}
_this.$message({
message: _this.flyLang.success,
type: 'success'
});
if(obj.real_name){
_this.setVisitorListItem(_this.visitor.visitor_id,"username",obj.real_name);
}
},
});
},
//格式化时间
formatTime:function(fmt,time) {
var timeStamp = Math.round(new Date(time).getTime()/1000);
var nowTime=Math.round(new Date().getTime()/1000);
if((nowTime-timeStamp)<=3600*24*30*6){
return beautifyTime(timeStamp,LANG);
}
return dateFormat(fmt,new Date(time));
},
//标签相关
getTags(visitor_id){
var _this=this;
sendAjax("/kefu/visitorTag","GET",{
"visitor_id":visitor_id,
},function(data){
_this.dynamicTags=data.result;
});
},
//获取所有标签
getAllTags(){
var _this=this;
sendAjax("/kefu/tags","GET",{
},function(data){
_this.allTags=data.result;
})
},
delTag(tagName) {
var _this=this;
sendAjax("/kefu/delVisitorTag","GET",{
"visitor_id":_this.visitor.visitor_id,
"tag_name":tagName
},function(data){
_this.getTags(_this.visitor.visitor_id);
})
},
showInput() {
this.inputVisible = true;
this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus();
});
},
//添加标签
addTag() {
let inputValue = this.inputValue;
var _this=this;
sendAjax("/kefu/visitorTag","POST",{
"visitor_id":this.visitor.visitor_id,
"tag_name":this.inputValue
},function(data){
_this.inputVisible = false;
_this.inputValue = '';
_this.getTags(_this.visitor.visitor_id);
})
},
//发送评价
sendComment(){
var _this=this;
this.$confirm(_this.flyLang.sendCommentDesc, _this.flyLang.tips, {
confirmButtonText: _this.flyLang.confirm,
cancelButtonText: _this.flyLang.cancel,
type: 'warning'
}).then(function(){
_this.sendAjax("/kefu/sendComment","GET",{
"visitor_id":_this.visitor.visitor_id,
},function(data){
_this.$message({
message: data.msg,
type: 'success'
});
})
});
},
copyText(text){
copyText(replaceHtml(text));
this.$message({
message: "ok",
type: 'success'
});
},
initEditor(id){
if(!id){
id='#welcomeEditor';
}
const E = window.wangEditor
this.editor = new E(id)
this.editor.config.height = 240;
// 配置 server 接口地址
this.editor.config.uploadImgServer = '/kefu/editorUploadImg?token='+localStorage.getItem("token");
this.editor.config.height = 240;
this.editor.config.uploadImgMaxSize = 1 * 1024 * 1024; // 1M
this.editor.config.uploadFileName = 'imgfile';
this.editor.config.uploadImgHooks = {
// 图片上传并返回了结果,图片插入已成功
success: function(xhr) {
console.log('success', xhr)
},
// 图片上传并返回了结果,但图片插入时出错了
fail: function(xhr, editor, resData) {
console.log('fail', resData)
},
// 上传图片出错,一般为 http 请求的错误
error: function(xhr, editor, resData) {
console.log('error', xhr, resData)
},
// 上传图片超时
timeout: function(xhr) {
console.log('timeout')
},
// 图片上传并返回了结果,想要自己把图片插入到编辑器中
// 例如服务器端返回的不是 { errno: 0, data: [...] } 这种格式,可使用 customInsert
customInsert: function(insertImgFn, result) {
// result 即服务端返回的接口
console.log('customInsert', result)
insertImgFn(result.data.url)
}
}
let isHTML = false;
const { BtnMenu } = E
class htmlMenu extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" style="font-size: 12px">HTML</div>`
)
super($elem, editor)
}
clickHandler() {
let source = this.editor.txt.html();
if(source){
isHTML = !isHTML;
}
if(isHTML){
source = source.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/ /g, "&nbsp;");
}else{
source = this.editor.txt.text().replace(/&lt;/ig, "<").replace(/&gt;/ig, ">").replace(/&nbsp;/ig, " ")
}
this.editor.txt.html(source);
this.tryChangeActive();
// console.log(source);
}
tryChangeActive() {
if(isHTML){
this.active()
}else{
this.unActive()
}
}
}
// 注册菜单
const menuKey = 'htmlMenuKey' // 菜单 key ,各个菜单不能重复
this.editor.menus.extend('htmlMenuKey', htmlMenu)
// 将菜单加入到 editor.config.menus 中
// 也可以通过配置 menus 调整菜单的顺序,参考【配置菜单】部分的文档
this.editor.config.menus = this.editor.config.menus.concat(menuKey)
this.editor.create();
if(this.replyContent!=""){
this.editor.txt.html(this.replyContent);
}
},
destoryEditor(){
this.replyTitle="";
this.replyContent="";
if(!this.editor){
return;
}
this.editor.destroy();
this.editor = null;
},
//录音相关
recoderFormat:function(percentage){
return percentage+"s";
},
//开始录音
startRecoder:function(e){
if(this.recorder){
this.recorder.destroy();
this.recorder=null;
}
var _this=this;
Recorder.getPermission().then(function() {
_this.recorder = new Recorder();
_this.recorderAudio = document.querySelector('#audio');
_this.recorder.start();
_this.recorder.onprogress = function (params) {
_this.recoderSecond = parseInt(params.duration);
}
this.talkBtnText = "松开 结束";
}, function(error){
_this.$message({
message: error,
type: 'error'
});
return;
});
e.preventDefault();
},
stopRecoder:function(e){
if(!this.recorder){
return;
}
var blob=this.recorder.getWAVBlob();
this.recorderAudio.src = URL.createObjectURL(blob);
this.recorderAudio.controls = true;
this.talkBtnText="按住 说话";
this.recorderEnd=true;
e.preventDefault();
},
sendRecoder:function(){
if(!this.recorder){
return;
}
var blob=this.recorder.getWAVBlob();
var formdata = new FormData(); // form 表单 {key:value}
formdata.append("realfile", blob); // form input type="file"
var _this=this;
_this.loading=true;
$.ajax({
url: "/2/uploadAudio",
type: 'post',
processData: false,
contentType: false,
data: formdata,
dataType: 'JSON',
mimeType: "multipart/form-data",
success: function (res) {
_this.loading=false;
if(res.code!=200){
_this.$message({
message: res.msg,
type: 'error'
});
}else{
_this.cancelRecoder();
_this.messageContent='audio[' + res.result.path + ']';
_this.chatToUser(true);
}
}
})
},
cancelRecoder:function(){
this.audioDialog=false;
if(!this.recorder){
return;
}
this.recorder.destroy();
this.recorder=null;
this.recoderSecond=0;
},
//重连对话框
reloadDialog(msg){
this.$message({
message: '服务器连接中断:'+msg,
type: 'warning'
});
},
initKefuEditor(){
var _this=this;
const E = window.wangEditor
_this.kefuEditor = new E('#kefuEditor');
_this.kefuEditor.config.height = 120;
_this.kefuEditor.config.placeholder = this.flyLang.textarea;
_this.kefuEditor.focus=true;
//this.kefuEditor.config.showFullScreen = false;
_this.kefuEditor.config.pasteIgnoreImg = true;
_this.kefuEditor.config.menus = [
'head',
'bold',
'fontSize',
'fontName',
// "image",
'italic',
'underline',
'strikeThrough',
'indent',
'lineHeight',
'foreColor',
'backColor',
'link',
'list',
'todo',
'justify',
'quote',
'emoticon',
'table',
];
// 配置 server 接口地址
// this.kefuEditor.config.uploadImgServer = '/kefu/editorUploadImg?token='+localStorage.getItem("token");
// this.kefuEditor.config.uploadImgMaxSize = 1 * 1024 * 1024; // 1M
// this.kefuEditor.config.uploadFileName = 'imgfile';
// this.kefuEditor.config.uploadImgHooks = {
// // 图片上传并返回了结果,图片插入已成功
// success: function(xhr) {
// console.log('success', xhr)
// },
// // 图片上传并返回了结果,但图片插入时出错了
// fail: function(xhr, editor, resData) {
// console.log('fail', resData)
// },
// // 上传图片出错,一般为 http 请求的错误
// error: function(xhr, editor, resData) {
// console.log('error', xhr, resData)
// },
// // 上传图片超时
// timeout: function(xhr) {
// console.log('timeout')
// },
// // 图片上传并返回了结果,想要自己把图片插入到编辑器中
// // 例如服务器端返回的不是 { errno: 0, data: [...] } 这种格式,可使用 customInsert
// customInsert: function(insertImgFn, result) {
// // result 即服务端返回的接口
// console.log('customInsert', result)
// insertImgFn(result.data.url)
// }
// }
_this.kefuEditor.config.zIndex=2;
_this.kefuEditor.config.fontSizes = {
'x-small': { name: '10px', value: '1' },
'small': { name: '13px', value: '2' },
'normal': { name: '13px', value: '3' },
'large': { name: '18px', value: '4' },
'x-large': { name: '24px', value: '5' },
'xx-large': { name: '32px', value: '6' },
'xxx-large': { name: '48px', value: '7' },
}
// this.editor.txt.eventHooks.enterUpEvents.push(function () {
// _this.chatToUser();
// })
// 配置 onchange 回调函数
_this.kefuEditor.config.onchange = function (newHtml) {
//_this.inputNextText(replaceHtml(newHtml));
};
// 配置触发 onchange 的时间频率,默认为 200ms
_this.kefuEditor.config.onchangeTimeout = 1000;
let isHTML = false;
const { BtnMenu } = E
class htmlMenu extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" style="font-size: 12px">HTML</div>`
)
super($elem, editor)
}
clickHandler() {
let source = _this.kefuEditor.txt.html();
if(source){
isHTML = !isHTML;
}
if(isHTML){
source = source.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/ /g, "&nbsp;");
}else{
source = _this.kefuEditor.txt.text().replace(/&lt;/ig, "<").replace(/&gt;/ig, ">").replace(/&nbsp;/ig, " ")
}
_this.kefuEditor.txt.html(source);
_this.tryChangeActive();
// console.log(source);
}
tryChangeActive() {
if(isHTML){
this.active()
}else{
this.unActive()
}
}
}
// 注册菜单
const menuKey = 'htmlMenuKey' // 菜单 key ,各个菜单不能重复
_this.kefuEditor.menus.extend('htmlMenuKey', htmlMenu)
// 将菜单加入到 editor.config.menus 中
// 也可以通过配置 menus 调整菜单的顺序,参考【配置菜单】部分的文档
_this.kefuEditor.config.menus = _this.kefuEditor.config.menus.concat(menuKey)
_this.kefuEditor.create();
},
//设置内容
setMessageContent(content){
this.kefuEditor.txt.html(content);
this.messageContent=content;
},
//切换机器人
switchRobotStatus(status){
let _this=this;
let value=status ? "1" : "2";
this.sendAjax("/kefu/visitorState","POST",{state:this.visitor.state,position:1,value:value,visitor_id:this.visitor.visitor_id},function(result) {
_this.$message({
message: "success",
type: 'success'
});
});
},
//一键已读
clearUnreadNum(){
let _this=this;
this.sendAjax("/kefu/clearUnreadNum","GET",{},function(result) {
_this.$message({
message: "success",
type: 'success'
});
});
},
//正在输入搜索
inputNextText:function(message){
var _this=this;
_this.searchList=[];
if(!message){
return;
}
this.sendAjax("/2/searchQuestion","get",{ent_id:_this.kfConfig.ent_id,content:message},function(result){
if(!result||result.length==0){
return;
}
for(key in result){
let title=result[key].title.split(",")[0];
result[key].title=title;
let str= title.replace(message,"<span>"+message+"</span>");
result[key].htmlTitle=str;
result[key].content=result[key].content;
}
_this.searchList=result;
});
},
//解析markdown
markdownHtml(str){
var result = this.markdownIt.render(str);
return result;
},
//AI知识库
getKefuAiKnowledge(){
let _this=this;
var xhr = new XMLHttpRequest();
xhr.open("GET", "/kefu/aiKnowledge?visitor_id="+this.visitor.visitor_id);
xhr.setRequestHeader("Content-Type", "text/html");
xhr.setRequestHeader("token", localStorage.getItem("token"));
xhr.onprogress = function(event) {
//console.log(event.currentTarget.responseText);
_this.kefuEditor.txt.html(event.currentTarget.responseText)
};
xhr.send();
},
//AI意图总结
customerIntent(){
let _this=this;
_this.$message({
message: "AI正在分析中请稍等片刻...",
type: 'success'
});
var xhr = new XMLHttpRequest();
xhr.open("GET", "/kefu/aiIntent?visitor_id="+this.visitor.visitor_id);
xhr.setRequestHeader("Content-Type", "text/html");
xhr.setRequestHeader("token", localStorage.getItem("token"));
xhr.onprogress = function(event) {
//console.log(event.currentTarget.responseText);
_this.aiHelper.reply=replaceContent(event.currentTarget.responseText);
};
xhr.send();
},
//获取训练文档
getFileList(){
let _this=this;
this.sendAjax("/kefu/ai/fileList","get",{},function(result){
if(result && result.length!=0){
_this.fileId=result[0].id;
_this.fileList=result;
}
});
},
//训练内容
postTrain(){
let _this=this;
let data={
fileId:this.fileId,
content:this.trainContent
}
this.loading=true;
sendAjax('/kefu/ai/training',"POST",data,function(data){
_this.loading=false;
_this.trainDialog=false;
_this.$message({
message: "success",
type: 'success'
});
})
},
},
mounted() {
var _this=this;
document.addEventListener('paste', this.onPasteUpload)
//监听页面显示隐藏
document.addEventListener('visibilitychange', function(){
var visibility = document.visibilityState;
if(visibility == 'visible'){
var mes = {
"type":"ping"
};
if(_this.socket!=null){
_this.socket.send(JSON.stringify(mes));
}
_this.getOnlineVisitors();
_this.getVisitorPage(1);
}else if(visibility == 'hidden'){
}
});
//监听iframe中发来的消息
window.addEventListener('message', function(event) {
if (event.data.type === 'FROM_IFRAME1') {
let receiveData=event.data.data;
let sendData='';
let url="";
let img="";
let price="";
for(key in receiveData){
if(key=="url"){
url=receiveData[key];
continue;
}
if(key=="img"){
img=receiveData[key];
continue;
}
if(key=="price"){
price=receiveData[key];
continue;
}
sendData+=key+""+receiveData[key]+"<br/>";
}
let data=JSON.stringify({
"title":sendData,
"url":url,
"img":img,
"price":price,
})
_this.messageContent='product[' + data+ ']';
_this.chatToUser();
}
}, false);
},
created: function () {
//jquery
this.initJquery();
this.getKefuInfo();
this.getOnlineVisitors();
this.getReplys();
this.getEntReplys();
this.getConfigs();
this.selectText();
this.getAllTags();
this.getOtherKefu();
//this.initPeerjs();
//心跳
this.ping();
this.initConn();
}
})
</script>
<style>
.kefuFuncBox .w-e-toolbar p, .w-e-text-container p, .w-e-menu-panel p{
font-size: 12px !important;
}
</style>
</html>